Changeset 1197

Show
Ignore:
Timestamp:
2008-10-01 17:27:57 (3 months ago)
Author:
gaspard
Message:

commit 338c9ec949e97be2da474882088bbcd9df363fc5
Author: Gaspard Bucher <gaspard@teti.ch>

Started to cleanup testing (functional tests not run during "zena:test" rake task).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/app/controllers/application.rb

    r1185 r1197  
    692692      end 
    693693       
    694       params = (opts == {}) ? '' : ('?' + opts.map{ |k,v| "#{k}=#{v}"}.join('&')) 
     694      params = (opts == {}) ? '' : ('?' + opts.map{ |k,v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')) 
    695695       
    696696      if !asset && node[:id] == current_site[:root_id] && mode.nil? && format == 'html' 
  • trunk/app/helpers/application_helper.rb

    r1185 r1197  
    11591159  end 
    11601160   
    1161   # TODO: could be used by all helpers: faster then routes... Rename obj_link (is used to link to versions) 
    1162   # Used by zafu 
    1163   def node_link(opts={}) 
    1164     options = {:node=>@node}.merge(opts) 
    1165     node = options.delete(:node) 
    1166     if href = options.delete(:href) 
    1167       node = href 
    1168     end     
    1169     return options[:text] unless node 
    1170  
    1171     unless url_only = options.delete(:url_only) 
    1172       text = options.delete(:text) || node.version.title 
    1173       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     end 
    1178     url_only ? zen_path(node, options) :  "<a#{attributes} href='#{zen_path(node, options)}'>#{text}</a>" 
    1179   end 
    1180    
    11811161  # shows links for site features 
    11821162  def show_link(link, opt={}) 
  • trunk/app/models/node.rb

    r1193 r1197  
    995995  end 
    996996   
     997  alias v_title title 
     998   
    997999  def text 
    9981000    version.text 
    9991001  end 
     1002   
     1003  alias v_text text 
    10001004 
    10011005  def title=(t) 
     
    10111015    return nil if new_record? 
    10121016    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 
    10141019    @icon = sql ? do_find(:first, eval("\"#{sql}\""), :ignore_source => !uses_node_name) : nil 
    10151020  end 
  • trunk/lib/comment_query.rb

    r1173 r1197  
    22 
    33class CommentQuery < QueryBuilder 
    4   attr_reader :uses_node_name 
     4  attr_reader :uses_node_name, :node_name 
    55  set_main_table 'comments' 
    66  set_main_class 'Comment' 
     
    2020    when 'author' 
    2121      add_table('users') 
    22       @where << "#{table('users')}.id = #{field_or_param('author_id')}" 
     22      @where << "#{table('users')}.id = #{field_or_attr('author_id')}" 
    2323      # should we move on to Contact ? 
    2424    when 'node', 'nodes' 
     
    7575  end 
    7676   
    77   def map_parameter(fld) 
     77  def map_attr(fld) 
    7878    # error 
    7979    nil 
    8080  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 
    8192end 
  • trunk/lib/node_query.rb

    r1185 r1197  
    33 
    44class NodeQuery < QueryBuilder 
    5   attr_reader :context, :uses_node_name 
     5  attr_reader :context, :uses_node_name, :node_name 
    66  set_main_table 'nodes' 
    77  set_main_class 'Node' 
     
    4646    end 
    4747    @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 
    4859  end 
    4960   
     
    109120      end 
    110121       
    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))}" 
    112123      true 
    113124    end 
     
    119130          # no need to load discussions, versions and all the mess 
    120131          add_table('comments') 
    121           @where << "#{table('comments')}.discussion_id = #{map_parameter('discussion_id')}" 
     132          @where << "#{table('comments')}.discussion_id = #{map_attr('discussion_id')}" 
    122133          return CommentQuery # class change 
    123134        else 
     
    152163          # remove caller join 
    153164          @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))}" 
    157168        end 
    158169      else 
     
    161172    end 
    162173     
    163     def map_literal(value
     174    def map_literal(value, env = :sql
    164175      if value =~ /(.*?)\[(visitor|param):(\w+)\](.*)/ 
    165176        val_start = $1 == '' ? '' : "#{$1.inspect} +" 
     
    167178        case $2 
    168179        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 
    170181        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 
    175186      end 
    176187    end 
     
    238249    end 
    239250     
    240     def map_parameter(fld
     251    def map_attr(fld, env = :sql
    241252      case fld 
    242253      when 'project_id', 'section_id', 'discussion_id' 
     
    251262        @errors << "invalid parameter '#{fld}'" 
    252263        "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 
    253279      end 
    254280    end 
     
    352378      #def build_find(count, pseudo_sql, node_name, raw_filters = nil, ignore_warnings = false, ref_date = nil) 
    353379      def build_find(count, pseudo_sql, opts = {}) 
    354         if count != :all 
     380        if count == :first 
    355381          opts[:limit] = 1 
    356382        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)) 
    359384      end 
    360385    end 
     
    367392        return nil if query.empty? 
    368393        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) 
    371398          if res == [] 
    372399            nil 
    373400          else 
    374             res.each{|r| visitor.visit(r)} 
     401            res.each {|r| visitor.visit(r)} 
    375402            res 
    376403          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 
    379407          res 
     408        when :count 
     409          klass.count_by_sql(query) 
    380410        else 
    381411          nil 
     
    391421          self.send(rel.first) 
    392422        else 
    393           sql, errors = Node.build_find(count, rel, :node_name => 'self') 
     423          sql = Node.build_find(count, rel, :node_name => 'self').to_sql 
    394424          if sql 
    395425            do_find(count, eval("\"#{sql}\"")) 
  • trunk/lib/parser/lib/rules/zena.rb

    r1190 r1197  
    148148      elsif @method =~ /\A(\w+)\s+(\w+)\s+(.+)$/ 
    149149        # 'pages where name ...' 
    150         @params[:method] = @method 
     150        @params[:select] = @method 
    151151        @method = 'context' 
    152152      end 
     
    155155        # ok 
    156156      else 
    157         @params[:method] = @method 
     157        @params[:select] = @method 
    158158        @method = 'context' 
    159159      end 
     
    17301730    # <r:link href='node' tattr='lang'/> 
    17311731    # <r:link update='dom_id'/> 
     1732    # <r:link page='next'/> <r:link page='previous'/> <r:link page='list'/> 
    17321733    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 
    17461757          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 
    17481774        end 
    17491775      end 
    17501776 
    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) 
    17761786        finder, klass = build_finder_for(:first, sharp_in, {}) 
    17771787        return unless finder 
    17781788        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 
    17931792      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 
    17951795        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)) 
    18021799        pre_space = @space_before || '' 
    18031800        @html_tag_done = true 
    18041801      end 
    18051802       
    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 
    18231855    end 
    18241856     
     
    18431875        return unless finder 
    18441876        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 
    18481881    end 
    18491882     
     
    18521885      if @context[:block] == self 
    18531886        # called from self (storing template / rendering) 
    1854         size     = (params[:size] || 'large').to_sym 
    1855         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' 
    18571890        type     = params[:type] ? params[:type].to_sym : :month 
    18581891           
     
    19451978    # use all other tags as relations 
    19461979    def r_unknown 
    1947       @params[:method] = @method 
     1980      @params[:select] = @method 
    19481981      r_context 
    19491982    end 
     
    19551988    def r_context 
    19561989      # 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
    19581991       
    19591992      context = node_class.zafu_known_contexts[method] 
    1960       if context && @params.keys == [:method
     1993      if context && @params.keys == [:select
    19611994        klass = context[:node_class] 
    19621995        # hack to store last 'Node' context until we fix node(Node) stuff: 
     
    19641997        if klass.kind_of?(Array) 
    19651998          # 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) ) 
    19672000        else 
    19682001          # singular 
     
    19702003        end 
    19712004      elsif node_kind_of?(Node) 
    1972         count   = ['first','all'].include?(@params[:find]) ? @params[:find].to_sym : nil 
     2005        count   = ['first','all','count'].include?(@params[:find]) ? @params[:find].to_sym : nil 
    19732006        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) 
    19752008        return unless finder 
    19762009        if node_kind_of?(Node) && !klass.ancestors.include?(Node) 
     
    19802013        if count == :all 
    19812014          # plural 
    1982           do_list( finder, :node_class => klass) 
     2015          do_list( finder, query, :node_class => klass) 
    19832016        # elsif count == :count 
    19842017        #   "<%= #{build_finder_for(count, method, @params)} %>" 
     
    20652098       
    20662099      # 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_query 
    2070         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(', ')) 
    20712104        return nil 
    20722105      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       
    20752115      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] 
    20842125      end 
    20852126    end 
     
    21172158      end 
    21182159       
    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 
    21222169      end 
    21232170       
     
    23162363    end 
    23172364     
    2318     def do_list(list_finder, opts={}) 
     2365    def do_list(list_finder, query, opts={}) 
    23192366      clear_dom_scope 
    23202367       
     
    23292376        @context[:need_link_id] = form_block.need_link_id 
    23302377         
    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         
    23322388        # should we publish ? 
    23332389        publish_after_save ||= form_block ? form_block.params[:publish] : nil 
     
    23652421          out "<% if nil -%>" 
    23662422        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         
    23672433        res = expand_with(:list=>list_var, :in_if => true) 
    23682434        out render_html_tag(res) 
     
    24832549     
    24842550    # Return a different name on each call 
    2485     def unique_name 
    2486       base = context_name 
     2551    def unique_name(base = context_name) 
    24872552      root.next_name_index(base, base == @name).gsub(/[^\d\w\/]/,'_') 
    24882553    end 
     
    28842949      res += "<% if #{opts[:cond]} -%>" if opts[:cond] 
    28852950      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]) 
    29012952      res += "</a>" 
    29022953      if opts[:cond] 
    29032954        if opts[:else] != :void 
    29042955          res += "<% else -%>" 
    2905           res += inner 
     2956          res += text_for_link(opts[:default_text]) 
    29062957        end 
    29072958        res += "<% end -%>" 
     
    29102961    end 
    29112962     
    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 
    29132979      if params[:attr] 
    2914         text = "#{node_attribute(params[:attr])}" 
     2980        string_context ? "<%= #{node_attribute(params[:attr])} %>" : node_attribute(params[:attr]) 
    29152981      elsif params[:tattr] 
    2916         text = "_(#{node_attribute(params[:tattr])})" 
     2982        string_context ? "<%= _(#{node_attribute(params[:tattr])}) %>" : "_(#{node_attribute(params[:tattr])})" 
    29172983      elsif params[:trans] 
    2918         text = _(params[:trans]).inspect 
     2984        string_context ? _(params[:trans]) : _(params[:trans]).inspect 
    29192985      elsif params[:text] 
    2920         text = params[:text].inspect 
     2986        string_context ? params[:text] : params[:text].inspect 
    29212987      elsif use_blocks && @blocks != [] 
    29222988        res  = [] 
     
    29413007        if static 
    29423008          # "just plain text" 
    2943           text = text.inspect 
     3009          string_context ? text : text.inspect 
    29443010        else 
    29453011          # 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 
    29523017    end 
    29533018     
     
    29863051     
    29873052    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       
    29943065      res 
    29953066    end 
  • trunk/lib/query_builder/lib/query_builder.rb

    r1169 r1197  
    1010=end 
    1111class 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 
    1313  @@main_table = {} 
    1414  @@main_class = {} 
     
    8484  end 
    8585   
     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   
    86107  def valid? 
    87108    @errors == [] 
     109  end 
     110   
     111  def pagination_key 
     112    @offset_limit_order_group[:paginate] 
    88113  end 
    89114   
     
    189214          rest = rest[$&.size..-1] 
    190215          fld, type = $1, $2 
    191           unless field = field_or_param(fld, table, :filter) 
     216          unless field = field_or_attr(fld, table, :filter) 
    192217            @errors << "invalid field or value #{fld.inspect}" 
    193218            return 
     
    243268          rest = rest[$&.size..-1] 
    244269          fld = $& 
    245           unless field = field_or_param(fld, table, :filter) 
     270          unless field = field_or_attr(fld, table, :filter) 
    246271            @errors << "invalid field or value #{fld.inspect}" 
    247272            return 
     
    278303            direction = 'ASC' 
    279304          end 
    280           if fld = field_or_param(fld_name, table, :order) 
     305          if fld = field_or_attr(fld_name, table, :order) 
    281306            res << "#{fld} #{direction.upcase}" 
    282307          elsif fld.nil? 
     
    309334      else 
    310335        @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}'" 
    311351        nil 
    312352      end 
     
    454494    end 
    455495     
    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) 
    458498      if fld =~ /^\d+$/ 
    459499        return fld 
     
    467507        map_field(fld, table_name, context) 
    468508      else 
    469         map_parameter(fld) 
     509        map_attr(fld) 
    470510      end 
    471511    end 
     
    504544       
    505545      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 != :void 
     546        @where << "#{field_or_attr(fields[0])} = #{field_or_attr(fields[1], table(main_table,-1))}" if fields != :void 
    507547      else 
    508548        @errors << "invalid context '#{clause}'" 
     
    511551     
    512552    # Map a litteral value to be used inside a query 
    513     def map_literal(value
    514       value.inspect 
     553    def map_literal(value, env = :sql
     554      env == :sql ? value.inspect : value 
    515555    end 
    516556     
     
    525565    end 
    526566     
    527     def map_parameter(fld) 
     567    def map_attr(fld) 
    528568      fld.to_s.upcase 
    529569    end 
     
    586626 
    587627          @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 
    589633 
    590634 
     
    620664        last_element = elements.last 
    621665        last_element, @offset_limit_order_group[:offset] = last_element.split(' offset ') 
     666        last_element, @offset_limit_order_group[:paginate] = last_element.split(' paginate ') 
    622667        last_element, @offset_limit_order_group[:limit]  = last_element.split(' limit ') 
    623668        last_element, @offset_limit_order_group[:order]  = last_element.split(' order by ') 
  • trunk/lib/query_builder/test/query_builder/basic.yml

    r1040 r1197  
    3838  res: "SELECT objects.* FROM objects WHERE objects.project_id = PROJECT_ID LIMIT 2 OFFSET 3" 
    3939 
     40paginate: 
     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 
    4044recipients_or_objects: 
    4145  src: 
     
    4953  src: "abc" 
    5054  res: "SELECT a,34 AS number,c FROM test WHERE 3 AND 2 AND 1 ORDER BY a" 
     55 
     56count_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  
    11bad_relation: 
    22  src: "bolobolo" 
    3   res:  
     3  res: "unknown relation 'bolobolo'" 
    44 
    55bad_relation_in_alternate_query: 
    …</