curlybars 1.3.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/curlybars.rb +0 -1
  3. data/lib/curlybars/configuration.rb +1 -9
  4. data/lib/curlybars/error/base.rb +2 -0
  5. data/lib/curlybars/lexer.rb +3 -0
  6. data/lib/curlybars/method_whitelist.rb +16 -11
  7. data/lib/curlybars/node/block_helper_else.rb +1 -0
  8. data/lib/curlybars/node/if_else.rb +1 -1
  9. data/lib/curlybars/node/path.rb +6 -0
  10. data/lib/curlybars/node/sub_expression.rb +62 -0
  11. data/lib/curlybars/node/unless_else.rb +1 -1
  12. data/lib/curlybars/parser.rb +11 -0
  13. data/lib/curlybars/processor/tilde.rb +3 -0
  14. data/lib/curlybars/rendering_support.rb +9 -1
  15. data/lib/curlybars/template_handler.rb +18 -6
  16. data/lib/curlybars/version.rb +1 -1
  17. data/lib/curlybars/visitor.rb +6 -0
  18. data/spec/acceptance/application_layout_spec.rb +2 -2
  19. data/spec/acceptance/collection_blocks_spec.rb +1 -1
  20. data/spec/acceptance/global_helper_spec.rb +1 -1
  21. data/spec/curlybars/lexer_spec.rb +25 -2
  22. data/spec/curlybars/method_whitelist_spec.rb +15 -0
  23. data/spec/curlybars/rendering_support_spec.rb +4 -9
  24. data/spec/curlybars/template_handler_spec.rb +33 -30
  25. data/spec/integration/cache_spec.rb +20 -18
  26. data/spec/integration/node/block_helper_else_spec.rb +0 -2
  27. data/spec/integration/node/each_else_spec.rb +0 -2
  28. data/spec/integration/node/each_spec.rb +0 -2
  29. data/spec/integration/node/helper_spec.rb +12 -2
  30. data/spec/integration/node/if_else_spec.rb +0 -2
  31. data/spec/integration/node/if_spec.rb +2 -4
  32. data/spec/integration/node/output_spec.rb +0 -2
  33. data/spec/integration/node/partial_spec.rb +0 -2
  34. data/spec/integration/node/path_spec.rb +0 -2
  35. data/spec/integration/node/root_spec.rb +0 -2
  36. data/spec/integration/node/sub_expression_spec.rb +426 -0
  37. data/spec/integration/node/template_spec.rb +0 -2
  38. data/spec/integration/node/unless_else_spec.rb +2 -4
  39. data/spec/integration/node/unless_spec.rb +0 -2
  40. data/spec/integration/node/with_spec.rb +0 -2
  41. data/spec/integration/processors_spec.rb +0 -1
  42. data/spec/integration/visitor_spec.rb +13 -5
  43. metadata +47 -15
@@ -25,8 +25,6 @@ describe "{{> partial}}" do
25
25
  end
26
26
 
27
27
  describe "#validate" do
28
- let(:presenter_class) { double(:presenter_class) }
29
-
30
28
  it "validates the path with errors" do
31
29
  dependency_tree = {}
32
30
 
@@ -123,8 +123,6 @@ describe "{{path}}" do
123
123
  end
124
124
 
125
125
  describe "#validate" do
126
- let(:presenter_class) { double(:presenter_class) }
127
-
128
126
  it "without errors" do
129
127
  dependency_tree = { sub_context: {}, outer_field: nil }
130
128
 
@@ -1,7 +1,5 @@
1
1
  describe "root" do
2
2
  describe "#validate" do
3
- let(:presenter_class) { double(:presenter_class) }
4
-
5
3
  it "without errors if template is empty" do
6
4
  dependency_tree = {}
7
5
 
@@ -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("<", "&lt;")
203
+ .gsub(">", "&gt;")
204
+ .gsub('"', "&quot;")
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("<", "&lt;")
221
+ .gsub(">", "&gt;")
222
+ .gsub('"', "&quot;")
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