liquid 2.2.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/{History.txt → History.md} +26 -23
  2. data/README.md +18 -16
  3. data/lib/liquid.rb +1 -4
  4. data/lib/liquid/block.rb +2 -2
  5. data/lib/liquid/context.rb +29 -33
  6. data/lib/liquid/drop.rb +11 -13
  7. data/lib/liquid/extensions.rb +13 -7
  8. data/lib/liquid/file_system.rb +2 -2
  9. data/lib/liquid/htmltags.rb +5 -4
  10. data/lib/liquid/standardfilters.rb +12 -1
  11. data/lib/liquid/strainer.rb +1 -1
  12. data/lib/liquid/tags/capture.rb +1 -1
  13. data/lib/liquid/tags/case.rb +4 -8
  14. data/lib/liquid/tags/decrement.rb +39 -0
  15. data/lib/liquid/tags/for.rb +20 -5
  16. data/lib/liquid/tags/include.rb +21 -12
  17. data/lib/liquid/tags/increment.rb +35 -0
  18. data/lib/liquid/tags/raw.rb +21 -0
  19. data/lib/liquid/tags/unless.rb +5 -5
  20. data/lib/liquid/template.rb +3 -2
  21. data/lib/liquid/variable.rb +1 -1
  22. data/test/liquid/assign_test.rb +15 -0
  23. data/test/liquid/block_test.rb +58 -0
  24. data/test/liquid/capture_test.rb +40 -0
  25. data/test/liquid/condition_test.rb +122 -0
  26. data/test/liquid/context_test.rb +478 -0
  27. data/test/liquid/drop_test.rb +162 -0
  28. data/test/liquid/error_handling_test.rb +81 -0
  29. data/test/liquid/file_system_test.rb +29 -0
  30. data/test/liquid/filter_test.rb +106 -0
  31. data/test/liquid/module_ex_test.rb +87 -0
  32. data/test/liquid/output_test.rb +116 -0
  33. data/test/liquid/parsing_quirks_test.rb +52 -0
  34. data/test/liquid/regexp_test.rb +44 -0
  35. data/test/liquid/security_test.rb +41 -0
  36. data/test/liquid/standard_filter_test.rb +191 -0
  37. data/test/liquid/strainer_test.rb +25 -0
  38. data/test/liquid/tags/html_tag_test.rb +29 -0
  39. data/test/liquid/tags/if_else_tag_test.rb +160 -0
  40. data/test/liquid/tags/include_tag_test.rb +139 -0
  41. data/test/liquid/tags/increment_tag_test.rb +24 -0
  42. data/test/liquid/tags/raw_tag_test.rb +15 -0
  43. data/test/liquid/tags/standard_tag_test.rb +461 -0
  44. data/test/liquid/tags/statements_test.rb +134 -0
  45. data/test/liquid/tags/unless_else_tag_test.rb +26 -0
  46. data/test/liquid/template_test.rb +74 -0
  47. data/test/liquid/variable_test.rb +170 -0
  48. data/test/test_helper.rb +29 -0
  49. metadata +67 -16
  50. data/Manifest.txt +0 -34
  51. data/lib/liquid/tags/literal.rb +0 -42
@@ -0,0 +1,478 @@
1
+ require 'test_helper'
2
+
3
+ class HundredCentes
4
+ def to_liquid
5
+ 100
6
+ end
7
+ end
8
+
9
+ class CentsDrop < Liquid::Drop
10
+ def amount
11
+ HundredCentes.new
12
+ end
13
+
14
+ def non_zero?
15
+ true
16
+ end
17
+ end
18
+
19
+ class ContextSensitiveDrop < Liquid::Drop
20
+ def test
21
+ @context['test']
22
+ end
23
+ end
24
+
25
+ class Category < Liquid::Drop
26
+ attr_accessor :name
27
+
28
+ def initialize(name)
29
+ @name = name
30
+ end
31
+
32
+ def to_liquid
33
+ CategoryDrop.new(self)
34
+ end
35
+ end
36
+
37
+ class CategoryDrop
38
+ attr_accessor :category, :context
39
+ def initialize(category)
40
+ @category = category
41
+ end
42
+ end
43
+
44
+ class CounterDrop < Liquid::Drop
45
+ def count
46
+ @count ||= 0
47
+ @count += 1
48
+ end
49
+ end
50
+
51
+ class ArrayLike
52
+ def fetch(index)
53
+ end
54
+
55
+ def [](index)
56
+ @counts ||= []
57
+ @counts[index] ||= 0
58
+ @counts[index] += 1
59
+ end
60
+
61
+ def to_liquid
62
+ self
63
+ end
64
+ end
65
+
66
+ class ContextTest < Test::Unit::TestCase
67
+ include Liquid
68
+
69
+ def setup
70
+ @context = Liquid::Context.new
71
+ end
72
+
73
+ def test_variables
74
+ @context['string'] = 'string'
75
+ assert_equal 'string', @context['string']
76
+
77
+ @context['num'] = 5
78
+ assert_equal 5, @context['num']
79
+
80
+ @context['time'] = Time.parse('2006-06-06 12:00:00')
81
+ assert_equal Time.parse('2006-06-06 12:00:00'), @context['time']
82
+
83
+ @context['date'] = Date.today
84
+ assert_equal Date.today, @context['date']
85
+
86
+ now = DateTime.now
87
+ @context['datetime'] = now
88
+ assert_equal now, @context['datetime']
89
+
90
+ @context['bool'] = true
91
+ assert_equal true, @context['bool']
92
+
93
+ @context['bool'] = false
94
+ assert_equal false, @context['bool']
95
+
96
+ @context['nil'] = nil
97
+ assert_equal nil, @context['nil']
98
+ assert_equal nil, @context['nil']
99
+ end
100
+
101
+ def test_variables_not_existing
102
+ assert_equal nil, @context['does_not_exist']
103
+ end
104
+
105
+ def test_scoping
106
+ assert_nothing_raised do
107
+ @context.push
108
+ @context.pop
109
+ end
110
+
111
+ assert_raise(Liquid::ContextError) do
112
+ @context.pop
113
+ end
114
+
115
+ assert_raise(Liquid::ContextError) do
116
+ @context.push
117
+ @context.pop
118
+ @context.pop
119
+ end
120
+ end
121
+
122
+ def test_length_query
123
+
124
+ @context['numbers'] = [1,2,3,4]
125
+
126
+ assert_equal 4, @context['numbers.size']
127
+
128
+ @context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4}
129
+
130
+ assert_equal 4, @context['numbers.size']
131
+
132
+ @context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4, 'size' => 1000}
133
+
134
+ assert_equal 1000, @context['numbers.size']
135
+
136
+ end
137
+
138
+ def test_hyphenated_variable
139
+
140
+ @context['oh-my'] = 'godz'
141
+ assert_equal 'godz', @context['oh-my']
142
+
143
+ end
144
+
145
+ def test_add_filter
146
+
147
+ filter = Module.new do
148
+ def hi(output)
149
+ output + ' hi!'
150
+ end
151
+ end
152
+
153
+ context = Context.new
154
+ context.add_filters(filter)
155
+ assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
156
+
157
+ context = Context.new
158
+ assert_equal 'hi?', context.invoke(:hi, 'hi?')
159
+
160
+ context.add_filters(filter)
161
+ assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
162
+
163
+ end
164
+
165
+ def test_override_global_filter
166
+ global = Module.new do
167
+ def notice(output)
168
+ "Global #{output}"
169
+ end
170
+ end
171
+
172
+ local = Module.new do
173
+ def notice(output)
174
+ "Local #{output}"
175
+ end
176
+ end
177
+
178
+ Template.register_filter(global)
179
+ assert_equal 'Global test', Template.parse("{{'test' | notice }}").render
180
+ assert_equal 'Local test', Template.parse("{{'test' | notice }}").render({}, :filters => [local])
181
+ end
182
+
183
+ def test_only_intended_filters_make_it_there
184
+
185
+ filter = Module.new do
186
+ def hi(output)
187
+ output + ' hi!'
188
+ end
189
+ end
190
+
191
+ context = Context.new
192
+ methods_before = context.strainer.methods.map { |method| method.to_s }
193
+ context.add_filters(filter)
194
+ methods_after = context.strainer.methods.map { |method| method.to_s }
195
+ assert_equal (methods_before + ["hi"]).sort, methods_after.sort
196
+ end
197
+
198
+ def test_add_item_in_outer_scope
199
+ @context['test'] = 'test'
200
+ @context.push
201
+ assert_equal 'test', @context['test']
202
+ @context.pop
203
+ assert_equal 'test', @context['test']
204
+ end
205
+
206
+ def test_add_item_in_inner_scope
207
+ @context.push
208
+ @context['test'] = 'test'
209
+ assert_equal 'test', @context['test']
210
+ @context.pop
211
+ assert_equal nil, @context['test']
212
+ end
213
+
214
+ def test_hierachical_data
215
+ @context['hash'] = {"name" => 'tobi'}
216
+ assert_equal 'tobi', @context['hash.name']
217
+ assert_equal 'tobi', @context['hash["name"]']
218
+ end
219
+
220
+ def test_keywords
221
+ assert_equal true, @context['true']
222
+ assert_equal false, @context['false']
223
+ end
224
+
225
+ def test_digits
226
+ assert_equal 100, @context['100']
227
+ assert_equal 100.00, @context['100.00']
228
+ end
229
+
230
+ def test_strings
231
+ assert_equal "hello!", @context['"hello!"']
232
+ assert_equal "hello!", @context["'hello!'"]
233
+ end
234
+
235
+ def test_merge
236
+ @context.merge({ "test" => "test" })
237
+ assert_equal 'test', @context['test']
238
+ @context.merge({ "test" => "newvalue", "foo" => "bar" })
239
+ assert_equal 'newvalue', @context['test']
240
+ assert_equal 'bar', @context['foo']
241
+ end
242
+
243
+ def test_array_notation
244
+ @context['test'] = [1,2,3,4,5]
245
+
246
+ assert_equal 1, @context['test[0]']
247
+ assert_equal 2, @context['test[1]']
248
+ assert_equal 3, @context['test[2]']
249
+ assert_equal 4, @context['test[3]']
250
+ assert_equal 5, @context['test[4]']
251
+ end
252
+
253
+ def test_recoursive_array_notation
254
+ @context['test'] = {'test' => [1,2,3,4,5]}
255
+
256
+ assert_equal 1, @context['test.test[0]']
257
+
258
+ @context['test'] = [{'test' => 'worked'}]
259
+
260
+ assert_equal 'worked', @context['test[0].test']
261
+ end
262
+
263
+ def test_hash_to_array_transition
264
+ @context['colors'] = {
265
+ 'Blue' => ['003366','336699', '6699CC', '99CCFF'],
266
+ 'Green' => ['003300','336633', '669966', '99CC99'],
267
+ 'Yellow' => ['CC9900','FFCC00', 'FFFF99', 'FFFFCC'],
268
+ 'Red' => ['660000','993333', 'CC6666', 'FF9999']
269
+ }
270
+
271
+ assert_equal '003366', @context['colors.Blue[0]']
272
+ assert_equal 'FF9999', @context['colors.Red[3]']
273
+ end
274
+
275
+ def test_try_first
276
+ @context['test'] = [1,2,3,4,5]
277
+
278
+ assert_equal 1, @context['test.first']
279
+ assert_equal 5, @context['test.last']
280
+
281
+ @context['test'] = {'test' => [1,2,3,4,5]}
282
+
283
+ assert_equal 1, @context['test.test.first']
284
+ assert_equal 5, @context['test.test.last']
285
+
286
+ @context['test'] = [1]
287
+ assert_equal 1, @context['test.first']
288
+ assert_equal 1, @context['test.last']
289
+ end
290
+
291
+ def test_access_hashes_with_hash_notation
292
+ @context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
293
+ @context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]}
294
+
295
+ assert_equal 5, @context['products["count"]']
296
+ assert_equal 'deepsnow', @context['products["tags"][0]']
297
+ assert_equal 'deepsnow', @context['products["tags"].first']
298
+ assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
299
+ assert_equal 'element151cm', @context['product["variants"][1]["title"]']
300
+ assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
301
+ assert_equal 'element151cm', @context['product["variants"].last["title"]']
302
+ end
303
+
304
+ def test_access_variable_with_hash_notation
305
+ @context['foo'] = 'baz'
306
+ @context['bar'] = 'foo'
307
+
308
+ assert_equal 'baz', @context['["foo"]']
309
+ assert_equal 'baz', @context['[bar]']
310
+ end
311
+
312
+ def test_access_hashes_with_hash_access_variables
313
+
314
+ @context['var'] = 'tags'
315
+ @context['nested'] = {'var' => 'tags'}
316
+ @context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
317
+
318
+ assert_equal 'deepsnow', @context['products[var].first']
319
+ assert_equal 'freestyle', @context['products[nested.var].last']
320
+ end
321
+
322
+ def test_hash_notation_only_for_hash_access
323
+ @context['array'] = [1,2,3,4,5]
324
+ @context['hash'] = {'first' => 'Hello'}
325
+
326
+ assert_equal 1, @context['array.first']
327
+ assert_equal nil, @context['array["first"]']
328
+ assert_equal 'Hello', @context['hash["first"]']
329
+ end
330
+
331
+ def test_first_can_appear_in_middle_of_callchain
332
+
333
+ @context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]}
334
+
335
+ assert_equal 'draft151cm', @context['product.variants[0].title']
336
+ assert_equal 'element151cm', @context['product.variants[1].title']
337
+ assert_equal 'draft151cm', @context['product.variants.first.title']
338
+ assert_equal 'element151cm', @context['product.variants.last.title']
339
+
340
+ end
341
+
342
+ def test_cents
343
+ @context.merge( "cents" => HundredCentes.new )
344
+ assert_equal 100, @context['cents']
345
+ end
346
+
347
+ def test_nested_cents
348
+ @context.merge( "cents" => { 'amount' => HundredCentes.new} )
349
+ assert_equal 100, @context['cents.amount']
350
+
351
+ @context.merge( "cents" => { 'cents' => { 'amount' => HundredCentes.new} } )
352
+ assert_equal 100, @context['cents.cents.amount']
353
+ end
354
+
355
+ def test_cents_through_drop
356
+ @context.merge( "cents" => CentsDrop.new )
357
+ assert_equal 100, @context['cents.amount']
358
+ end
359
+
360
+ def test_nested_cents_through_drop
361
+ @context.merge( "vars" => {"cents" => CentsDrop.new} )
362
+ assert_equal 100, @context['vars.cents.amount']
363
+ end
364
+
365
+ def test_drop_methods_with_question_marks
366
+ @context.merge( "cents" => CentsDrop.new )
367
+ assert @context['cents.non_zero?']
368
+ end
369
+
370
+ def test_context_from_within_drop
371
+ @context.merge( "test" => '123', "vars" => ContextSensitiveDrop.new )
372
+ assert_equal '123', @context['vars.test']
373
+ end
374
+
375
+ def test_nested_context_from_within_drop
376
+ @context.merge( "test" => '123', "vars" => {"local" => ContextSensitiveDrop.new } )
377
+ assert_equal '123', @context['vars.local.test']
378
+ end
379
+
380
+ def test_ranges
381
+ @context.merge( "test" => '5' )
382
+ assert_equal (1..5), @context['(1..5)']
383
+ assert_equal (1..5), @context['(1..test)']
384
+ assert_equal (5..5), @context['(test..test)']
385
+ end
386
+
387
+ def test_cents_through_drop_nestedly
388
+ @context.merge( "cents" => {"cents" => CentsDrop.new} )
389
+ assert_equal 100, @context['cents.cents.amount']
390
+
391
+ @context.merge( "cents" => { "cents" => {"cents" => CentsDrop.new}} )
392
+ assert_equal 100, @context['cents.cents.cents.amount']
393
+ end
394
+
395
+ def test_drop_with_variable_called_only_once
396
+ @context['counter'] = CounterDrop.new
397
+
398
+ assert_equal 1, @context['counter.count']
399
+ assert_equal 2, @context['counter.count']
400
+ assert_equal 3, @context['counter.count']
401
+ end
402
+
403
+ def test_drop_with_key_called_only_once
404
+ @context['counter'] = CounterDrop.new
405
+
406
+ assert_equal 1, @context['counter["count"]']
407
+ assert_equal 2, @context['counter["count"]']
408
+ assert_equal 3, @context['counter["count"]']
409
+ end
410
+
411
+ def test_proc_as_variable
412
+ @context['dynamic'] = Proc.new { 'Hello' }
413
+
414
+ assert_equal 'Hello', @context['dynamic']
415
+ end
416
+
417
+ def test_lambda_as_variable
418
+ @context['dynamic'] = proc { 'Hello' }
419
+
420
+ assert_equal 'Hello', @context['dynamic']
421
+ end
422
+
423
+ def test_nested_lambda_as_variable
424
+ @context['dynamic'] = { "lambda" => proc { 'Hello' } }
425
+
426
+ assert_equal 'Hello', @context['dynamic.lambda']
427
+ end
428
+
429
+ def test_array_containing_lambda_as_variable
430
+ @context['dynamic'] = [1,2, proc { 'Hello' } ,4,5]
431
+
432
+ assert_equal 'Hello', @context['dynamic[2]']
433
+ end
434
+
435
+ def test_lambda_is_called_once
436
+ @context['callcount'] = proc { @global ||= 0; @global += 1; @global.to_s }
437
+
438
+ assert_equal '1', @context['callcount']
439
+ assert_equal '1', @context['callcount']
440
+ assert_equal '1', @context['callcount']
441
+
442
+ @global = nil
443
+ end
444
+
445
+ def test_nested_lambda_is_called_once
446
+ @context['callcount'] = { "lambda" => proc { @global ||= 0; @global += 1; @global.to_s } }
447
+
448
+ assert_equal '1', @context['callcount.lambda']
449
+ assert_equal '1', @context['callcount.lambda']
450
+ assert_equal '1', @context['callcount.lambda']
451
+
452
+ @global = nil
453
+ end
454
+
455
+ def test_lambda_in_array_is_called_once
456
+ @context['callcount'] = [1,2, proc { @global ||= 0; @global += 1; @global.to_s } ,4,5]
457
+
458
+ assert_equal '1', @context['callcount[2]']
459
+ assert_equal '1', @context['callcount[2]']
460
+ assert_equal '1', @context['callcount[2]']
461
+
462
+ @global = nil
463
+ end
464
+
465
+ def test_access_to_context_from_proc
466
+ @context.registers[:magic] = 345392
467
+
468
+ @context['magic'] = proc { @context.registers[:magic] }
469
+
470
+ assert_equal 345392, @context['magic']
471
+ end
472
+
473
+ def test_to_liquid_and_context_at_first_level
474
+ @context['category'] = Category.new("foobar")
475
+ assert_kind_of CategoryDrop, @context['category']
476
+ assert_equal @context, @context['category'].context
477
+ end
478
+ end # ContextTest