curlybars 1.3.0 → 1.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/lib/curlybars.rb +0 -1
- data/lib/curlybars/configuration.rb +1 -9
- data/lib/curlybars/error/base.rb +2 -0
- data/lib/curlybars/lexer.rb +3 -0
- data/lib/curlybars/method_whitelist.rb +16 -11
- data/lib/curlybars/node/block_helper_else.rb +1 -0
- data/lib/curlybars/node/if_else.rb +1 -1
- data/lib/curlybars/node/path.rb +6 -0
- data/lib/curlybars/node/sub_expression.rb +62 -0
- data/lib/curlybars/node/unless_else.rb +1 -1
- data/lib/curlybars/parser.rb +11 -0
- data/lib/curlybars/processor/tilde.rb +3 -0
- data/lib/curlybars/rendering_support.rb +9 -1
- data/lib/curlybars/template_handler.rb +18 -6
- data/lib/curlybars/version.rb +1 -1
- data/lib/curlybars/visitor.rb +6 -0
- data/spec/acceptance/application_layout_spec.rb +2 -2
- data/spec/acceptance/collection_blocks_spec.rb +1 -1
- data/spec/acceptance/global_helper_spec.rb +1 -1
- data/spec/curlybars/lexer_spec.rb +25 -2
- data/spec/curlybars/method_whitelist_spec.rb +15 -0
- data/spec/curlybars/rendering_support_spec.rb +4 -9
- data/spec/curlybars/template_handler_spec.rb +33 -30
- data/spec/integration/cache_spec.rb +20 -18
- data/spec/integration/node/block_helper_else_spec.rb +0 -2
- data/spec/integration/node/each_else_spec.rb +0 -2
- data/spec/integration/node/each_spec.rb +0 -2
- data/spec/integration/node/helper_spec.rb +12 -2
- data/spec/integration/node/if_else_spec.rb +0 -2
- data/spec/integration/node/if_spec.rb +2 -4
- data/spec/integration/node/output_spec.rb +0 -2
- data/spec/integration/node/partial_spec.rb +0 -2
- data/spec/integration/node/path_spec.rb +0 -2
- data/spec/integration/node/root_spec.rb +0 -2
- data/spec/integration/node/sub_expression_spec.rb +426 -0
- data/spec/integration/node/template_spec.rb +0 -2
- data/spec/integration/node/unless_else_spec.rb +2 -4
- data/spec/integration/node/unless_spec.rb +0 -2
- data/spec/integration/node/with_spec.rb +0 -2
- data/spec/integration/processors_spec.rb +0 -1
- data/spec/integration/visitor_spec.rb +13 -5
- metadata +47 -15
@@ -0,0 +1,426 @@
|
|
1
|
+
describe "{{(helper arg1 arg2 ... key=value ...)}}" do
|
2
|
+
let(:global_helpers_providers) { [IntegrationTest::GlobalHelperProvider.new] }
|
3
|
+
|
4
|
+
describe "#compile" do
|
5
|
+
let(:presenter) { IntegrationTest::Presenter.new(double("view_context")) }
|
6
|
+
|
7
|
+
it "can be an argument to helpers" do
|
8
|
+
template = Curlybars.compile(<<-HBS)
|
9
|
+
{{global_helper (global_helper 'argument' option='value') option='value'}}
|
10
|
+
HBS
|
11
|
+
|
12
|
+
expect(eval(template)).to resemble(<<-HTML)
|
13
|
+
argument - option:value - option:value
|
14
|
+
HTML
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can be an argument to itself" do
|
18
|
+
template = Curlybars.compile(<<-HBS)
|
19
|
+
{{global_helper (global_helper (global_helper 'a' option='b') option='c') option='d'}}
|
20
|
+
HBS
|
21
|
+
|
22
|
+
expect(eval(template)).to resemble(<<-HTML)
|
23
|
+
a - option:b - option:c - option:d
|
24
|
+
HTML
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can handle data objects as argument" do
|
28
|
+
template = Curlybars.compile(<<-HBS)
|
29
|
+
{{global_helper (extract user attribute='first_name') option='value'}}
|
30
|
+
HBS
|
31
|
+
|
32
|
+
expect(eval(template)).to resemble(<<-HTML)
|
33
|
+
Libo - option:value
|
34
|
+
HTML
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can handle calls inside with" do
|
38
|
+
template = Curlybars.compile(<<-HBS)
|
39
|
+
{{#with article}}
|
40
|
+
{{global_helper (extract author attribute='first_name') option='value'}}
|
41
|
+
{{/with}}
|
42
|
+
HBS
|
43
|
+
|
44
|
+
expect(eval(template)).to resemble(<<-HTML)
|
45
|
+
Nicolò - option:value
|
46
|
+
HTML
|
47
|
+
end
|
48
|
+
|
49
|
+
it "does not accept subexpressions in the root" do
|
50
|
+
expect do
|
51
|
+
Curlybars.compile(<<-HBS)
|
52
|
+
{{(join articles attribute='title' separator='-'}}
|
53
|
+
HBS
|
54
|
+
end.to raise_error(Curlybars::Error::Parse)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "can be called within if expressions" do
|
58
|
+
template = Curlybars.compile(<<-HBS)
|
59
|
+
{{#if (calc 3 ">" 1)}}
|
60
|
+
True
|
61
|
+
{{/if}}
|
62
|
+
HBS
|
63
|
+
|
64
|
+
expect(eval(template)).to resemble(<<-HTML)
|
65
|
+
True
|
66
|
+
HTML
|
67
|
+
end
|
68
|
+
|
69
|
+
# Replication of Handlebars' subexpression specs for feature parity
|
70
|
+
# https://github.com/handlebars-lang/handlebars.js/blob/1a08e1d0a7f500f2c1188cbd21750bb9180afcbb/spec/subexpressions.js
|
71
|
+
|
72
|
+
it "arg-less helper" do
|
73
|
+
template = Curlybars.compile(<<-HBS)
|
74
|
+
{{foo (bar)}}!
|
75
|
+
HBS
|
76
|
+
|
77
|
+
expect(eval(template)).to resemble(<<-HTML)
|
78
|
+
LOLLOL!
|
79
|
+
HTML
|
80
|
+
end
|
81
|
+
|
82
|
+
context "with blog presenter" do
|
83
|
+
let(:presenter) do
|
84
|
+
IntegrationTest::BlogPresenter.new(
|
85
|
+
lambda { |*args, options|
|
86
|
+
val = args.first
|
87
|
+
"val is #{val}"
|
88
|
+
}
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "helper w args" do
|
93
|
+
template = Curlybars.compile(<<-HBS)
|
94
|
+
{{blog (equal a b)}}
|
95
|
+
HBS
|
96
|
+
|
97
|
+
expect(eval(template)).to resemble(<<-HTML)
|
98
|
+
val is true
|
99
|
+
HTML
|
100
|
+
end
|
101
|
+
|
102
|
+
it "supports much nesting" do
|
103
|
+
template = Curlybars.compile(<<-HBS)
|
104
|
+
{{blog (equal (equal true true) true)}}
|
105
|
+
HBS
|
106
|
+
|
107
|
+
expect(eval(template)).to resemble(<<-HTML)
|
108
|
+
val is true
|
109
|
+
HTML
|
110
|
+
end
|
111
|
+
|
112
|
+
it "with hashes" do
|
113
|
+
template = Curlybars.compile(<<-HBS)
|
114
|
+
{{blog (equal (equal true true) true fun='yes')}}
|
115
|
+
HBS
|
116
|
+
|
117
|
+
expect(eval(template)).to resemble(<<-HTML)
|
118
|
+
val is true
|
119
|
+
HTML
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "with a different blog presenter" do
|
124
|
+
let(:presenter) do
|
125
|
+
IntegrationTest::BlogPresenter.new(
|
126
|
+
lambda { |*args, options|
|
127
|
+
"val is #{options[:fun]}"
|
128
|
+
}
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "as hashes" do
|
133
|
+
template = Curlybars.compile(<<-HBS)
|
134
|
+
{{blog fun=(equal (blog fun=1) 'val is 1')}}
|
135
|
+
HBS
|
136
|
+
|
137
|
+
expect(eval(template)).to resemble(<<-HTML)
|
138
|
+
val is true
|
139
|
+
HTML
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "with yet another blog presenter" do
|
144
|
+
let(:presenter) do
|
145
|
+
IntegrationTest::BlogPresenter.new(
|
146
|
+
lambda { |*args, options|
|
147
|
+
first, second, third = args
|
148
|
+
"val is #{first}, #{second} and #{third}"
|
149
|
+
}
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "mixed paths and helpers" do
|
154
|
+
template = Curlybars.compile(<<-HBS)
|
155
|
+
{{blog baz.bat (equal a b) baz.bar}}
|
156
|
+
HBS
|
157
|
+
|
158
|
+
expect(eval(template)).to resemble(<<-HTML)
|
159
|
+
val is bat!, true and bar!
|
160
|
+
HTML
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "GH-800 : Complex subexpressions" do
|
165
|
+
let(:presenter) do
|
166
|
+
IntegrationTest::LetterPresenter.new(
|
167
|
+
a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' }
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "can handle complex subexpressions" do
|
172
|
+
inputs = [
|
173
|
+
"{{dash 'abc' (concat a b)}}",
|
174
|
+
"{{dash d (concat a b)}}",
|
175
|
+
"{{dash c.c (concat a b)}}",
|
176
|
+
"{{dash (concat a b) c.c}}",
|
177
|
+
"{{dash (concat a e.e) c.c}}"
|
178
|
+
]
|
179
|
+
|
180
|
+
expected_results = [
|
181
|
+
"abc-ab",
|
182
|
+
"d-ab",
|
183
|
+
"c-ab",
|
184
|
+
"ab-c",
|
185
|
+
"ae-c"
|
186
|
+
]
|
187
|
+
|
188
|
+
aggregate_failures do
|
189
|
+
inputs.each_with_index do |input, i|
|
190
|
+
expect(eval(Curlybars.compile(input))).to resemble(expected_results[i])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
it "multiple subexpressions in a hash" do
|
197
|
+
template = Curlybars.compile(<<-HBS)
|
198
|
+
{{input aria-label=(t "Name") placeholder=(t "Example User")}}
|
199
|
+
HBS
|
200
|
+
|
201
|
+
expected_output = '<input aria-label="Name" placeholder="Example User" />'
|
202
|
+
.gsub("<", "<")
|
203
|
+
.gsub(">", ">")
|
204
|
+
.gsub('"', """)
|
205
|
+
|
206
|
+
expect(eval(template)).to resemble(expected_output)
|
207
|
+
end
|
208
|
+
|
209
|
+
context "with item show presenter" do
|
210
|
+
let(:presenter) do
|
211
|
+
IntegrationTest::ItemShowPresenter.new(field: "Name", placeholder: "Example User")
|
212
|
+
end
|
213
|
+
|
214
|
+
it "multiple subexpressions in a hash with context" do
|
215
|
+
template = Curlybars.compile(<<-HBS)
|
216
|
+
{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}
|
217
|
+
HBS
|
218
|
+
|
219
|
+
expected_output = '<input aria-label="Name" placeholder="Example User" />'
|
220
|
+
.gsub("<", "<")
|
221
|
+
.gsub(">", ">")
|
222
|
+
.gsub('"', """)
|
223
|
+
|
224
|
+
expect(eval(template)).to resemble(expected_output)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#validate" do
|
230
|
+
before do
|
231
|
+
allow(Curlybars.configuration).to receive(:global_helpers_provider_classes).and_return([IntegrationTest::GlobalHelperProvider])
|
232
|
+
end
|
233
|
+
|
234
|
+
it "without errors when global helper" do
|
235
|
+
dependency_tree = {}
|
236
|
+
|
237
|
+
source = <<-HBS
|
238
|
+
{{#if (global_helper)}} ... {{/if}}
|
239
|
+
HBS
|
240
|
+
|
241
|
+
errors = Curlybars.validate(dependency_tree, source)
|
242
|
+
|
243
|
+
expect(errors).to be_empty
|
244
|
+
end
|
245
|
+
|
246
|
+
it "with errors when invoking a leaf" do
|
247
|
+
dependency_tree = { name: nil }
|
248
|
+
|
249
|
+
source = <<-HBS
|
250
|
+
{{#if (name)}} ... {{/if}}
|
251
|
+
HBS
|
252
|
+
|
253
|
+
errors = Curlybars.validate(dependency_tree, source)
|
254
|
+
|
255
|
+
expect(errors).not_to be_empty
|
256
|
+
end
|
257
|
+
|
258
|
+
it "without errors if argument is a leaf" do
|
259
|
+
dependency_tree = { helper: :helper, argument: nil }
|
260
|
+
|
261
|
+
source = <<-HBS
|
262
|
+
{{#if (helper argument)}} ... {{/if}}
|
263
|
+
HBS
|
264
|
+
|
265
|
+
errors = Curlybars.validate(dependency_tree, source)
|
266
|
+
|
267
|
+
expect(errors).to be_empty
|
268
|
+
end
|
269
|
+
|
270
|
+
it "without errors if argument is a literal" do
|
271
|
+
dependency_tree = { helper: :helper }
|
272
|
+
|
273
|
+
source = <<-HBS
|
274
|
+
{{#if (helper 'argument')}} ... {{/if}}
|
275
|
+
HBS
|
276
|
+
|
277
|
+
errors = Curlybars.validate(dependency_tree, source)
|
278
|
+
|
279
|
+
expect(errors).to be_empty
|
280
|
+
end
|
281
|
+
|
282
|
+
it "without errors if argument is a variable" do
|
283
|
+
dependency_tree = { helper: :helper }
|
284
|
+
|
285
|
+
source = <<-HBS
|
286
|
+
{{#if (helper @var)}} ... {{/if}}
|
287
|
+
HBS
|
288
|
+
|
289
|
+
errors = Curlybars.validate(dependency_tree, source)
|
290
|
+
|
291
|
+
expect(errors).to be_empty
|
292
|
+
end
|
293
|
+
|
294
|
+
it "without errors if argument is another subexpression" do
|
295
|
+
dependency_tree = { helper: :helper }
|
296
|
+
|
297
|
+
source = <<-HBS
|
298
|
+
{{#if (helper (helper option='argument'))}} ... {{/if}}
|
299
|
+
HBS
|
300
|
+
|
301
|
+
errors = Curlybars.validate(dependency_tree, source)
|
302
|
+
|
303
|
+
expect(errors).to be_empty
|
304
|
+
end
|
305
|
+
|
306
|
+
it "without errors if option is a leaf" do
|
307
|
+
dependency_tree = { helper: :helper, argument: nil }
|
308
|
+
|
309
|
+
source = <<-HBS
|
310
|
+
{{#if (helper option=argument)}} ... {{/if}}
|
311
|
+
HBS
|
312
|
+
|
313
|
+
errors = Curlybars.validate(dependency_tree, source)
|
314
|
+
|
315
|
+
expect(errors).to be_empty
|
316
|
+
end
|
317
|
+
|
318
|
+
it "without errors if option is a literal" do
|
319
|
+
dependency_tree = { helper: :helper }
|
320
|
+
|
321
|
+
source = <<-HBS
|
322
|
+
{{#if (helper option='argument')}} ... {{/if}}
|
323
|
+
HBS
|
324
|
+
|
325
|
+
errors = Curlybars.validate(dependency_tree, source)
|
326
|
+
|
327
|
+
expect(errors).to be_empty
|
328
|
+
end
|
329
|
+
|
330
|
+
it "without errors if option is a variable" do
|
331
|
+
dependency_tree = { helper: :helper }
|
332
|
+
|
333
|
+
source = <<-HBS
|
334
|
+
{{#if (helper option=@var)}} ... {{/if}}
|
335
|
+
HBS
|
336
|
+
|
337
|
+
errors = Curlybars.validate(dependency_tree, source)
|
338
|
+
|
339
|
+
expect(errors).to be_empty
|
340
|
+
end
|
341
|
+
|
342
|
+
it "without errors if option is another subexpression" do
|
343
|
+
dependency_tree = { helper: :helper }
|
344
|
+
|
345
|
+
source = <<-HBS
|
346
|
+
{{#if (helper option=(helper))}} ... {{/if}}
|
347
|
+
HBS
|
348
|
+
|
349
|
+
errors = Curlybars.validate(dependency_tree, source)
|
350
|
+
|
351
|
+
expect(errors).to be_empty
|
352
|
+
end
|
353
|
+
|
354
|
+
it "with errors when helper does not exist" do
|
355
|
+
dependency_tree = {}
|
356
|
+
|
357
|
+
source = <<-HBS
|
358
|
+
{{#if (helper)}} ... {{/if}}
|
359
|
+
HBS
|
360
|
+
|
361
|
+
errors = Curlybars.validate(dependency_tree, source)
|
362
|
+
|
363
|
+
expect(errors).not_to be_empty
|
364
|
+
end
|
365
|
+
|
366
|
+
it "with errors when invoking a leaf with arguments" do
|
367
|
+
dependency_tree = { name: nil }
|
368
|
+
|
369
|
+
source = <<-HBS
|
370
|
+
{{#if (name 'argument')}} ... {{/if}}
|
371
|
+
HBS
|
372
|
+
|
373
|
+
errors = Curlybars.validate(dependency_tree, source)
|
374
|
+
|
375
|
+
expect(errors).not_to be_empty
|
376
|
+
end
|
377
|
+
|
378
|
+
it "with errors when invoking a leaf with options" do
|
379
|
+
dependency_tree = { name: nil }
|
380
|
+
|
381
|
+
source = <<-HBS
|
382
|
+
{{#if (name option='value')}} ... {{/if}}
|
383
|
+
HBS
|
384
|
+
|
385
|
+
errors = Curlybars.validate(dependency_tree, source)
|
386
|
+
|
387
|
+
expect(errors).not_to be_empty
|
388
|
+
end
|
389
|
+
|
390
|
+
it "with errors if argument is not a value" do
|
391
|
+
dependency_tree = { helper: :helper }
|
392
|
+
|
393
|
+
source = <<-HBS
|
394
|
+
{{#if (helper not_a_value)}} ... {{/if}}
|
395
|
+
HBS
|
396
|
+
|
397
|
+
errors = Curlybars.validate(dependency_tree, source)
|
398
|
+
|
399
|
+
expect(errors).not_to be_empty
|
400
|
+
end
|
401
|
+
|
402
|
+
it "with errors if option is not a value" do
|
403
|
+
dependency_tree = { helper: :helper }
|
404
|
+
|
405
|
+
source = <<-HBS
|
406
|
+
{{#if (helper option=not_a_value)}} ... {{/if}}
|
407
|
+
HBS
|
408
|
+
|
409
|
+
errors = Curlybars.validate(dependency_tree, source)
|
410
|
+
|
411
|
+
expect(errors).not_to be_empty
|
412
|
+
end
|
413
|
+
|
414
|
+
it "without errors when invoking a helper with the result of a subexpression" do
|
415
|
+
dependency_tree = { join: :helper, uppercase: :helper, article: nil }
|
416
|
+
|
417
|
+
source = <<-HBS
|
418
|
+
{{join (uppercase article) attribute='title' separator='-'}}
|
419
|
+
HBS
|
420
|
+
|
421
|
+
errors = Curlybars.validate(dependency_tree, source)
|
422
|
+
|
423
|
+
expect(errors).to be_empty
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|