curly-templates 2.5.0 → 2.6.0
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/.gitignore +20 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +4 -2
- data/README.md +76 -3
- data/Rakefile +3 -116
- data/circle.yml +1 -1
- data/curly-templates.gemspec +6 -83
- data/lib/curly.rb +1 -1
- data/lib/curly/compiler.rb +2 -12
- data/lib/curly/component_compiler.rb +6 -3
- data/lib/curly/presenter.rb +32 -20
- data/lib/curly/presenter_name_error.rb +16 -0
- data/lib/curly/rspec.rb +16 -0
- data/lib/curly/version.rb +3 -0
- metadata +14 -72
- data/perf/compile_benchmark.rb +0 -71
- data/perf/compile_profile.rb +0 -64
- data/perf/component_benchmark.rb +0 -41
- data/spec/attribute_scanner_spec.rb +0 -44
- data/spec/collection_blocks_spec.rb +0 -78
- data/spec/compiler/collections_spec.rb +0 -230
- data/spec/compiler/context_blocks_spec.rb +0 -106
- data/spec/compiler_spec.rb +0 -192
- data/spec/component_compiler_spec.rb +0 -107
- data/spec/component_scanner_spec.rb +0 -36
- data/spec/components_spec.rb +0 -43
- data/spec/conditional_blocks_spec.rb +0 -40
- data/spec/dummy/.gitignore +0 -1
- data/spec/dummy/app/controllers/application_controller.rb +0 -2
- data/spec/dummy/app/controllers/dashboards_controller.rb +0 -14
- data/spec/dummy/app/helpers/application_helper.rb +0 -5
- data/spec/dummy/app/presenters/dashboards/collection_presenter.rb +0 -7
- data/spec/dummy/app/presenters/dashboards/item_presenter.rb +0 -23
- data/spec/dummy/app/presenters/dashboards/new_presenter.rb +0 -29
- data/spec/dummy/app/presenters/dashboards/partials_presenter.rb +0 -5
- data/spec/dummy/app/presenters/dashboards/show_presenter.rb +0 -12
- data/spec/dummy/app/presenters/layouts/application_presenter.rb +0 -21
- data/spec/dummy/app/views/dashboards/_item.html.curly +0 -1
- data/spec/dummy/app/views/dashboards/collection.html.curly +0 -8
- data/spec/dummy/app/views/dashboards/new.html.curly +0 -7
- data/spec/dummy/app/views/dashboards/partials.html.curly +0 -3
- data/spec/dummy/app/views/dashboards/show.html.curly +0 -3
- data/spec/dummy/app/views/layouts/application.html.curly +0 -11
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/config/application.rb +0 -12
- data/spec/dummy/config/boot.rb +0 -5
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/test.rb +0 -36
- data/spec/dummy/config/routes.rb +0 -6
- data/spec/generators/controller_generator_spec.rb +0 -34
- data/spec/integration/application_layout_spec.rb +0 -22
- data/spec/integration/collection_blocks_spec.rb +0 -37
- data/spec/integration/context_blocks_spec.rb +0 -27
- data/spec/integration/partials_spec.rb +0 -24
- data/spec/matchers/have_structure.rb +0 -29
- data/spec/parser_spec.rb +0 -92
- data/spec/presenter_spec.rb +0 -247
- data/spec/scanner_spec.rb +0 -124
- data/spec/spec_helper.rb +0 -45
- data/spec/syntax_error_spec.rb +0 -12
- data/spec/template_handler_spec.rb +0 -209
data/spec/compiler_spec.rb
DELETED
@@ -1,192 +0,0 @@
|
|
1
|
-
describe Curly::Compiler do
|
2
|
-
include CompilationSupport
|
3
|
-
|
4
|
-
describe ".compile" do
|
5
|
-
it "raises ArgumentError if the presenter class is nil" do
|
6
|
-
expect do
|
7
|
-
Curly::Compiler.compile("foo", nil)
|
8
|
-
end.to raise_exception(ArgumentError)
|
9
|
-
end
|
10
|
-
|
11
|
-
it "makes sure only public methods are called on the presenter object" do
|
12
|
-
expect { render("{{bar}}") }.to raise_exception(Curly::InvalidComponent)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "includes the invalid component when failing to compile" do
|
16
|
-
begin
|
17
|
-
render("{{bar}}")
|
18
|
-
fail
|
19
|
-
rescue Curly::InvalidComponent => e
|
20
|
-
e.component.should == "bar"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
it "propagates yields to the caller" do
|
25
|
-
define_presenter do
|
26
|
-
def i_want
|
27
|
-
"I want #{yield}!"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
render("{{i_want}}") { "$$$" }.should == "I want $$$!"
|
32
|
-
end
|
33
|
-
|
34
|
-
it "sends along arguments passed to yield" do
|
35
|
-
define_presenter do
|
36
|
-
def hello(&block)
|
37
|
-
"Hello, #{block.call('world')}!"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
render("{{hello}}") {|v| v.upcase }.should == "Hello, WORLD!"
|
42
|
-
end
|
43
|
-
|
44
|
-
it "escapes non HTML safe strings returned from the presenter" do
|
45
|
-
define_presenter do
|
46
|
-
def dirty
|
47
|
-
"<p>dirty</p>"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
render("{{dirty}}").should == "<p>dirty</p>"
|
52
|
-
end
|
53
|
-
|
54
|
-
it "does not escape HTML safe strings returned from the presenter" do
|
55
|
-
define_presenter do
|
56
|
-
def dirty
|
57
|
-
"<p>dirty</p>".html_safe
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
render("{{dirty}}").should == "<p>dirty</p>"
|
62
|
-
end
|
63
|
-
|
64
|
-
it "does not escape HTML in the template itself" do
|
65
|
-
render("<div>").should == "<div>"
|
66
|
-
end
|
67
|
-
|
68
|
-
it "treats all values returned from the presenter as strings" do
|
69
|
-
define_presenter do
|
70
|
-
def foo; 42; end
|
71
|
-
end
|
72
|
-
|
73
|
-
render("{{foo}}").should == "42"
|
74
|
-
end
|
75
|
-
|
76
|
-
it "removes comments from the output" do
|
77
|
-
render("hello{{! I'm a comment, yo }}world").should == "helloworld"
|
78
|
-
end
|
79
|
-
|
80
|
-
it "removes text in false blocks" do
|
81
|
-
define_presenter do
|
82
|
-
def false?
|
83
|
-
false
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
render("{{#false?}}wut{{/false?}}").should == ""
|
88
|
-
end
|
89
|
-
|
90
|
-
it "keeps text in true blocks" do
|
91
|
-
define_presenter do
|
92
|
-
def true?
|
93
|
-
true
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
render("{{#true?}}yello{{/true?}}").should == "yello"
|
98
|
-
end
|
99
|
-
|
100
|
-
it "removes text in inverse true blocks" do
|
101
|
-
define_presenter do
|
102
|
-
def true?
|
103
|
-
true
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
render("{{^true?}}bar{{/true?}}").should == ""
|
108
|
-
end
|
109
|
-
|
110
|
-
it "keeps text in inverse false blocks" do
|
111
|
-
define_presenter do
|
112
|
-
def false?
|
113
|
-
false
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
render("{{^false?}}yeah!{{/false?}}").should == "yeah!"
|
118
|
-
end
|
119
|
-
|
120
|
-
it "passes an argument to blocks" do
|
121
|
-
define_presenter do
|
122
|
-
def hello?(value)
|
123
|
-
value == "world"
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
render("{{#hello.world?}}foo{{/hello.world?}}").should == "foo"
|
128
|
-
render("{{#hello.mars?}}bar{{/hello.mars?}}").should == ""
|
129
|
-
end
|
130
|
-
|
131
|
-
it "passes attributes to blocks" do
|
132
|
-
define_presenter do
|
133
|
-
def square?(width:, height:)
|
134
|
-
width.to_i == height.to_i
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
render("{{#square? width=2 height=2}}yeah!{{/square?}}").should == "yeah!"
|
139
|
-
end
|
140
|
-
|
141
|
-
it "gives an error on incomplete blocks" do
|
142
|
-
expect do
|
143
|
-
render("{{#hello?}}")
|
144
|
-
end.to raise_exception(Curly::IncompleteBlockError)
|
145
|
-
end
|
146
|
-
|
147
|
-
it "gives an error when closing unopened blocks" do
|
148
|
-
expect do
|
149
|
-
render("{{/goodbye?}}")
|
150
|
-
end.to raise_exception(Curly::IncorrectEndingError)
|
151
|
-
end
|
152
|
-
|
153
|
-
it "gives an error on mismatching block ends" do
|
154
|
-
expect do
|
155
|
-
render("{{#x?}}{{#y?}}{{/x?}}{{/y?}}")
|
156
|
-
end.to raise_exception(Curly::IncorrectEndingError)
|
157
|
-
end
|
158
|
-
|
159
|
-
it "does not execute arbitrary Ruby code" do
|
160
|
-
render('#{foo}').should == '#{foo}'
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
describe ".valid?" do
|
165
|
-
it "returns true if only available methods are referenced" do
|
166
|
-
define_presenter do
|
167
|
-
def foo; end
|
168
|
-
end
|
169
|
-
|
170
|
-
validate("Hello, {{foo}}!").should == true
|
171
|
-
end
|
172
|
-
|
173
|
-
it "returns false if a missing method is referenced" do
|
174
|
-
define_presenter
|
175
|
-
validate("Hello, {{i_am_missing}}").should == false
|
176
|
-
end
|
177
|
-
|
178
|
-
it "returns false if an unavailable method is referenced" do
|
179
|
-
define_presenter do
|
180
|
-
def self.available_components
|
181
|
-
[]
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
validate("Hello, {{inspect}}").should == false
|
186
|
-
end
|
187
|
-
|
188
|
-
def validate(template)
|
189
|
-
Curly.valid?(template, ShowPresenter)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
@@ -1,107 +0,0 @@
|
|
1
|
-
describe Curly::ComponentCompiler do
|
2
|
-
describe ".compile" do
|
3
|
-
let(:presenter_class) do
|
4
|
-
Class.new do
|
5
|
-
def title
|
6
|
-
"Welcome!"
|
7
|
-
end
|
8
|
-
|
9
|
-
def i18n(key, fallback: nil)
|
10
|
-
case key
|
11
|
-
when "home.welcome" then "Welcome to our lovely place!"
|
12
|
-
else fallback
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def summary(length = "long")
|
17
|
-
case length
|
18
|
-
when "long" then "This is a long summary"
|
19
|
-
when "short" then "This is a short summary"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def invalid(x, y)
|
24
|
-
end
|
25
|
-
|
26
|
-
def widget(size:, color: nil)
|
27
|
-
s = "Widget (#{size})"
|
28
|
-
s << " - #{color}" if color
|
29
|
-
s
|
30
|
-
end
|
31
|
-
|
32
|
-
def form(&block)
|
33
|
-
"some form"
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.component_available?(name)
|
37
|
-
true
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
it "compiles components with identifiers" do
|
43
|
-
evaluate("i18n.home.welcome").should == "Welcome to our lovely place!"
|
44
|
-
end
|
45
|
-
|
46
|
-
it "compiles components with optional identifiers" do
|
47
|
-
evaluate("summary").should == "This is a long summary"
|
48
|
-
evaluate("summary.short").should == "This is a short summary"
|
49
|
-
end
|
50
|
-
|
51
|
-
it "compiles components with attributes" do
|
52
|
-
evaluate("widget size=100px").should == "Widget (100px)"
|
53
|
-
end
|
54
|
-
|
55
|
-
it "compiles components with optional attributes" do
|
56
|
-
evaluate("widget color=blue size=50px").should == "Widget (50px) - blue"
|
57
|
-
end
|
58
|
-
|
59
|
-
it "compiles context block components" do
|
60
|
-
evaluate("form", type: :context).should == "some form"
|
61
|
-
end
|
62
|
-
|
63
|
-
it "allows both identifier and attributes" do
|
64
|
-
evaluate("i18n.hello fallback=yolo").should == "yolo"
|
65
|
-
end
|
66
|
-
|
67
|
-
it "fails when an invalid attribute is used" do
|
68
|
-
expect { evaluate("i18n.foo extreme=true") }.to raise_exception(Curly::Error)
|
69
|
-
end
|
70
|
-
|
71
|
-
it "fails when a component is missing a required identifier" do
|
72
|
-
expect { evaluate("i18n") }.to raise_exception(Curly::Error)
|
73
|
-
end
|
74
|
-
|
75
|
-
it "fails when a component is missing a required attribute" do
|
76
|
-
expect { evaluate("widget") }.to raise_exception(Curly::Error)
|
77
|
-
end
|
78
|
-
|
79
|
-
it "fails when an identifier is specified for a component that doesn't support one" do
|
80
|
-
expect { evaluate("title.rugby") }.to raise_exception(Curly::Error)
|
81
|
-
end
|
82
|
-
|
83
|
-
it "fails when the method takes more than one argument" do
|
84
|
-
expect { evaluate("invalid") }.to raise_exception(Curly::Error)
|
85
|
-
end
|
86
|
-
|
87
|
-
it "fails when a context block component is used with a method that doesn't take a block" do
|
88
|
-
expect { evaluate("title", type: :context) }.to raise_exception(Curly::Error)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def evaluate(text, type: nil, &block)
|
93
|
-
name, identifier, attributes = Curly::ComponentScanner.scan(text)
|
94
|
-
component = Curly::Parser::Component.new(name, identifier, attributes)
|
95
|
-
code = Curly::ComponentCompiler.compile(presenter_class, component, type: type)
|
96
|
-
presenter = presenter_class.new
|
97
|
-
context = double("context", presenter: presenter)
|
98
|
-
|
99
|
-
context.instance_eval(<<-RUBY)
|
100
|
-
def self.render
|
101
|
-
#{code}
|
102
|
-
end
|
103
|
-
RUBY
|
104
|
-
|
105
|
-
context.render(&block)
|
106
|
-
end
|
107
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
describe Curly::ComponentScanner do
|
2
|
-
it "scans the component name, identifier, and attributes" do
|
3
|
-
scan('hello.world weather="sunny"').should == [
|
4
|
-
"hello",
|
5
|
-
"world",
|
6
|
-
{ "weather" => "sunny" },
|
7
|
-
[]
|
8
|
-
]
|
9
|
-
end
|
10
|
-
|
11
|
-
it "allows context namespaces" do
|
12
|
-
scan('island:beach:hello.world').should == [
|
13
|
-
"hello",
|
14
|
-
"world",
|
15
|
-
{},
|
16
|
-
["island", "beach"]
|
17
|
-
]
|
18
|
-
end
|
19
|
-
|
20
|
-
it "allows a question mark after the identifier" do
|
21
|
-
scan('hello.world?').should == ["hello?", "world", {}, []]
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'allows spaces before and after component' do
|
25
|
-
scan(' hello.world weather="sunny" ').should == [
|
26
|
-
"hello",
|
27
|
-
"world",
|
28
|
-
{ "weather" => "sunny" },
|
29
|
-
[]
|
30
|
-
]
|
31
|
-
end
|
32
|
-
|
33
|
-
def scan(component)
|
34
|
-
described_class.scan(component)
|
35
|
-
end
|
36
|
-
end
|
data/spec/components_spec.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
describe "Components" do
|
2
|
-
include CompilationSupport
|
3
|
-
|
4
|
-
example "with neither identifier nor attributes" do
|
5
|
-
define_presenter do
|
6
|
-
def title
|
7
|
-
"A Clockwork Orange"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
render("{{title}}").should == "A Clockwork Orange"
|
12
|
-
end
|
13
|
-
|
14
|
-
example "with an identifier" do
|
15
|
-
define_presenter do
|
16
|
-
def reverse(str)
|
17
|
-
str.reverse
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
render("{{reverse.123}}").should == "321"
|
22
|
-
end
|
23
|
-
|
24
|
-
example "with attributes" do
|
25
|
-
define_presenter do
|
26
|
-
def double(number:)
|
27
|
-
number.to_i * 2
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
render("{{double number=3}}").should == "6"
|
32
|
-
end
|
33
|
-
|
34
|
-
example "with both identifier and attributes" do
|
35
|
-
define_presenter do
|
36
|
-
def a(href:, title:)
|
37
|
-
content_tag :a, nil, href: href, title: title
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
render(%({{a href="/welcome.html" title="Welcome!"}})).should == %(<a href="/welcome.html" title="Welcome!"></a>)
|
42
|
-
end
|
43
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
describe "Conditional block components" do
|
2
|
-
include CompilationSupport
|
3
|
-
|
4
|
-
example "with neither identifier nor attributes" do
|
5
|
-
define_presenter do
|
6
|
-
def high?
|
7
|
-
true
|
8
|
-
end
|
9
|
-
|
10
|
-
def low?
|
11
|
-
false
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
render("{{#high?}}yup{{/high?}}").should == "yup"
|
16
|
-
render("{{#low?}}nah{{/low?}}").should == ""
|
17
|
-
end
|
18
|
-
|
19
|
-
example "with an identifier" do
|
20
|
-
define_presenter do
|
21
|
-
def even?(number)
|
22
|
-
number.to_i % 2 == 0
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
render("{{#even.42?}}even{{/even.42?}}").should == "even"
|
27
|
-
render("{{#even.13?}}even{{/even.13?}}").should == ""
|
28
|
-
end
|
29
|
-
|
30
|
-
example "with attributes" do
|
31
|
-
define_presenter do
|
32
|
-
def square?(width:, height:)
|
33
|
-
width.to_i == height.to_i
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
render("{{#square? width=2 height=2}}square{{/square?}}").should == "square"
|
38
|
-
render("{{#square? width=3 height=2}}square{{/square?}}").should == ""
|
39
|
-
end
|
40
|
-
end
|
data/spec/dummy/.gitignore
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
log
|