Changeset 1034

Show
Ignore:
Timestamp:
2008-05-30 14:24:28 (7 months ago)
Author:
gaspard
Message:

Implemented [set] to define parts/values that can be reused latter in the same scope:
<r:set var='entries_count' do='stat' find='count'/>
...
<r:show var='entries_count'/>
Implemented 'eval' parameter for [set] and [show]:
<r:show eval='entries_count * [d_price]'/>

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/lib/parser/lib/parser.rb

    r997 r1034  
    282282   
    283283  # Return a has of all descendants. Find a specific descendant with descendant['form'] for example. 
    284   def descendants 
    285     @descendants ||= begin 
     284  def all_descendants 
     285    @all_descendants ||= begin 
    286286      d = {} 
    287287      @blocks.each do |b| 
     
    299299  end 
    300300   
     301  def descendants(key) 
     302    all_descendants[key] || [] 
     303  end 
     304   
    301305  def ancestors 
    302306    @ancestors ||= begin 
     
    309313  end 
    310314   
    311   alias public_descendants descendants 
     315  alias public_descendants all_descendants 
    312316   
    313317  # Return the last defined parent for the given key. 
     
    325329  # Return the last defined descendant for the given key. 
    326330  def descendant(key) 
    327     (descendants[key] || []).last 
     331    descendants(key).last 
    328332  end 
    329333   
  • trunk/lib/parser/lib/rules/zena.rb

    r1033 r1034  
    122122      end 
    123123      @anchor_param = @params[:anchor] 
    124  
     124       
    125125      true 
    126126    end 
     
    128128    def do_method(sym) 
    129129      method = sym 
     130      pre, post = '', '' 
     131       
    130132      if method == :r_unknown 
    131133        if @method =~ /^\[(.*)\]$/ 
     
    142144      if inc && inc.params[:part] == @name 
    143145        @context["#{@name}_method".to_sym] = method_name = get_template_url[1..-1].gsub('/','_') 
    144         pre = "<% def #{method_name}(depth, node, list); return '' if depth > #{inc.params[:depth] ? [inc.params[:depth].to_i,30].min : 5}; _erbout = '' -%>" 
    145         post = "<% _erbout; end -%><%= #{method_name}(0,#{node},#{list || "[#{node}]"}) %>" 
     146        pre << "<% def #{method_name}(depth, node, list); return '' if depth > #{inc.params[:depth] ? [inc.params[:depth].to_i,30].min : 5}; _erbout = '' -%>" 
     147        post << "<% _erbout; end -%><%= #{method_name}(0,#{node},#{list || "[#{node}]"}) %>" 
    146148        @context[:node] = 'node' 
    147149        @context[:list] = 'list' 
     
    174176      res ||= super(method) 
    175177       
    176       if pre 
    177         "#{pre}#{res}#{post}" 
    178       else 
    179         res 
    180       end 
     178      "#{pre}#{res}#{post}" 
    181179    end 
    182180     
     
    195193    def r_show 
    196194      attribute = @params[:attr] || @params[:tattr] 
    197       if @params[:tattr] 
     195      if var_name = @params[:var] 
     196        return parser_error("var #{@params[:var].inspect} not set") unless @context[:vars] && @context[:vars].include?(var_name) 
     197        attribute_method = "set_#{var_name}" 
     198      elsif @params[:eval] 
     199        return unless attribute_method = parse_eval_parameter(@params[:eval]) 
     200      elsif @params[:tattr] 
    198201        attribute_method = "_(#{node_attribute(attribute, :else=>@params[:else])})" 
    199202      elsif @params[:attr] 
    200         if @params[:format] 
    201           attribute_method = "sprintf(#{@params[:format].inspect}, #{node_attribute(attribute, :else=>@params[:else])})" 
    202         else 
    203           attribute_method = "#{node_attribute(attribute, :else=>@params[:else])}" 
    204         end 
     203        attribute_method = node_attribute(attribute, :else=>@params[:else]) 
    205204      elsif @params[:date] 
    206205        # date can be any attribute v_created_at or updated_at etc. 
     
    237236      else   
    238237        return parser_error("missing attribute") 
     238      end 
     239       
     240      if !@params[:date] && fmt = @params[:format] 
     241        begin 
     242          # test argument 
     243          sprintf(fmt, 123.45) 
     244        rescue ArgumentError 
     245          return parser_error("incorect format #{fmt.inspect}") 
     246        end 
     247        if fmt =~ /%[\d\.]*f/ 
     248          modifier = ".to_f" 
     249        elsif fmt =~ /%[\d\.]*i/ 
     250          modifier = ".to_i" 
     251        else 
     252          modifier = '' 
     253        end 
     254        attribute_method = "sprintf(#{fmt.inspect}, #{attribute_method}#{modifier})" 
    239255      end 
    240256       
     
    322338      do_list("@nodes") 
    323339    end 
     340     
     341     
     342    def r_set 
     343      return parser_error("'var' missing") unless var_name = @params[:var] 
     344      return parser_error("bad value for 'var' (#{var_name.inspect})") unless var_name =~ /^[a-zA-Z_]+$/ 
     345      return '' unless @context[:set] 
     346      if @params[:value] 
     347        out "<% set_#{var_name} = #{@params[:value].inspect} -%>" 
     348      elsif @params[:eval] 
     349        return unless eval_string = parse_eval_parameter(@params[:eval]) 
     350        out "<% set_#{var_name} = #{eval_string} -%>" 
     351      else 
     352        out "<% set_#{var_name} = capture do %>" 
     353        out expand_with(:set => false) # do not propagate 
     354        out "<% end -%>" 
     355      end 
     356    end 
     357     
    324358     
    325359    # TODO: write a test (please) 
     
    703737      set_fields = [] 
    704738      var_name   = base_class.to_s.underscore 
    705       ((descendants['input'] || []) + (descendants['select'] || [])).each do |tag| 
     739      (descendants('input') + descendants('select')).each do |tag| 
    706740        set_fields << "#{var_name}[#{tag.params[:name]}]" 
    707741      end 
     
    873907        add_btn = make(:void, :method=>'add_btn', :params=>@params.dup, :text=>'') 
    874908        add_btn.blocks = blocks 
    875         remove_instance_variable(:@descendants) 
     909        remove_instance_variable(:@all_descendants) 
    876910      end 
    877911       
     
    11621196        return expand_with(:in_if => false) 
    11631197      elsif cond == 'false' 
    1164         if (descendants['else'] || descendants['elsif']
     1198        if descendant('else') || descendant('elsif'
    11651199          return expand_with(:in_if=>true, :only=>['else', 'elsif']) 
    11661200        else 
     
    22702304    end 
    22712305     
     2306    def parse_eval_parameter(str) 
     2307      # evaluate an expression. Can only contain vars, '(', ')', '*', '+', '/', '-', '[attr]' 
     2308      # FIXME: SECURITY (audit this) 
     2309      vars = @context[:vars] || [] 
     2310      parts = str.split(/\s+/) 
     2311      res  = [] 
     2312      test = [] 
     2313      parts.each do |p| 
     2314        if p =~ /\[([\w_]+)\]/ 
     2315          test << 1 
     2316          res << (node_attribute($1) + '.to_f') 
     2317        elsif p =~ /^[a-zA-Z_]+$/ 
     2318          unless vars.include?(p) 
     2319            out parser_error("var #{p.inspect} not set in eval")  
     2320            return nil 
     2321          end 
     2322          test << 1 
     2323          res  << "set_#{p}.to_f" 
     2324        elsif ['(', ')', '*', '+', '/', '-'].include?(p) 
     2325          res  << p 
     2326          test << p 
     2327        elsif p =~ /^[0-9\.]+$/ 
     2328          res  << p 
     2329          test << p 
     2330        else 
     2331          out parser_error("bad argument #{p.inspect} in eval") 
     2332          return nil 
     2333        end 
     2334      end 
     2335      begin 
     2336        begin 
     2337          eval test.join(' ') 
     2338        rescue 
     2339          # rescue evaluation error 
     2340          out parser_error("error in eval") 
     2341          return nil 
     2342        end 
     2343        "(#{res.join(' ')})" 
     2344      rescue SyntaxError => err 
     2345        # rescue compilation error 
     2346        out parser_error("compilation error in eval") 
     2347        return nil 
     2348      end 
     2349    end 
     2350     
    22722351    def find_stored(klass, key) 
    22732352      @context["#{klass}_#{key}"] 
     
    23132392    def parser_error(message, tag=@method) 
    23142393      "<span class='parser_error'>[#{tag}] #{message}</span>" 
     2394    end 
     2395     
     2396    def expand_with(acontext={}) 
     2397      # set variables 
     2398      context = nil 
     2399      pre = '' 
     2400      @blocks.each do |block| 
     2401        next if block.kind_of?(String) || block.method != 'set' 
     2402        @context[:vars] ||= [] 
     2403        context ||= @context.merge(acontext).merge(:set => true) 
     2404        pre << expand_block(block, context) 
     2405        @context[:vars] << block.params[:var] 
     2406      end 
     2407       
     2408      pre + super 
    23152409    end 
    23162410  end 
  • trunk/lib/parser/test/parser_test.rb

    r1006 r1034  
    9696  end 
    9797   
     98  def test_all_descendants 
     99    block = @@test_parsers['zafu'].new( 
     100    "<r:pages><r:each><b do='test'/></r:each><r:add><p><i do='add_link'/><b do='title'/></p></r:add><b do='title'/></r:pages>",  
     101    :helper=>ParserModule::DummyHelper.new(@@test_strings['basic'])) 
     102    assert_equal ['add', 'add_link', 'each', 'pages', 'test', 'title'], block.all_descendants.keys.sort 
     103    assert_equal 2, block.all_descendants['title'].size 
     104    assert_equal ['add_link', 'title'], block.descendant('add').all_descendants.keys.sort 
     105  end 
     106   
    98107  def test_descendants 
    99108    block = @@test_parsers['zafu'].new( 
    100109    "<r:pages><r:each><b do='test'/></r:each><r:add><p><i do='add_link'/><b do='title'/></p></r:add><b do='title'/></r:pages>",  
    101110    :helper=>ParserModule::DummyHelper.new(@@test_strings['basic'])) 
    102     assert_equal ['add', 'add_link', 'each', 'pages', 'test', 'title'], block.descendants.keys.sort 
    103     assert_equal 2, block.descendants['title'].size 
    104     assert_equal ['add_link', 'title'], block.descendant('add').descendants.keys.sort 
     111    assert_equal 2, block.descendants('title').size 
     112    assert_equal ['test'], block.descendants('each')[0].descendants('test').map {|n| n.method} 
     113    assert_equal [], block.descendants('each')[0].descendants('foo') 
    105114  end 
    106115   
     
    118127    "<r:pages><r:each><b do='test'/></r:each><r:add><p><i do='add_link'/><b do='title'/></p></r:add><b do='title'/></r:pages>",  
    119128    :helper=>ParserModule::DummyHelper.new(@@test_strings['basic'])) 
    120     block.descendants.merge('self'=>[block]).each do |k,blocks| 
     129    block.all_descendants.merge('self'=>[block]).each do |k,blocks| 
    121130      blocks.each do |b| 
    122         b.send(:remove_instance_variable, :@descendants) 
     131        b.send(:remove_instance_variable, :@all_descendants) 
    123132        class << b 
    124133          def public_descendants 
     
    132141      end 
    133142    end 
    134     assert_equal ['add', 'add_link', 'each', 'pages', 'title'], block.descendants.keys.sort 
    135     assert_equal ['test'], block.descendant('each').descendants.keys.sort 
     143    assert_equal ['add', 'add_link', 'each', 'pages', 'title'], block.all_descendants.keys.sort 
     144    assert_equal ['test'], block.descendant('each').all_descendants.keys.sort 
    136145  end 
    137146   
  • trunk/test/helpers/zena_parser/basic.yml

    r1033 r1034  
    712712do_not_set_parent_id_if_form_contains_parent_id: 
    713713  src: "<r:children><r:each do='[name]'/><r:add/><r:form><r:select name='parent_id' nodes='projects in site'/><input name='v_title'/></r:form></r:children>" 
    714   res: "!/node\[parent_id\]'\s+value='22'/" 
     714  res: "!/node\[parent_id\].\s+value/" 
    715715 
    716716select_nodes_in_ajax: 
  • trunk/test/helpers/zena_parser/errors.yml

    r1000 r1034  
    3737  src: "<r:date select='<% puts \"mean\" %>'/>" 
    3838  res: "<span class='parser_error'>[date] bad parameter '&lt;% puts \"mean\" %&gt;'</span>" 
     39 
     40show_var: 
     41  src: "<r:show var='boo'/>" 
     42  tem: "<span class='parser_error'>[show] var \"boo\" not set</span>" 
     43 
     44show_bad_sprintf: 
     45  src: "<p do='show' attr='name' format='%.2f %i hop'/>" 
     46  res: "<p><span class='parser_error'>[show] incorect format \"%.2f %i hop\"</span></p>" 
     47 
     48division_by_zero_in_eval: 
     49  src: "<r:set var='foo'>134</r:set><r:show eval='foo / 0'/>" 
     50  res: "<span class='parser_error'>[show] error in eval</span>" 
     51 
     52division_by_zero_in_eval_on_runtime: 
     53  src: "<r:set var='foo'>134</r:set><r:show eval='foo / ( 21 - [id].to_f )'/>" 
     54  res: "Infinity" 
     55 
     56bad_argument_list_in_eval: 
     57  src: "<r:show eval='3 * / * 5'/>" 
     58  res: "<span class='parser_error'>[show] compilation error in eval</span>" 
     59 
     60nil_value: 
     61  src: "<r:show eval='4 * [d_bob]'/>" 
     62  res: "0.0" 
     63 
     64mean_eval: 
     65  src: "<r:show eval='puts \"bad\"'/>" 
     66  tem: "<span class='parser_error'>[show] var \"puts\" not set in eval</span>"