curly-templates 1.0.1 → 2.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +13 -4
 - data/README.md +179 -8
 - data/Rakefile +1 -1
 - data/curly-templates.gemspec +10 -3
 - data/lib/curly.rb +4 -4
 - data/lib/curly/attribute_parser.rb +69 -0
 - data/lib/curly/compiler.rb +77 -47
 - data/lib/curly/component_compiler.rb +119 -0
 - data/lib/curly/component_parser.rb +13 -0
 - data/lib/curly/incomplete_block_error.rb +3 -3
 - data/lib/curly/incorrect_ending_error.rb +17 -3
 - data/lib/curly/invalid_component.rb +13 -0
 - data/lib/curly/presenter.rb +31 -11
 - data/lib/curly/scanner.rb +24 -11
 - data/spec/attribute_parser_spec.rb +46 -0
 - data/spec/compiler/collections_spec.rb +153 -0
 - data/spec/compiler_spec.rb +18 -34
 - data/spec/component_compiler_spec.rb +160 -0
 - data/spec/incorrect_ending_error_spec.rb +13 -0
 - data/spec/presenter_spec.rb +27 -10
 - data/spec/scanner_spec.rb +22 -13
 - data/spec/spec_helper.rb +15 -0
 - data/spec/template_handler_spec.rb +1 -1
 - metadata +16 -5
 - data/lib/curly/invalid_reference.rb +0 -13
 
    
        data/lib/curly/scanner.rb
    CHANGED
    
    | 
         @@ -15,8 +15,9 @@ module Curly 
     | 
|
| 
       15 
15 
     | 
    
         
             
                ESCAPED_CURLY_START = /\{\{\{/
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                COMMENT_MARKER = /!/
         
     | 
| 
       18 
     | 
    
         
            -
                 
     | 
| 
      
 18 
     | 
    
         
            +
                CONDITIONAL_BLOCK_MARKER = /#/
         
     | 
| 
       19 
19 
     | 
    
         
             
                INVERSE_BLOCK_MARKER = /\^/
         
     | 
| 
      
 20 
     | 
    
         
            +
                COLLECTION_BLOCK_MARKER = /\*/
         
     | 
| 
       20 
21 
     | 
    
         
             
                END_BLOCK_MARKER = /\//
         
     | 
| 
       21 
22 
     | 
    
         | 
| 
       22 
23 
     | 
    
         | 
| 
         @@ -27,7 +28,7 @@ module Curly 
     | 
|
| 
       27 
28 
     | 
    
         
             
                # Example
         
     | 
| 
       28 
29 
     | 
    
         
             
                #
         
     | 
| 
       29 
30 
     | 
    
         
             
                #   Curly::Scanner.scan("hello {{name}}!")
         
     | 
| 
       30 
     | 
    
         
            -
                #   #=> [[:text, "hello "], [: 
     | 
| 
      
 31 
     | 
    
         
            +
                #   #=> [[:text, "hello "], [:component, "name"], [:text, "!"]]
         
     | 
| 
       31 
32 
     | 
    
         
             
                #
         
     | 
| 
       32 
33 
     | 
    
         
             
                # Returns an Array of type/value pairs representing the tokens in the
         
     | 
| 
       33 
34 
     | 
    
         
             
                #   template.
         
     | 
| 
         @@ -70,14 +71,16 @@ module Curly 
     | 
|
| 
       70 
71 
     | 
    
         
             
                def scan_tag
         
     | 
| 
       71 
72 
     | 
    
         
             
                  if @scanner.scan(COMMENT_MARKER)
         
     | 
| 
       72 
73 
     | 
    
         
             
                    scan_comment
         
     | 
| 
       73 
     | 
    
         
            -
                  elsif @scanner.scan( 
     | 
| 
       74 
     | 
    
         
            -
                     
     | 
| 
      
 74 
     | 
    
         
            +
                  elsif @scanner.scan(CONDITIONAL_BLOCK_MARKER)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    scan_conditional_block_start
         
     | 
| 
       75 
76 
     | 
    
         
             
                  elsif @scanner.scan(INVERSE_BLOCK_MARKER)
         
     | 
| 
       76 
77 
     | 
    
         
             
                    scan_inverse_block_start
         
     | 
| 
      
 78 
     | 
    
         
            +
                  elsif @scanner.scan(COLLECTION_BLOCK_MARKER)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    scan_collection_block_start
         
     | 
| 
       77 
80 
     | 
    
         
             
                  elsif @scanner.scan(END_BLOCK_MARKER)
         
     | 
| 
       78 
81 
     | 
    
         
             
                    scan_block_end
         
     | 
| 
       79 
82 
     | 
    
         
             
                  else
         
     | 
| 
       80 
     | 
    
         
            -
                     
     | 
| 
      
 83 
     | 
    
         
            +
                    scan_component
         
     | 
| 
       81 
84 
     | 
    
         
             
                  end
         
     | 
| 
       82 
85 
     | 
    
         
             
                end
         
     | 
| 
       83 
86 
     | 
    
         | 
| 
         @@ -87,27 +90,37 @@ module Curly 
     | 
|
| 
       87 
90 
     | 
    
         
             
                  end
         
     | 
| 
       88 
91 
     | 
    
         
             
                end
         
     | 
| 
       89 
92 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
                def  
     | 
| 
      
 93 
     | 
    
         
            +
                def scan_conditional_block_start
         
     | 
| 
       91 
94 
     | 
    
         
             
                  if value = scan_until_end_of_curly
         
     | 
| 
       92 
     | 
    
         
            -
                    [: 
     | 
| 
      
 95 
     | 
    
         
            +
                    [:conditional_block_start, value]
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def scan_collection_block_start
         
     | 
| 
      
 100 
     | 
    
         
            +
                  if value = scan_until_end_of_curly
         
     | 
| 
      
 101 
     | 
    
         
            +
                    [:collection_block_start, value]
         
     | 
| 
       93 
102 
     | 
    
         
             
                  end
         
     | 
| 
       94 
103 
     | 
    
         
             
                end
         
     | 
| 
       95 
104 
     | 
    
         | 
| 
       96 
105 
     | 
    
         
             
                def scan_inverse_block_start
         
     | 
| 
       97 
106 
     | 
    
         
             
                  if value = scan_until_end_of_curly
         
     | 
| 
       98 
     | 
    
         
            -
                    [: 
     | 
| 
      
 107 
     | 
    
         
            +
                    [:inverse_conditional_block_start, value]
         
     | 
| 
       99 
108 
     | 
    
         
             
                  end
         
     | 
| 
       100 
109 
     | 
    
         
             
                end
         
     | 
| 
       101 
110 
     | 
    
         | 
| 
       102 
111 
     | 
    
         
             
                def scan_block_end
         
     | 
| 
       103 
112 
     | 
    
         
             
                  if value = scan_until_end_of_curly
         
     | 
| 
       104 
     | 
    
         
            -
                     
     | 
| 
      
 113 
     | 
    
         
            +
                    if value.end_with?("?")
         
     | 
| 
      
 114 
     | 
    
         
            +
                      [:conditional_block_end, value]
         
     | 
| 
      
 115 
     | 
    
         
            +
                    else
         
     | 
| 
      
 116 
     | 
    
         
            +
                      [:collection_block_end, value]
         
     | 
| 
      
 117 
     | 
    
         
            +
                    end
         
     | 
| 
       105 
118 
     | 
    
         
             
                  end
         
     | 
| 
       106 
119 
     | 
    
         
             
                end
         
     | 
| 
       107 
120 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                def  
     | 
| 
      
 121 
     | 
    
         
            +
                def scan_component
         
     | 
| 
       109 
122 
     | 
    
         
             
                  if value = scan_until_end_of_curly
         
     | 
| 
       110 
     | 
    
         
            -
                    [: 
     | 
| 
      
 123 
     | 
    
         
            +
                    [:component, value]
         
     | 
| 
       111 
124 
     | 
    
         
             
                  end
         
     | 
| 
       112 
125 
     | 
    
         
             
                end
         
     | 
| 
       113 
126 
     | 
    
         | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Curly::AttributeParser do
         
     | 
| 
      
 4 
     | 
    
         
            +
              it "parses attributes" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                parse("width=10px height=20px").should == {
         
     | 
| 
      
 6 
     | 
    
         
            +
                  "width" => "10px",
         
     | 
| 
      
 7 
     | 
    
         
            +
                  "height" => "20px"
         
     | 
| 
      
 8 
     | 
    
         
            +
                }
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              it "parses single quoted values" do
         
     | 
| 
      
 12 
     | 
    
         
            +
                parse("title='hello world'").should == { "title" => "hello world" }
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              it "parses double quoted values" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                parse('title="hello world"').should == { "title" => "hello world" }
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              it "parses mixed quotes" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                parse(%[x=y q="foo's bar" v='bim " bum' t="foo ' bar"]).should == {
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "x" => "y",
         
     | 
| 
      
 22 
     | 
    
         
            +
                  "q" => "foo's bar",
         
     | 
| 
      
 23 
     | 
    
         
            +
                  "t" => "foo ' bar",
         
     | 
| 
      
 24 
     | 
    
         
            +
                  "v" => 'bim " bum'
         
     | 
| 
      
 25 
     | 
    
         
            +
                }
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              it "deals with weird whitespace" do
         
     | 
| 
      
 29 
     | 
    
         
            +
                parse(" size=big  ").should == { "size" => "big" }
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              it "parses empty attribute lists" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                parse(nil).should == {}
         
     | 
| 
      
 34 
     | 
    
         
            +
                parse("").should == {}
         
     | 
| 
      
 35 
     | 
    
         
            +
                parse(" ").should == {}
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              it "fails when an invalid attribute list is passed" do
         
     | 
| 
      
 39 
     | 
    
         
            +
                expect { parse("foo") }.to raise_exception(Curly::AttributeError)
         
     | 
| 
      
 40 
     | 
    
         
            +
                expect { parse("foo=") }.to raise_exception(Curly::AttributeError)
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def parse(str)
         
     | 
| 
      
 44 
     | 
    
         
            +
                described_class.parse(str)
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,153 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Curly::Compiler do
         
     | 
| 
      
 4 
     | 
    
         
            +
              include CompilationSupport
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              let(:presenter_class) do
         
     | 
| 
      
 7 
     | 
    
         
            +
                Class.new(Curly::Presenter) do
         
     | 
| 
      
 8 
     | 
    
         
            +
                  presents :list
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def title
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @list.title
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def items(status: nil)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    if status
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @list.items.select {|item| item.status == status }
         
     | 
| 
      
 17 
     | 
    
         
            +
                    else
         
     | 
| 
      
 18 
     | 
    
         
            +
                      @list.items
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def companies
         
     | 
| 
      
 23 
     | 
    
         
            +
                    "Nike, Adidas"
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  def numbers
         
     | 
| 
      
 27 
     | 
    
         
            +
                    "one, two, three"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              let(:simple_presenter_class) do
         
     | 
| 
      
 33 
     | 
    
         
            +
                Class.new(Curly::Presenter) do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  presents :company
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def name
         
     | 
| 
      
 37 
     | 
    
         
            +
                    @company
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              let(:inner_presenter_class) do
         
     | 
| 
      
 43 
     | 
    
         
            +
                Class.new(Curly::Presenter) do
         
     | 
| 
      
 44 
     | 
    
         
            +
                  presents :item, :item_counter
         
     | 
| 
      
 45 
     | 
    
         
            +
                  presents :list, default: nil
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  attr_reader :item_counter
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def name
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @item.name
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def list_title
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @list.title
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  def parts
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @item.parts
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              let(:inner_inner_presenter_class) do
         
     | 
| 
      
 64 
     | 
    
         
            +
                Class.new(Curly::Presenter) do
         
     | 
| 
      
 65 
     | 
    
         
            +
                  presents :part
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def identifier
         
     | 
| 
      
 68 
     | 
    
         
            +
                    @part.identifier
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              let(:list) { double("list", title: "Inventory") }
         
     | 
| 
      
 74 
     | 
    
         
            +
              let(:context) { double("context") }
         
     | 
| 
      
 75 
     | 
    
         
            +
              let(:presenter) { presenter_class.new(context, list: list) }
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
              before do
         
     | 
| 
      
 78 
     | 
    
         
            +
                stub_const("ItemPresenter", inner_presenter_class)
         
     | 
| 
      
 79 
     | 
    
         
            +
                stub_const("PartPresenter", inner_inner_presenter_class)
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
              it "compiles collection blocks" do
         
     | 
| 
      
 83 
     | 
    
         
            +
                item1 = double("item1", name: "foo")
         
     | 
| 
      
 84 
     | 
    
         
            +
                item2 = double("item2", name: "bar")
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                list.stub(:items) { [item1, item2] }
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                template = "<ul>{{*items}}<li>{{name}}</li>{{/items}}</ul>"
         
     | 
| 
      
 89 
     | 
    
         
            +
                expect(evaluate(template)).to eql "<ul><li>foo</li><li>bar</li></ul>"
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
              it "allows attributes on collection blocks" do
         
     | 
| 
      
 93 
     | 
    
         
            +
                item1 = double("item1", name: "foo", status: "active")
         
     | 
| 
      
 94 
     | 
    
         
            +
                item2 = double("item2", name: "bar", status: "inactive")
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                list.stub(:items) { [item1, item2] }
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                template = "<ul>{{*items status=active}}<li>{{name}}</li>{{/items}}</ul>"
         
     | 
| 
      
 99 
     | 
    
         
            +
                expect(evaluate(template)).to eql "<ul><li>foo</li></ul>"
         
     | 
| 
      
 100 
     | 
    
         
            +
              end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
              it "fails if the component isn't available" do
         
     | 
| 
      
 103 
     | 
    
         
            +
                template = "<ul>{{*doodads}}<li>{{name}}</li>{{/doodads}}</ul>"
         
     | 
| 
      
 104 
     | 
    
         
            +
                expect { evaluate(template) }.to raise_exception(Curly::Error)
         
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
              it "fails if the component doesn't support enumeration" do
         
     | 
| 
      
 108 
     | 
    
         
            +
                template = "<ul>{{*numbers}}<li>{{name}}</li>{{/numbers}}</ul>"
         
     | 
| 
      
 109 
     | 
    
         
            +
                expect { evaluate(template) }.to raise_exception(Curly::Error)
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
              it "works even if the component method doesn't return an Array" do
         
     | 
| 
      
 113 
     | 
    
         
            +
                stub_const("CompanyPresenter", simple_presenter_class)
         
     | 
| 
      
 114 
     | 
    
         
            +
                template = "<ul>{{*companies}}<li>{{name}}</li>{{/companies}}</ul>"
         
     | 
| 
      
 115 
     | 
    
         
            +
                expect(evaluate(template)).to eql "<ul><li>Nike, Adidas</li></ul>"
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
              it "passes the index of the current item to the nested presenter" do
         
     | 
| 
      
 119 
     | 
    
         
            +
                item1 = double("item1")
         
     | 
| 
      
 120 
     | 
    
         
            +
                item2 = double("item2")
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                list.stub(:items) { [item1, item2] }
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                template = "<ul>{{*items}}<li>{{item_counter}}</li>{{/items}}</ul>"
         
     | 
| 
      
 125 
     | 
    
         
            +
                expect(evaluate(template)).to eql "<ul><li>1</li><li>2</li></ul>"
         
     | 
| 
      
 126 
     | 
    
         
            +
              end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
              it "restores the previous scope after exiting the collection block" do
         
     | 
| 
      
 129 
     | 
    
         
            +
                part = double("part", identifier: "X")
         
     | 
| 
      
 130 
     | 
    
         
            +
                item = double("item", name: "foo", parts: [part])
         
     | 
| 
      
 131 
     | 
    
         
            +
                list.stub(:items) { [item] }
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                template = "{{*items}}{{*parts}}{{identifier}}{{/parts}}{{name}}{{/items}}{{title}}"
         
     | 
| 
      
 134 
     | 
    
         
            +
                expect(evaluate(template)).to eql "XfooInventory"
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
              it "passes the parent presenter's options to the nested presenter" do
         
     | 
| 
      
 138 
     | 
    
         
            +
                list.stub(:items) { [double(name: "foo"), double(name: "bar")] }
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                template = "{{*items}}{{list_title}}: {{name}}. {{/items}}"
         
     | 
| 
      
 141 
     | 
    
         
            +
                expect(evaluate(template, list: list)).to eql "Inventory: foo. Inventory: bar. "
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
              it "compiles nested collection blocks" do
         
     | 
| 
      
 145 
     | 
    
         
            +
                item1 = double("item1", name: "item1", parts: [double(identifier: "A"), double(identifier: "B")])
         
     | 
| 
      
 146 
     | 
    
         
            +
                item2 = double("item2", name: "item2", parts: [double(identifier: "C"), double(identifier: "D")])
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                list.stub(:items) { [item1, item2] }
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                template = "{{title}}: {{*items}}{{name}} - {{*parts}}{{identifier}}{{/parts}}. {{/items}}"
         
     | 
| 
      
 151 
     | 
    
         
            +
                expect(evaluate(template)).to eql "Inventory: item1 - AB. item2 - CD. "
         
     | 
| 
      
 152 
     | 
    
         
            +
              end
         
     | 
| 
      
 153 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/compiler_spec.rb
    CHANGED
    
    | 
         @@ -1,6 +1,8 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'spec_helper'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            describe Curly::Compiler do
         
     | 
| 
      
 4 
     | 
    
         
            +
              include CompilationSupport
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       4 
6 
     | 
    
         
             
              let :presenter_class do
         
     | 
| 
       5 
7 
     | 
    
         
             
                Class.new do
         
     | 
| 
       6 
8 
     | 
    
         
             
                  def foo
         
     | 
| 
         @@ -27,6 +29,10 @@ describe Curly::Compiler do 
     | 
|
| 
       27 
29 
     | 
    
         
             
                    nil
         
     | 
| 
       28 
30 
     | 
    
         
             
                  end
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                  def square?(width:, height:)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    width.to_i == height.to_i
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
       30 
36 
     | 
    
         
             
                  def false?
         
     | 
| 
       31 
37 
     | 
    
         
             
                    false
         
     | 
| 
       32 
38 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -35,16 +41,11 @@ describe Curly::Compiler do 
     | 
|
| 
       35 
41 
     | 
    
         
             
                    true
         
     | 
| 
       36 
42 
     | 
    
         
             
                  end
         
     | 
| 
       37 
43 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
                  def  
     | 
| 
       39 
     | 
    
         
            -
                     
     | 
| 
       40 
     | 
    
         
            -
                  end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                  def self.method_available?(method)
         
     | 
| 
       43 
     | 
    
         
            -
                    [:foo, :parameterized, :high_yield, :yield_value, :dirty,
         
     | 
| 
       44 
     | 
    
         
            -
                      :false?, :true?, :hello?].include?(method)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  def self.component_available?(method)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    %w[foo high_yield yield_value dirty false? true? hello? square?].include?(method)
         
     | 
| 
       45 
46 
     | 
    
         
             
                  end
         
     | 
| 
       46 
47 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                  def self. 
     | 
| 
      
 48 
     | 
    
         
            +
                  def self.available_components
         
     | 
| 
       48 
49 
     | 
    
         
             
                    public_instance_methods
         
     | 
| 
       49 
50 
     | 
    
         
             
                  end
         
     | 
| 
       50 
51 
     | 
    
         | 
| 
         @@ -63,14 +64,6 @@ describe Curly::Compiler do 
     | 
|
| 
       63 
64 
     | 
    
         
             
                  evaluate("{{foo}}").should == "FOO"
         
     | 
| 
       64 
65 
     | 
    
         
             
                end
         
     | 
| 
       65 
66 
     | 
    
         | 
| 
       66 
     | 
    
         
            -
                it "passes on an optional reference parameter to the presenter method" do
         
     | 
| 
       67 
     | 
    
         
            -
                  evaluate("{{parameterized.foo.bar}}").should == "foo.bar"
         
     | 
| 
       68 
     | 
    
         
            -
                end
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                it "passes an empty string to methods that take a parameter when none is provided" do
         
     | 
| 
       71 
     | 
    
         
            -
                  evaluate("{{parameterized}}").should == ""
         
     | 
| 
       72 
     | 
    
         
            -
                end
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
67 
     | 
    
         
             
                it "raises ArgumentError if the presenter class is nil" do
         
     | 
| 
       75 
68 
     | 
    
         
             
                  expect do
         
     | 
| 
       76 
69 
     | 
    
         
             
                    Curly::Compiler.compile("foo", nil)
         
     | 
| 
         @@ -78,15 +71,15 @@ describe Curly::Compiler do 
     | 
|
| 
       78 
71 
     | 
    
         
             
                end
         
     | 
| 
       79 
72 
     | 
    
         | 
| 
       80 
73 
     | 
    
         
             
                it "makes sure only public methods are called on the presenter object" do
         
     | 
| 
       81 
     | 
    
         
            -
                  expect { evaluate("{{bar}}") }.to raise_exception(Curly:: 
     | 
| 
      
 74 
     | 
    
         
            +
                  expect { evaluate("{{bar}}") }.to raise_exception(Curly::InvalidComponent)
         
     | 
| 
       82 
75 
     | 
    
         
             
                end
         
     | 
| 
       83 
76 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
                it "includes the invalid  
     | 
| 
      
 77 
     | 
    
         
            +
                it "includes the invalid component when failing to compile" do
         
     | 
| 
       85 
78 
     | 
    
         
             
                  begin
         
     | 
| 
       86 
79 
     | 
    
         
             
                    evaluate("{{bar}}")
         
     | 
| 
       87 
80 
     | 
    
         
             
                    fail
         
     | 
| 
       88 
     | 
    
         
            -
                  rescue Curly:: 
     | 
| 
       89 
     | 
    
         
            -
                    e. 
     | 
| 
      
 81 
     | 
    
         
            +
                  rescue Curly::InvalidComponent => e
         
     | 
| 
      
 82 
     | 
    
         
            +
                    e.component.should == "bar"
         
     | 
| 
       90 
83 
     | 
    
         
             
                  end
         
     | 
| 
       91 
84 
     | 
    
         
             
                end
         
     | 
| 
       92 
85 
     | 
    
         | 
| 
         @@ -141,6 +134,10 @@ describe Curly::Compiler do 
     | 
|
| 
       141 
134 
     | 
    
         
             
                  evaluate("{{#hello.world?}}foo{{/hello.world?}}{{#hello.foo?}}bar{{/hello.foo?}}").should == "foo"
         
     | 
| 
       142 
135 
     | 
    
         
             
                end
         
     | 
| 
       143 
136 
     | 
    
         | 
| 
      
 137 
     | 
    
         
            +
                it "passes attributes to blocks" do
         
     | 
| 
      
 138 
     | 
    
         
            +
                  evaluate("{{#square? width=2 height=2}}yeah!{{/square?}}").should == "yeah!"
         
     | 
| 
      
 139 
     | 
    
         
            +
                end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
       144 
141 
     | 
    
         
             
                it "gives an error on mismatching blocks" do
         
     | 
| 
       145 
142 
     | 
    
         
             
                  expect do
         
     | 
| 
       146 
143 
     | 
    
         
             
                    evaluate("test{{#false?}}bar{{/true?}}")
         
     | 
| 
         @@ -174,7 +171,7 @@ describe Curly::Compiler do 
     | 
|
| 
       174 
171 
     | 
    
         
             
                end
         
     | 
| 
       175 
172 
     | 
    
         | 
| 
       176 
173 
     | 
    
         
             
                it "returns false if an unavailable method is referenced" do
         
     | 
| 
       177 
     | 
    
         
            -
                  presenter_class.stub(: 
     | 
| 
      
 174 
     | 
    
         
            +
                  presenter_class.stub(:available_components) { [:foo] }
         
     | 
| 
       178 
175 
     | 
    
         
             
                  validate("Hello, {{inspect}}").should == false
         
     | 
| 
       179 
176 
     | 
    
         
             
                end
         
     | 
| 
       180 
177 
     | 
    
         | 
| 
         @@ -190,17 +187,4 @@ describe Curly::Compiler do 
     | 
|
| 
       190 
187 
     | 
    
         
             
                  Curly.valid?(template, presenter_class)
         
     | 
| 
       191 
188 
     | 
    
         
             
                end
         
     | 
| 
       192 
189 
     | 
    
         
             
              end
         
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
              def evaluate(template, &block)
         
     | 
| 
       195 
     | 
    
         
            -
                code = Curly::Compiler.compile(template, presenter_class)
         
     | 
| 
       196 
     | 
    
         
            -
                context = double("context", presenter: presenter)
         
     | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
       198 
     | 
    
         
            -
                context.instance_eval(<<-RUBY)
         
     | 
| 
       199 
     | 
    
         
            -
                  def self.render
         
     | 
| 
       200 
     | 
    
         
            -
                    #{code}
         
     | 
| 
       201 
     | 
    
         
            -
                  end
         
     | 
| 
       202 
     | 
    
         
            -
                RUBY
         
     | 
| 
       203 
     | 
    
         
            -
             
     | 
| 
       204 
     | 
    
         
            -
                context.render(&block)
         
     | 
| 
       205 
     | 
    
         
            -
              end
         
     | 
| 
       206 
190 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,160 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Curly::ComponentCompiler do
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe ".compile_conditional" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                let(:presenter_class) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  Class.new do
         
     | 
| 
      
 7 
     | 
    
         
            +
                    def monday?
         
     | 
| 
      
 8 
     | 
    
         
            +
                      true
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    def tuesday?
         
     | 
| 
      
 12 
     | 
    
         
            +
                      false
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    def day?(name)
         
     | 
| 
      
 16 
     | 
    
         
            +
                      name == "monday"
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    def season?(name:)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      name == "summer"
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    def hello
         
     | 
| 
      
 24 
     | 
    
         
            +
                      "hello"
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    def self.component_available?(name)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      true
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                it "compiles simple components" do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  evaluate("monday?").should == true
         
     | 
| 
      
 35 
     | 
    
         
            +
                  evaluate("tuesday?").should == false
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                it "compiles components with an identifier" do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  evaluate("day.monday?").should == true
         
     | 
| 
      
 40 
     | 
    
         
            +
                  evaluate("day.tuesday?").should == false
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                it "compiles components with attributes" do
         
     | 
| 
      
 44 
     | 
    
         
            +
                  evaluate("season? name=summer").should == true
         
     | 
| 
      
 45 
     | 
    
         
            +
                  evaluate("season? name=winter").should == false
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                it "fails if the component is missing a question mark" do
         
     | 
| 
      
 49 
     | 
    
         
            +
                  expect { evaluate("hello") }.to raise_exception(Curly::Error)
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def evaluate(component, &block)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  method, argument, attributes = Curly::ComponentParser.parse(component)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  code = Curly::ComponentCompiler.compile_conditional(presenter_class, method, argument, attributes)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  presenter = presenter_class.new
         
     | 
| 
      
 56 
     | 
    
         
            +
                  context = double("context", presenter: presenter)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  context.instance_eval(<<-RUBY)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    def self.render
         
     | 
| 
      
 60 
     | 
    
         
            +
                      #{code}
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  RUBY
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  context.render(&block)
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              describe ".compile_component" do
         
     | 
| 
      
 69 
     | 
    
         
            +
                let(:presenter_class) do
         
     | 
| 
      
 70 
     | 
    
         
            +
                  Class.new do
         
     | 
| 
      
 71 
     | 
    
         
            +
                    def title
         
     | 
| 
      
 72 
     | 
    
         
            +
                      "Welcome!"
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    def i18n(key, fallback: nil)
         
     | 
| 
      
 76 
     | 
    
         
            +
                      case key
         
     | 
| 
      
 77 
     | 
    
         
            +
                      when "home.welcome" then "Welcome to our lovely place!"
         
     | 
| 
      
 78 
     | 
    
         
            +
                      else fallback
         
     | 
| 
      
 79 
     | 
    
         
            +
                      end
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    def summary(length = "long")
         
     | 
| 
      
 83 
     | 
    
         
            +
                      case length
         
     | 
| 
      
 84 
     | 
    
         
            +
                      when "long" then "This is a long summary"
         
     | 
| 
      
 85 
     | 
    
         
            +
                      when "short" then "This is a short summary"
         
     | 
| 
      
 86 
     | 
    
         
            +
                      end
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    def invalid(x, y)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    def widget(size:, color: nil)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      s = "Widget (#{size})"
         
     | 
| 
      
 94 
     | 
    
         
            +
                      s << " - #{color}" if color
         
     | 
| 
      
 95 
     | 
    
         
            +
                      s
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    def self.component_available?(name)
         
     | 
| 
      
 99 
     | 
    
         
            +
                      true
         
     | 
| 
      
 100 
     | 
    
         
            +
                    end
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                it "compiles components with identifiers" do
         
     | 
| 
      
 105 
     | 
    
         
            +
                  evaluate("i18n.home.welcome").should == "Welcome to our lovely place!"
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                it "compiles components with optional identifiers" do
         
     | 
| 
      
 109 
     | 
    
         
            +
                  evaluate("summary").should == "This is a long summary"
         
     | 
| 
      
 110 
     | 
    
         
            +
                  evaluate("summary.short").should == "This is a short summary"
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                it "compiles components with attributes" do
         
     | 
| 
      
 114 
     | 
    
         
            +
                  evaluate("widget size=100px").should == "Widget (100px)"
         
     | 
| 
      
 115 
     | 
    
         
            +
                end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                it "compiles components with optional attributes" do
         
     | 
| 
      
 118 
     | 
    
         
            +
                  evaluate("widget color=blue size=50px").should == "Widget (50px) - blue"
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                it "allows both identifier and attributes" do
         
     | 
| 
      
 122 
     | 
    
         
            +
                  evaluate("i18n.hello fallback=yolo").should == "yolo"
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                it "fails when an invalid attribute is used" do
         
     | 
| 
      
 126 
     | 
    
         
            +
                  expect { evaluate("i18n.foo extreme=true") }.to raise_exception(Curly::Error)
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                it "fails when a component is missing a required identifier" do
         
     | 
| 
      
 130 
     | 
    
         
            +
                  expect { evaluate("i18n") }.to raise_exception(Curly::Error)
         
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                it "fails when a component is missing a required attribute" do
         
     | 
| 
      
 134 
     | 
    
         
            +
                  expect { evaluate("widget") }.to raise_exception(Curly::Error)
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                it "fails when an identifier is specified for a component that doesn't support one" do
         
     | 
| 
      
 138 
     | 
    
         
            +
                  expect { evaluate("title.rugby") }.to raise_exception(Curly::Error)
         
     | 
| 
      
 139 
     | 
    
         
            +
                end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                it "fails when the method takes more than one argument" do
         
     | 
| 
      
 142 
     | 
    
         
            +
                  expect { evaluate("invalid") }.to raise_exception(Curly::Error)
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                def evaluate(component, &block)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  method, argument, attributes = Curly::ComponentParser.parse(component)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  code = Curly::ComponentCompiler.compile_component(presenter_class, method, argument, attributes)
         
     | 
| 
      
 148 
     | 
    
         
            +
                  presenter = presenter_class.new
         
     | 
| 
      
 149 
     | 
    
         
            +
                  context = double("context", presenter: presenter)
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                  context.instance_eval(<<-RUBY)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    def self.render
         
     | 
| 
      
 153 
     | 
    
         
            +
                      #{code}
         
     | 
| 
      
 154 
     | 
    
         
            +
                    end
         
     | 
| 
      
 155 
     | 
    
         
            +
                  RUBY
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                  context.render(&block)
         
     | 
| 
      
 158 
     | 
    
         
            +
                end
         
     | 
| 
      
 159 
     | 
    
         
            +
              end
         
     | 
| 
      
 160 
     | 
    
         
            +
            end
         
     |