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
|