Changeset 1197
- Timestamp:
- 2008-10-01 17:27:57 (3 months ago)
- Files:
-
- trunk/app/controllers/application.rb (modified) (1 diff)
- trunk/app/helpers/application_helper.rb (modified) (1 diff)
- trunk/app/models/node.rb (modified) (2 diffs)
- trunk/lib/comment_query.rb (modified) (3 diffs)
- trunk/lib/node_query.rb (modified) (12 diffs)
- trunk/lib/parser/lib/rules/zena.rb (modified) (20 diffs)
- trunk/lib/query_builder/lib/query_builder.rb (modified) (13 diffs)
- trunk/lib/query_builder/test/query_builder/basic.yml (modified) (2 diffs)
- trunk/lib/query_builder/test/query_builder/errors.yml (modified) (2 diffs)
- trunk/lib/query_builder/test/query_builder_test.rb (modified) (7 diffs)
- trunk/lib/tasks/zena.rake (modified) (1 diff)
- trunk/lib/yaml_test.rb (modified) (3 diffs)
- trunk/test/fixtures/files/Node-test.zafu (modified) (1 diff)
- trunk/test/functional/comments_controller_test.rb (modified) (1 diff)
- trunk/test/functional/data_entries_controller_test.rb (modified) (1 diff)
- trunk/test/functional/iformats_controller_test.rb (modified) (3 diffs)
- trunk/test/functional/links_controller_test.rb (modified) (1 diff)
- trunk/test/helpers/node_query/basic.yml (modified) (1 diff)
- trunk/test/helpers/node_query_test.rb (modified) (3 diffs)
- trunk/test/helpers/zena_parser/ajax.yml (modified) (1 diff)
- trunk/test/helpers/zena_parser/basic.yml (modified) (7 diffs)
- trunk/test/helpers/zena_parser/relations.yml (modified) (1 diff)
- trunk/test/sites/zena/comments.yml (modified) (1 diff)
- trunk/test/test_helper.rb (modified) (1 diff)
- trunk/test/test_zena.rb (added)
- trunk/test/unit/virtual_class_test.rb (modified) (1 diff)
- trunk/test/zena_test.rb (deleted)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/app/controllers/application.rb
r1185 r1197 692 692 end 693 693 694 params = (opts == {}) ? '' : ('?' + opts.map{ |k,v| "#{k}=#{ v}"}.join('&'))694 params = (opts == {}) ? '' : ('?' + opts.map{ |k,v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')) 695 695 696 696 if !asset && node[:id] == current_site[:root_id] && mode.nil? && format == 'html' trunk/app/helpers/application_helper.rb
r1185 r1197 1159 1159 end 1160 1160 1161 # TODO: could be used by all helpers: faster then routes... Rename obj_link (is used to link to versions)1162 # Used by zafu1163 def node_link(opts={})1164 options = {:node=>@node}.merge(opts)1165 node = options.delete(:node)1166 if href = options.delete(:href)1167 node = href1168 end1169 return options[:text] unless node1170 1171 unless url_only = options.delete(:url_only)1172 text = options.delete(:text) || node.version.title1173 attributes = ""1174 attributes += options[:class] ? " class='#{options.delete(:class)}'" : ''1175 attributes += options[:id] ? " id='#{options.delete(:id)}'" : ''1176 attributes += options[:name] ? " name='#{options.delete(:name)}'" : ''1177 end1178 url_only ? zen_path(node, options) : "<a#{attributes} href='#{zen_path(node, options)}'>#{text}</a>"1179 end1180 1181 1161 # shows links for site features 1182 1162 def show_link(link, opt={}) trunk/app/models/node.rb
r1193 r1197 995 995 end 996 996 997 alias v_title title 998 997 999 def text 998 1000 version.text 999 1001 end 1002 1003 alias v_text text 1000 1004 1001 1005 def title=(t) … … 1011 1015 return nil if new_record? 1012 1016 return @icon if defined? @icon 1013 sql, errors, uses_node_name = Node.build_find(:first, ['icon', 'image'], :node_name => 'self') 1017 query = Node.build_find(:first, ['icon', 'image'], :node_name => 'self') 1018 sql, uses_node_name = query.to_sql, query.uses_node_name 1014 1019 @icon = sql ? do_find(:first, eval("\"#{sql}\""), :ignore_source => !uses_node_name) : nil 1015 1020 end trunk/lib/comment_query.rb
r1173 r1197 2 2 3 3 class CommentQuery < QueryBuilder 4 attr_reader :uses_node_name 4 attr_reader :uses_node_name, :node_name 5 5 set_main_table 'comments' 6 6 set_main_class 'Comment' … … 20 20 when 'author' 21 21 add_table('users') 22 @where << "#{table('users')}.id = #{field_or_ param('author_id')}"22 @where << "#{table('users')}.id = #{field_or_attr('author_id')}" 23 23 # should we move on to Contact ? 24 24 when 'node', 'nodes' … … 75 75 end 76 76 77 def map_ parameter(fld)77 def map_attr(fld) 78 78 # error 79 79 nil 80 80 end 81 82 # Erb finder used by zafu 83 def finder(count) 84 return 'nil' unless valid? 85 case count 86 when :count 87 "#{node_name}.do_find(:count, \"#{count_sql}\", #{!uses_node_name}, #{main_class})" 88 else 89 "#{node_name}.do_find(#{count.inspect}, \"#{to_sql}\", #{!uses_node_name}, #{main_class})" 90 end 91 end 81 92 end trunk/lib/node_query.rb
r1185 r1197 3 3 4 4 class NodeQuery < QueryBuilder 5 attr_reader :context, :uses_node_name 5 attr_reader :context, :uses_node_name, :node_name 6 6 set_main_table 'nodes' 7 7 set_main_class 'Node' … … 46 46 end 47 47 @distinct = true if @tables.include?('versions') 48 end 49 50 # Erb finder used by zafu 51 def finder(count) 52 return 'nil' unless valid? 53 case count 54 when :count 55 "#{node_name}.do_find(:count, \"#{count_sql}\", #{!uses_node_name}, #{main_class})" 56 else 57 "#{node_name}.do_find(#{count.inspect}, \"#{to_sql}\", #{!uses_node_name}, #{main_class})" 58 end 48 59 end 49 60 … … 109 120 end 110 121 111 @where << "#{field_or_ param(fields[0])} = #{field_or_param(fields[1], table(main_table,-1))}"122 @where << "#{field_or_attr(fields[0])} = #{field_or_attr(fields[1], table(main_table,-1))}" 112 123 true 113 124 end … … 119 130 # no need to load discussions, versions and all the mess 120 131 add_table('comments') 121 @where << "#{table('comments')}.discussion_id = #{map_ parameter('discussion_id')}"132 @where << "#{table('comments')}.discussion_id = #{map_attr('discussion_id')}" 122 133 return CommentQuery # class change 123 134 else … … 152 163 # remove caller join 153 164 @distinct = true 154 @where << "#{field_or_ param('id')} = #{table('links')}.#{rel.other_side} AND #{table('links')}.relation_id = #{rel[:id]}"155 else 156 @where << "#{field_or_ param('id')} = #{table('links')}.#{rel.other_side} AND #{table('links')}.relation_id = #{rel[:id]} AND #{table('links')}.#{rel.link_side} = #{field_or_param('id', table(main_table,-1))}"165 @where << "#{field_or_attr('id')} = #{table('links')}.#{rel.other_side} AND #{table('links')}.relation_id = #{rel[:id]}" 166 else 167 @where << "#{field_or_attr('id')} = #{table('links')}.#{rel.other_side} AND #{table('links')}.relation_id = #{rel[:id]} AND #{table('links')}.#{rel.link_side} = #{field_or_attr('id', table(main_table,-1))}" 157 168 end 158 169 else … … 161 172 end 162 173 163 def map_literal(value )174 def map_literal(value, env = :sql) 164 175 if value =~ /(.*?)\[(visitor|param):(\w+)\](.*)/ 165 176 val_start = $1 == '' ? '' : "#{$1.inspect} +" … … 167 178 case $2 168 179 when 'visitor' 169 value = "\#{Node.connection.quote(\#{#{val_start}Node.zafu_attribute(visitor.contact, #{$3.inspect})#{val_end}})}"180 value = env == :sql ? "\#{Node.connection.quote(\#{#{val_start}Node.zafu_attribute(visitor.contact, #{$3.inspect})#{val_end}})}" : nil 170 181 when 'param' 171 value = "\#{Node.connection.quote(#{val_start}params[:#{$3}].to_s#{val_end})}"172 end 173 else 174 value = Node.connection.quote(value)182 value = env == :sql ? "\#{Node.connection.quote(#{val_start}params[:#{$3}].to_s#{val_end})}" : "params[:#{$3}]" 183 end 184 else 185 value = env == :sql ? Node.connection.quote(value) : nil 175 186 end 176 187 end … … 238 249 end 239 250 240 def map_ parameter(fld)251 def map_attr(fld, env = :sql) 241 252 case fld 242 253 when 'project_id', 'section_id', 'discussion_id' … … 251 262 @errors << "invalid parameter '#{fld}'" 252 263 "0" 264 end 265 end 266 267 def parse_paginate_clause(paginate) 268 return @offset unless paginate 269 if !@limit 270 # TODO: raise error ? 271 @errors << "invalid paginate clause '#{paginate}' (used without limit)" 272 nil 273 elsif (fld = map_literal("[#{paginate}]", :ruby)) && (page_size = @limit[/ LIMIT (\d+)/,1]) 274 @page_size = [2,page_size.to_i].max 275 " OFFSET \#{((#{fld}.to_i > 0 ? #{fld}.to_i : 1)-1)*#{page_size.to_i}}" 276 else 277 @errors << "invalid paginate clause '#{paginate}'" 278 nil 253 279 end 254 280 end … … 352 378 #def build_find(count, pseudo_sql, node_name, raw_filters = nil, ignore_warnings = false, ref_date = nil) 353 379 def build_find(count, pseudo_sql, opts = {}) 354 if count != :all380 if count == :first 355 381 opts[:limit] = 1 356 382 end 357 query = NodeQuery.new(pseudo_sql, opts.merge(:custom_query_group => visitor.site.host)) 358 [query.to_sql, query.errors, query.uses_node_name, query.main_class] 383 NodeQuery.new(pseudo_sql, opts.merge(:custom_query_group => visitor.site.host)) 359 384 end 360 385 end … … 367 392 return nil if query.empty? 368 393 return nil if (new_record? && !ignore_source) # do not run query (might contain nil id) 369 res = klass.find_by_sql(query) 370 if count == :all 394 395 case count 396 when :all 397 res = klass.find_by_sql(query) 371 398 if res == [] 372 399 nil 373 400 else 374 res.each {|r| visitor.visit(r)}401 res.each {|r| visitor.visit(r)} 375 402 res 376 403 end 377 elsif res = res.first 378 visitor.visit(res) 404 when :first 405 res = klass.find_by_sql(query).first 406 visitor.visit(res) if res 379 407 res 408 when :count 409 klass.count_by_sql(query) 380 410 else 381 411 nil … … 391 421 self.send(rel.first) 392 422 else 393 sql , errors = Node.build_find(count, rel, :node_name => 'self')423 sql = Node.build_find(count, rel, :node_name => 'self').to_sql 394 424 if sql 395 425 do_find(count, eval("\"#{sql}\"")) trunk/lib/parser/lib/rules/zena.rb
r1190 r1197 148 148 elsif @method =~ /\A(\w+)\s+(\w+)\s+(.+)$/ 149 149 # 'pages where name ...' 150 @params[: method] = @method150 @params[:select] = @method 151 151 @method = 'context' 152 152 end … … 155 155 # ok 156 156 else 157 @params[: method] = @method157 @params[:select] = @method 158 158 @method = 'context' 159 159 end … … 1730 1730 # <r:link href='node' tattr='lang'/> 1731 1731 # <r:link update='dom_id'/> 1732 # <r:link page='next'/> <r:link page='previous'/> <r:link page='list'/> 1732 1733 def r_link 1733 if @blocks.size > 1 || (@blocks.size == 1 && !@blocks.first.kind_of?(String)) 1734 text_mode = :raw 1735 text = expand_with 1736 else 1737 text = get_text_for_erb(params, false) 1738 text_mode = :erb 1739 end 1740 1741 opts = '' 1742 if @params[:href] 1743 unless lnode = find_stored(Node, @params[:href]) 1744 finder, klass = build_finder_for(:first, @params[:href]) 1745 return unless finder 1734 if @params[:page] 1735 pagination_links 1736 elsif upd = @params[:update] 1737 return unless target = find_target(upd) 1738 link_to_update(target) 1739 else 1740 link_to_node 1741 end 1742 end 1743 1744 def link_to_node(opts = {}) 1745 url_params = opts[:url_params] || {} 1746 default_text = opts[:default_text] 1747 params = @params.dup 1748 1749 opts = {} 1750 1751 if href = params.delete(:href) 1752 if lnode = find_stored(Node, href) 1753 # using stored node 1754 else 1755 lnode, klass = build_finder_for(:first, href, {}) 1756 return unless lnode 1746 1757 return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node) 1747 opts << ", :href=>#{finder}" 1758 end 1759 else 1760 # obj 1761 if node_class == Version 1762 lnode = "#{node}.node" 1763 opts[:lang] = "#{node}.lang" 1764 else 1765 lnode = node 1766 end 1767 end 1768 1769 if fmt = params.delete(:format) 1770 if fmt == 'data' 1771 opts[:format] = "#{node}.c_ext" 1772 else 1773 opts[:format] = fmt.inspect 1748 1774 end 1749 1775 end 1750 1776 1751 # obj 1752 if node_class == Version 1753 lnode ||= "#{node}.node" 1754 opts << ", :lang=>#{node}.lang" 1755 else 1756 lnode ||= node 1757 end 1758 1759 if fmt = @params[:format] 1760 if fmt == 'data' 1761 opts << ", :format => #{node}.c_ext" 1762 else 1763 opts << ", :format => #{fmt.inspect}" 1764 end 1765 end 1766 1767 if mode = @params[:mode] 1768 opts << ", :mode => #{mode.inspect}" 1769 end 1770 1771 if sharp = @params[:sharp] 1772 opts << ", :sharp=>#{sharp.inspect}" 1773 end 1774 1775 if sharp_in = @params[:in] 1777 if mode = params.delete(:mode) 1778 opts[:mode] = mode.inspect 1779 end 1780 1781 if sharp = params.delete(:sharp) 1782 opts[:sharp] = sharp.inspect 1783 end 1784 1785 if sharp_in = params.delete(:in) 1776 1786 finder, klass = build_finder_for(:first, sharp_in, {}) 1777 1787 return unless finder 1778 1788 return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node) 1779 opts << ", :sharp_in=>#{finder}" 1780 end 1781 1782 if date = @params[:date] 1783 if date == 'current_date' 1784 opts << ", :date=>#{current_date}" 1785 elsif date =~ /\A\d/ 1786 opts << ", :date=>#{date.inspect}" 1787 else 1788 opts << ", :date=>#{node_attribute(date)}" 1789 end 1790 end 1791 1792 html_params = {} 1789 opts[:sharp_in] = finder 1790 end 1791 1793 1792 if @html_tag && @html_tag != 'a' 1794 # html attributes do not belong to sharp 1793 # FIXME: can we remove this ? 1794 # html attributes do not belong to anchor 1795 1795 pre_space = '' 1796 else 1797 [:class, :id, :style, :name].each do |sym| 1798 if value = @html_tag_params[sym] || @params[sym] 1799 html_params[sym] = value 1800 end 1801 end 1796 html_params = {} 1797 else 1798 html_params = get_html_params(params.merge(@html_tag_params)) 1802 1799 pre_space = @space_before || '' 1803 1800 @html_tag_done = true 1804 1801 end 1805 1802 1806 if @params[:anchor] 1807 @anchor_param = nil 1808 html_params[:name] = anchor_name(@params[:anchor], node) 1809 end 1810 1811 if upd = @params[:update] 1812 return unless target = find_target(upd) 1813 link_to_update(target, :html_params => html_params) 1814 else 1815 if text_mode == :raw 1816 pre_space + "<a#{params_to_html(html_params)} href='<%= node_link(:url_only=>true, :node=>#{lnode}#{opts}) %>'>#{text}</a>" 1817 else 1818 text = text.blank? ? '' : ", :text=>#{text}" 1819 pre_space + "<%= node_link(:node=>#{lnode}#{text}#{opts}#{params_to_erb(html_params)}) %>" 1820 end 1821 end 1822 1803 (params.keys - [:style, :class, :id, :rel, :name, :anchor, :attr, :tattr, :trans, :text, :page]).each do |k| 1804 url_params[k] = params[k] 1805 end 1806 1807 url_params.each do |k,v| 1808 if k == :date 1809 if v == 'current_date' 1810 url_params[k] = current_date 1811 elsif v =~ /\A\d/ 1812 url_params[k] = v.inspect 1813 elsif v =~ /\[/ 1814 attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false) 1815 url_params[k] = "\"#{attribute}\"" 1816 else 1817 url_params[k] = node_attribute(v) 1818 end 1819 else 1820 attribute, static = parse_attributes_in_value(v.gsub('"',''), :erb => false) 1821 url_params[k] = "\"#{attribute}\"" 1822 end 1823 end 1824 1825 opts_str = '' 1826 opts.merge!(url_params) 1827 opts.keys.sort {|a,b| a.to_s <=> b.to_s }.each do |k| 1828 opts_str << ",:#{k.to_s.gsub(/[^a-z_A-Z_]/,'')}=>#{opts[k]}" 1829 end 1830 1831 pre_space + "<a#{params_to_html(html_params)} href='<%= zen_path(#{lnode}#{opts_str}) %>'>#{text_for_link(default_text)}</a>" 1832 end 1833 1834 # <r:link page='next'/> <r:link page='previous'/> <r:link page='list'/> 1835 def pagination_links 1836 return parser_error("not in pagination scope") unless pagination_key = @context[:paginate] 1837 case @params[:page] 1838 when 'previous' 1839 out "<% if set_#{pagination_key}_previous = (set_#{pagination_key} > 1 ? set_#{pagination_key} - 1 : nil) -%>" 1840 @context[:vars] ||= [] 1841 @context[:vars] << "#{pagination_key}_previous" 1842 out link_to_node(:default_text => "<%= set_#{pagination_key}_previous %>", :url_params => {pagination_key => "[#{pagination_key}_previous]"}) 1843 out "<% end -%>" 1844 when 'next' 1845 out "<% if set_#{pagination_key}_next = (set_#{pagination_key}_count - set_#{pagination_key} > 0 ? set_#{pagination_key} + 1 : nil) -%>" 1846 @context[:vars] ||= [] 1847 @context[:vars] << "#{pagination_key}_next" 1848 out link_to_node(:default_text => "<%= set_#{pagination_key}_next %>", :url_params => {pagination_key => "[#{pagination_key}_next]"}) 1849 out "<% end -%>" 1850 when 'list' 1851 "pagination list helper..." 1852 else 1853 parser_error("unkown 'page' option #{@params[:page].inspect} should be ('previous', 'next' or 'list')") 1854 end 1823 1855 end 1824 1856 … … 1843 1875 return unless finder 1844 1876 return parser_error("invalid class (#{klass})") unless klass.ancestors.include?(Node) 1845 res = "node_link(:node=>#{finder}, :text=>#{res})" 1846 end 1847 "<%= #{res} %>" 1877 "<a href='<%= zen_path(#{finder}) %>'><%= #{res} %></a>" 1878 else 1879 "<%= #{res} %>" 1880 end 1848 1881 end 1849 1882 … … 1852 1885 if @context[:block] == self 1853 1886 # called from self (storing template / rendering) 1854 size = (params[:size] || 'large').to_sym1855 finder = params[: find] || 'notes in project'1856 ref_date = params[:date] || 'event_at'1887 size = (params[:size] || 'large').to_sym 1888 finder = params[:select] || 'notes in project' 1889 ref_date = params[:date] || 'event_at' 1857 1890 type = params[:type] ? params[:type].to_sym : :month 1858 1891 … … 1945 1978 # use all other tags as relations 1946 1979 def r_unknown 1947 @params[: method] = @method1980 @params[:select] = @method 1948 1981 r_context 1949 1982 end … … 1955 1988 def r_context 1956 1989 # DRY ! (build_finder_for, block) 1957 return parser_error("missing 'method' parameter") unless method = @params[: method]1990 return parser_error("missing 'method' parameter") unless method = @params[:select] 1958 1991 1959 1992 context = node_class.zafu_known_contexts[method] 1960 if context && @params.keys == [: method]1993 if context && @params.keys == [:select] 1961 1994 klass = context[:node_class] 1962 1995 # hack to store last 'Node' context until we fix node(Node) stuff: … … 1964 1997 if klass.kind_of?(Array) 1965 1998 # plural 1966 do_list( "#{node}.#{method}", context.merge(:node_class => klass[0], :previous_node => previous_node) )1999 do_list( "#{node}.#{method}", nil, context.merge(:node_class => klass[0], :previous_node => previous_node) ) 1967 2000 else 1968 2001 # singular … … 1970 2003 end 1971 2004 elsif node_kind_of?(Node) 1972 count = ['first','all' ].include?(@params[:find]) ? @params[:find].to_sym : nil2005 count = ['first','all','count'].include?(@params[:find]) ? @params[:find].to_sym : nil 1973 2006 count ||= Node.plural_relation?(method) ? :all : :first 1974 finder, klass = build_finder_for(count, method, @params)2007 finder, klass, query = build_finder_for(count, method, @params) 1975 2008 return unless finder 1976 2009 if node_kind_of?(Node) && !klass.ancestors.include?(Node) … … 1980 2013 if count == :all 1981 2014 # plural 1982 do_list( finder, :node_class => klass)2015 do_list( finder, query, :node_class => klass) 1983 2016 # elsif count == :count 1984 2017 # "<%= #{build_finder_for(count, method, @params)} %>" … … 2065 2098 2066 2099 # make sure we do not use a new record in a find query: 2067 sql_query, query_errors, uses_node_name, klass= Node.build_find(count, pseudo_sql, :node_name => node_name, :raw_filters => raw_filters, :ref_date => "\#{#{current_date}}")2068 2069 unless sql_query2070 out parser_error(query _errors.join(' '), pseudo_sql.join(', '))2100 query = Node.build_find(count, pseudo_sql, :node_name => node_name, :raw_filters => raw_filters, :ref_date => "\#{#{current_date}}") 2101 2102 unless query.valid? 2103 out parser_error(query.errors.join(' '), pseudo_sql.join(', ')) 2071 2104 return nil 2072 2105 end 2073 node_class_param = klass == Node ? '' : ", #{klass}" 2074 res = "#{node_name}.do_find(#{count.inspect}, \"#{sql_query}\", #{!uses_node_name}#{node_class_param})" 2106 2107 2108 if count == :count 2109 out "<%= #{query.finder(:count)} %>" 2110 return nil 2111 end 2112 2113 klass = query.main_class 2114 2075 2115 if params[:else] 2076 else_query, else_klass = build_finder_for(count, params[:else], {}) 2077 if else_query && (else_klass == klass || klass.ancestors.include?(else_klass) || else_klass.ancestors.include?(klass)) 2078 ["(#{res} || #{else_query})", klass] 2079 else 2080 [res, klass] 2081 end 2082 else 2083 [res, klass] 2116 # FIXME: else not working with zafu_known_contexts 2117 finder, else_class, else_query = build_finder_for(count, params[:else], {}) 2118 if finder && (else_query.nil? || else_query.valid?) && (else_class == klass || klass.ancestors.include?(else_class) || else_class.ancestors.include?(klass)) 2119 ["(#{query.finder(count)} || #{finder})", klass, query] 2120 else 2121 [query.finder(count), query.main_class, query] 2122 end 2123 else 2124 [query.finder(count), query.main_class, query] 2084 2125 end 2085 2126 end … … 2117 2158 end 2118 2159 2119 [:limit, :offset].each do |k| 2120 next unless params[k] 2121 parts[-1] << " #{k} #{params[k]}" unless parts[0] =~ / #{k} / 2160 if paginate = params[:paginate] 2161 page_size = params[:limit].to_i 2162 page_size = 20 if page_size < 1 2163 parts[-1] << " limit #{page_size} paginate param:#{paginate.gsub(/[^a-z_A-Z]/,'')}" 2164 else 2165 [:limit, :offset].each do |k| 2166 next unless params[k] 2167 parts[-1] << " #{k} #{params[k]}" unless parts[0] =~ / #{k} / 2168 end 2122 2169 end 2123 2170 … … 2316 2363 end 2317 2364 2318 def do_list(list_finder, opts={})2365 def do_list(list_finder, query, opts={}) 2319 2366 clear_dom_scope 2320 2367 … … 2329 2376 @context[:need_link_id] = form_block.need_link_id 2330 2377 2331 out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>" 2378 out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>" 2379 if query && (pagination_key = query.pagination_key) 2380 out "<% set_#{pagination_key}_nodes = #{query.finder(:count)}; set_#{pagination_key}_count = (set_#{pagination_key}_nodes / #{query.page_size.to_f}).ceil; set_#{pagination_key} = [1,params[:#{pagination_key}].to_i].max -%>" 2381 @context[:paginate] = pagination_key 2382 @context[:vars] ||= [] 2383 @context[:vars] << "#{pagination_key}_nodes" 2384 @context[:vars] << "#{pagination_key}_count" 2385 @context[:vars] << "#{pagination_key}" 2386 end 2387 2332 2388 # should we publish ? 2333 2389 publish_after_save ||= form_block ? form_block.params[:publish] : nil … … 2365 2421 out "<% if nil -%>" 2366 2422 end 2423 2424 if query && query.pagination_key && (pagination_key = query.pagination_key[/param:([a-zA-Z_]+)/,1]) 2425 out "<% set_#{pagination_key}_nodes = #{query.finder(:count)}; set_#{pagination_key}_count = (set_#{pagination_key}_nodes / #{query.page_size.to_f}).ceil; set_#{pagination_key} = [1,params[:#{pagination_key}].to_i].max -%>" 2426 @context[:paginate] = pagination_key 2427 @context[:vars] ||= [] 2428 @context[:vars] << "#{pagination_key}_nodes" 2429 @context[:vars] << "#{pagination_key}_count" 2430 @context[:vars] << "#{pagination_key}" 2431 end 2432 2367 2433 res = expand_with(:list=>list_var, :in_if => true) 2368 2434 out render_html_tag(res) … … 2483 2549 2484 2550 # Return a different name on each call 2485 def unique_name 2486 base = context_name 2551 def unique_name(base = context_name) 2487 2552 root.next_name_index(base, base == @name).gsub(/[^\d\w\/]/,'_') 2488 2553 end … … 2884 2949 res += "<% if #{opts[:cond]} -%>" if opts[:cond] 2885 2950 res += "<%= tag_to_remote({:url => \"#{url}?#{url_params.join('&')}\", :method => #{method.inspect}}#{params_to_erb(html_params)}) %>" 2886 2887 if @blocks != [] 2888 inner = expand_with 2889 else 2890 inner = opts[:default_text] || get_text_for_erb 2891 end 2892 2893 unless inner 2894 if node_kind_of?(Node) 2895 inner = "<%= #{node}.v_title %>" 2896 else 2897 inner = _('edit') 2898 end 2899 end 2900 res += inner 2951 res += text_for_link(opts[:default_text]) 2901 2952 res += "</a>" 2902 2953 if opts[:cond] 2903 2954 if opts[:else] != :void 2904 2955 res += "<% else -%>" 2905 res += inner2956 res += text_for_link(opts[:default_text]) 2906 2957 end 2907 2958 res += "<% end -%>" … … 2910 2961 end 2911 2962 2912 def get_text_for_erb(params = @params, use_blocks = true) 2963 def text_for_link(default = nil) 2964 if @blocks.size > 1 || (@blocks.size == 1 && !@blocks.first.kind_of?(String)) 2965 expand_with 2966 elsif default 2967 default 2968 elsif erb_text = get_text_for_erb(@params, false, :string) 2969 erb_text 2970 elsif node_kind_of?(Node) 2971 "<%= #{node}.v_title %>" 2972 else 2973 _('edit') 2974 end 2975 end 2976 2977 def get_text_for_erb(params = @params, use_blocks = true, context = :erb) 2978 string_context = context == :string 2913 2979 if params[:attr] 2914 text = "#{node_attribute(params[:attr])}"2980 string_context ? "<%= #{node_attribute(params[:attr])} %>" : node_attribute(params[:attr]) 2915 2981 elsif params[:tattr] 2916 text ="_(#{node_attribute(params[:tattr])})"2982 string_context ? "<%= _(#{node_attribute(params[:tattr])}) %>" : "_(#{node_attribute(params[:tattr])})" 2917 2983 elsif params[:trans] 2918 text =_(params[:trans]).inspect2984 string_context ? _(params[:trans]) : _(params[:trans]).inspect 2919 2985 elsif params[:text] 2920 text =params[:text].inspect2986 string_context ? params[:text] : params[:text].inspect 2921 2987 elsif use_blocks && @blocks != [] 2922 2988 res = [] … … 2941 3007 if static 2942 3008 # "just plain text" 2943 text =text.inspect3009 string_context ? text : text.inspect 2944 3010 else 2945 3011 # function(...) + "blah" + function() 2946 text = res.join(' + ') 2947 end 2948 else 2949 text = nil 2950 end 2951 text 3012 string_context ? "<%= #{res.join(' + ')} %>" : res.join(' + ') 3013 end 3014 else 3015 nil 3016 end 2952 3017 end 2953 3018 … … 2986 3051 2987 3052 def get_html_params(params) 2988 res = {} 2989 params.each do |k,v| 2990 if [:style, :class, :id, :rel].include?(k) 2991 res[k] = v 2992 end 2993 end 3053 res = {} 3054 [:style, :class, :id, :rel, :name].each do |sym| 3055 if value = params[sym] 3056 res[sym] = value 3057 end 3058 end 3059 3060 if params[:anchor] 3061 @anchor_param = nil 3062 res[:name] = anchor_name(params[:anchor], node) 3063 end 3064 2994 3065 res 2995 3066 end trunk/lib/query_builder/lib/query_builder.rb
r1169 r1197 10 10 =end 11 11 class QueryBuilder 12 attr_reader :tables, :where, :errors, :join_tables, :distinct, :final_parser 12 attr_reader :tables, :where, :errors, :join_tables, :distinct, :final_parser, :page_size 13 13 @@main_table = {} 14 14 @@main_class = {} … … 84 84 end 85 85 86 def count_sql 87 return nil if !valid? 88 return "SELECT COUNT(*) FROM #{@main_table} WHERE 0" if @tables.empty? # all alternate queries invalid and 'ignore_warnings' set. 89 90 table_list = [] 91 @tables.each do |t| 92 table_name = t.split(/\s+/).last # objects AS ob1 93 if joins = @join_tables[table_name] 94 table_list << "#{t} #{joins.join(' ')}" 95 else 96 table_list << t 97 end 98 end 99 100 if @distinct 101 @group ||= @tables.size > 1 ? " GROUP BY #{table}.id" : " GROUP BY id" 102 end 103 104 "SELECT COUNT(*) FROM #{table_list.flatten.join(',')}" + (@where == [] ? '' : " WHERE #{@where.reverse.join(' AND ')}") + @group.to_s 105 end 106 86 107 def valid? 87 108 @errors == [] 109 end 110 111 def pagination_key 112 @offset_limit_order_group[:paginate] 88 113 end 89 114 … … 189 214 rest = rest[$&.size..-1] 190 215 fld, type = $1, $2 191 unless field = field_or_ param(fld, table, :filter)216 unless field = field_or_attr(fld, table, :filter) 192 217 @errors << "invalid field or value #{fld.inspect}" 193 218 return … … 243 268 rest = rest[$&.size..-1] 244 269 fld = $& 245 unless field = field_or_ param(fld, table, :filter)270 unless field = field_or_attr(fld, table, :filter) 246 271 @errors << "invalid field or value #{fld.inspect}" 247 272 return … … 278 303 direction = 'ASC' 279 304 end 280 if fld = field_or_ param(fld_name, table, :order)305 if fld = field_or_attr(fld_name, table, :order) 281 306 res << "#{fld} #{direction.upcase}" 282 307 elsif fld.nil? … … 309 334 else 310 335 @errors << "invalid limit clause '#{limit}'" 336 nil 337 end 338 end 339 340 def parse_paginate_clause(paginate) 341 return @offset unless paginate 342 if !@limit 343 # TODO: raise error ? 344 @errors << "invalid paginate clause '#{paginate}' (used without limit)" 345 nil 346 elsif (fld = map_literal(paginate, :ruby)) && (page_size = @limit[/ LIMIT (\d+)/,1]) 347 @page_size = [2,page_size.to_i].max 348 " OFFSET \#{((#{fld}.to_i > 0 ? #{fld}.to_i : 1)-1)*#{@page_size}}" 349 else 350 @errors << "invalid paginate clause '#{paginate}'" 311 351 nil 312 352 end … … 454 494 end 455 495 456 # Map a field to be used inside a query 457 def field_or_ param(fld, table_name = table, context = nil)496 # Map a field to be used inside a query. An attr is a field from table at index 0 = @node attribute. 497 def field_or_attr(fld, table_name = table, context = nil) 458 498 if fld =~ /^\d+$/ 459 499 return fld … … 467 507 map_field(fld, table_name, context) 468 508 else 469 map_ parameter(fld)509 map_attr(fld) 470 510 end 471 511 end … … 504 544 505 545 if fields = context_filter_fields(clause, is_last) 506 @where << "#{field_or_ param(fields[0])} = #{field_or_param(fields[1], table(main_table,-1))}" if fields != :void546 @where << "#{field_or_attr(fields[0])} = #{field_or_attr(fields[1], table(main_table,-1))}" if fields != :void 507 547 else 508 548 @errors << "invalid context '#{clause}'" … … 511 551 512 552 # Map a litteral value to be used inside a query 513 def map_literal(value )514 value.inspect553 def map_literal(value, env = :sql) 554 env == :sql ? value.inspect : value 515 555 end 516 556 … … 525 565 end 526 566 527 def map_ parameter(fld)567 def map_attr(fld) 528 568 fld.to_s.upcase 529 569 end … … 586 626 587 627 @limit = parse_limit_clause(@offset_limit_order_group[:limit]) 588 @offset = parse_offset_clause(@offset_limit_order_group[:offset]) 628 if @offset_limit_order_group[:paginate] 629 @offset = parse_paginate_clause(@offset_limit_order_group[:paginate]) 630 else 631 @offset = parse_offset_clause(@offset_limit_order_group[:offset]) 632 end 589 633 590 634 … … 620 664 last_element = elements.last 621 665 last_element, @offset_limit_order_group[:offset] = last_element.split(' offset ') 666 last_element, @offset_limit_order_group[:paginate] = last_element.split(' paginate ') 622 667 last_element, @offset_limit_order_group[:limit] = last_element.split(' limit ') 623 668 last_element, @offset_limit_order_group[:order] = last_element.split(' order by ') trunk/lib/query_builder/test/query_builder/basic.yml
r1040 r1197 38 38 res: "SELECT objects.* FROM objects WHERE objects.project_id = PROJECT_ID LIMIT 2 OFFSET 3" 39 39 40 paginate: 41 src: "objects in site limit 2 paginate p" 42 res: "SELECT objects.* FROM objects LIMIT 2 OFFSET #{((p.to_i > 0 ? p.to_i : 1)-1)*2}" 43 40 44 recipients_or_objects: 41 45 src: … … 49 53 src: "abc" 50 54 res: "SELECT a,34 AS number,c FROM test WHERE 3 AND 2 AND 1 ORDER BY a" 55 56 count_sql: 57 src: "objects in project" 58 count: "SELECT COUNT(*) FROM objects WHERE objects.project_id = PROJECT_ID" trunk/lib/query_builder/test/query_builder/errors.yml
r1121 r1197 1 1 bad_relation: 2 2 src: "bolobolo" 3 res: 3 res: "unknown relation 'bolobolo'" 4 4 5 5 bad_relation_in_alternate_query: … …
