spinto-liquid 2.3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/History.md +56 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +44 -0
  4. data/lib/extras/liquid_view.rb +51 -0
  5. data/lib/liquid/block.rb +101 -0
  6. data/lib/liquid/condition.rb +120 -0
  7. data/lib/liquid/context.rb +245 -0
  8. data/lib/liquid/document.rb +17 -0
  9. data/lib/liquid/drop.rb +49 -0
  10. data/lib/liquid/errors.rb +11 -0
  11. data/lib/liquid/extensions.rb +62 -0
  12. data/lib/liquid/file_system.rb +62 -0
  13. data/lib/liquid/htmltags.rb +75 -0
  14. data/lib/liquid/module_ex.rb +62 -0
  15. data/lib/liquid/standardfilters.rb +241 -0
  16. data/lib/liquid/strainer.rb +54 -0
  17. data/lib/liquid/tag.rb +26 -0
  18. data/lib/liquid/tags/assign.rb +33 -0
  19. data/lib/liquid/tags/capture.rb +35 -0
  20. data/lib/liquid/tags/case.rb +79 -0
  21. data/lib/liquid/tags/comment.rb +9 -0
  22. data/lib/liquid/tags/cycle.rb +59 -0
  23. data/lib/liquid/tags/decrement.rb +39 -0
  24. data/lib/liquid/tags/for.rb +190 -0
  25. data/lib/liquid/tags/if.rb +79 -0
  26. data/lib/liquid/tags/ifchanged.rb +20 -0
  27. data/lib/liquid/tags/include.rb +65 -0
  28. data/lib/liquid/tags/increment.rb +35 -0
  29. data/lib/liquid/tags/raw.rb +21 -0
  30. data/lib/liquid/tags/unless.rb +33 -0
  31. data/lib/liquid/template.rb +150 -0
  32. data/lib/liquid/variable.rb +50 -0
  33. data/lib/liquid.rb +66 -0
  34. data/test/liquid/assign_test.rb +21 -0
  35. data/test/liquid/block_test.rb +58 -0
  36. data/test/liquid/capture_test.rb +40 -0
  37. data/test/liquid/condition_test.rb +127 -0
  38. data/test/liquid/context_test.rb +478 -0
  39. data/test/liquid/drop_test.rb +162 -0
  40. data/test/liquid/error_handling_test.rb +81 -0
  41. data/test/liquid/file_system_test.rb +29 -0
  42. data/test/liquid/filter_test.rb +106 -0
  43. data/test/liquid/module_ex_test.rb +87 -0
  44. data/test/liquid/output_test.rb +116 -0
  45. data/test/liquid/parsing_quirks_test.rb +52 -0
  46. data/test/liquid/regexp_test.rb +44 -0
  47. data/test/liquid/security_test.rb +41 -0
  48. data/test/liquid/standard_filter_test.rb +195 -0
  49. data/test/liquid/strainer_test.rb +25 -0
  50. data/test/liquid/tags/for_tag_test.rb +215 -0
  51. data/test/liquid/tags/html_tag_test.rb +39 -0
  52. data/test/liquid/tags/if_else_tag_test.rb +160 -0
  53. data/test/liquid/tags/include_tag_test.rb +139 -0
  54. data/test/liquid/tags/increment_tag_test.rb +24 -0
  55. data/test/liquid/tags/raw_tag_test.rb +15 -0
  56. data/test/liquid/tags/standard_tag_test.rb +295 -0
  57. data/test/liquid/tags/statements_test.rb +134 -0
  58. data/test/liquid/tags/unless_else_tag_test.rb +26 -0
  59. data/test/liquid/template_test.rb +74 -0
  60. data/test/liquid/variable_test.rb +170 -0
  61. data/test/test_helper.rb +29 -0
  62. metadata +136 -0
data/lib/liquid.rb ADDED
@@ -0,0 +1,66 @@
1
+ # Copyright (c) 2005 Tobias Luetke
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ module Liquid
23
+ FilterSeparator = /\|/
24
+ ArgumentSeparator = ','
25
+ FilterArgumentSeparator = ':'
26
+ VariableAttributeSeparator = '.'
27
+ TagStart = /\{\%/
28
+ TagEnd = /\%\}/
29
+ VariableSignature = /\(?[\w\-\.\[\]]\)?/
30
+ VariableSegment = /[\w\-]/
31
+ VariableStart = /\{\{/
32
+ VariableEnd = /\}\}/
33
+ VariableIncompleteEnd = /\}\}?/
34
+ QuotedString = /"[^"]*"|'[^']*'/
35
+ QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/
36
+ StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s|:,]+/
37
+ FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
38
+ OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
39
+ SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
40
+ Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
41
+ TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
42
+ AnyStartingTag = /\{\{|\{\%/
43
+ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
44
+ TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/
45
+ VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/
46
+ end
47
+
48
+ require 'liquid/drop'
49
+ require 'liquid/extensions'
50
+ require 'liquid/errors'
51
+ require 'liquid/strainer'
52
+ require 'liquid/context'
53
+ require 'liquid/tag'
54
+ require 'liquid/block'
55
+ require 'liquid/document'
56
+ require 'liquid/variable'
57
+ require 'liquid/file_system'
58
+ require 'liquid/template'
59
+ require 'liquid/htmltags'
60
+ require 'liquid/standardfilters'
61
+ require 'liquid/condition'
62
+ require 'liquid/module_ex'
63
+
64
+ # Load all the tags of the standard library
65
+ #
66
+ Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class AssignTest < Test::Unit::TestCase
4
+ include Liquid
5
+
6
+ def test_assigned_variable
7
+ assert_template_result('.foo.',
8
+ '{% assign foo = values %}.{{ foo[0] }}.',
9
+ 'values' => %w{foo bar baz})
10
+
11
+ assert_template_result('.bar.',
12
+ '{% assign foo = values %}.{{ foo[1] }}.',
13
+ 'values' => %w{foo bar baz})
14
+ end
15
+
16
+ def test_assign_with_filter
17
+ assert_template_result('.bar.',
18
+ '{% assign foo = values | split: "," %}.{{ foo[1] }}.',
19
+ 'values' => "foo,bar,baz")
20
+ end
21
+ end # AssignTest
@@ -0,0 +1,58 @@
1
+ require 'test_helper'
2
+
3
+ class VariableTest < Test::Unit::TestCase
4
+ include Liquid
5
+
6
+ def test_blankspace
7
+ template = Liquid::Template.parse(" ")
8
+ assert_equal [" "], template.root.nodelist
9
+ end
10
+
11
+ def test_variable_beginning
12
+ template = Liquid::Template.parse("{{funk}} ")
13
+ assert_equal 2, template.root.nodelist.size
14
+ assert_equal Variable, template.root.nodelist[0].class
15
+ assert_equal String, template.root.nodelist[1].class
16
+ end
17
+
18
+ def test_variable_end
19
+ template = Liquid::Template.parse(" {{funk}}")
20
+ assert_equal 2, template.root.nodelist.size
21
+ assert_equal String, template.root.nodelist[0].class
22
+ assert_equal Variable, template.root.nodelist[1].class
23
+ end
24
+
25
+ def test_variable_middle
26
+ template = Liquid::Template.parse(" {{funk}} ")
27
+ assert_equal 3, template.root.nodelist.size
28
+ assert_equal String, template.root.nodelist[0].class
29
+ assert_equal Variable, template.root.nodelist[1].class
30
+ assert_equal String, template.root.nodelist[2].class
31
+ end
32
+
33
+ def test_variable_many_embedded_fragments
34
+ template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
35
+ assert_equal 7, template.root.nodelist.size
36
+ assert_equal [String, Variable, String, Variable, String, Variable, String],
37
+ block_types(template.root.nodelist)
38
+ end
39
+
40
+ def test_with_block
41
+ template = Liquid::Template.parse(" {% comment %} {% endcomment %} ")
42
+ assert_equal [String, Comment, String], block_types(template.root.nodelist)
43
+ assert_equal 3, template.root.nodelist.size
44
+ end
45
+
46
+ def test_with_custom_tag
47
+ Liquid::Template.register_tag("testtag", Block)
48
+
49
+ assert_nothing_thrown do
50
+ template = Liquid::Template.parse( "{% testtag %} {% endtesttag %}")
51
+ end
52
+ end
53
+
54
+ private
55
+ def block_types(nodelist)
56
+ nodelist.collect { |node| node.class }
57
+ end
58
+ end # VariableTest
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class CaptureTest < Test::Unit::TestCase
4
+ include Liquid
5
+
6
+ def test_captures_block_content_in_variable
7
+ assert_template_result("test string", "{% capture 'var' %}test string{% endcapture %}{{var}}", {})
8
+ end
9
+
10
+ def test_capture_to_variable_from_outer_scope_if_existing
11
+ template_source = <<-END_TEMPLATE
12
+ {% assign var = '' %}
13
+ {% if true %}
14
+ {% capture var %}first-block-string{% endcapture %}
15
+ {% endif %}
16
+ {% if true %}
17
+ {% capture var %}test-string{% endcapture %}
18
+ {% endif %}
19
+ {{var}}
20
+ END_TEMPLATE
21
+ template = Template.parse(template_source)
22
+ rendered = template.render
23
+ assert_equal "test-string", rendered.gsub(/\s/, '')
24
+ end
25
+
26
+ def test_assigning_from_capture
27
+ template_source = <<-END_TEMPLATE
28
+ {% assign first = '' %}
29
+ {% assign second = '' %}
30
+ {% for number in (1..3) %}
31
+ {% capture first %}{{number}}{% endcapture %}
32
+ {% assign second = first %}
33
+ {% endfor %}
34
+ {{ first }}-{{ second }}
35
+ END_TEMPLATE
36
+ template = Template.parse(template_source)
37
+ rendered = template.render
38
+ assert_equal "3-3", rendered.gsub(/\s/, '')
39
+ end
40
+ end # CaptureTest
@@ -0,0 +1,127 @@
1
+ require 'test_helper'
2
+
3
+ class ConditionTest < Test::Unit::TestCase
4
+ include Liquid
5
+
6
+ def test_basic_condition
7
+ assert_equal false, Condition.new('1', '==', '2').evaluate
8
+ assert_equal true, Condition.new('1', '==', '1').evaluate
9
+ end
10
+
11
+ def test_default_operators_evalute_true
12
+ assert_evalutes_true '1', '==', '1'
13
+ assert_evalutes_true '1', '!=', '2'
14
+ assert_evalutes_true '1', '<>', '2'
15
+ assert_evalutes_true '1', '<', '2'
16
+ assert_evalutes_true '2', '>', '1'
17
+ assert_evalutes_true '1', '>=', '1'
18
+ assert_evalutes_true '2', '>=', '1'
19
+ assert_evalutes_true '1', '<=', '2'
20
+ assert_evalutes_true '1', '<=', '1'
21
+ # negative numbers
22
+ assert_evalutes_true '1', '>', '-1'
23
+ assert_evalutes_true '-1', '<', '1'
24
+ assert_evalutes_true '1.0', '>', '-1.0'
25
+ assert_evalutes_true '-1.0', '<', '1.0'
26
+ end
27
+
28
+ def test_default_operators_evalute_false
29
+ assert_evalutes_false '1', '==', '2'
30
+ assert_evalutes_false '1', '!=', '1'
31
+ assert_evalutes_false '1', '<>', '1'
32
+ assert_evalutes_false '1', '<', '0'
33
+ assert_evalutes_false '2', '>', '4'
34
+ assert_evalutes_false '1', '>=', '3'
35
+ assert_evalutes_false '2', '>=', '4'
36
+ assert_evalutes_false '1', '<=', '0'
37
+ assert_evalutes_false '1', '<=', '0'
38
+ end
39
+
40
+ def test_contains_works_on_strings
41
+ assert_evalutes_true "'bob'", 'contains', "'o'"
42
+ assert_evalutes_true "'bob'", 'contains', "'b'"
43
+ assert_evalutes_true "'bob'", 'contains', "'bo'"
44
+ assert_evalutes_true "'bob'", 'contains', "'ob'"
45
+ assert_evalutes_true "'bob'", 'contains', "'bob'"
46
+
47
+ assert_evalutes_false "'bob'", 'contains', "'bob2'"
48
+ assert_evalutes_false "'bob'", 'contains', "'a'"
49
+ assert_evalutes_false "'bob'", 'contains', "'---'"
50
+ end
51
+
52
+ def test_contains_works_on_arrays
53
+ @context = Liquid::Context.new
54
+ @context['array'] = [1,2,3,4,5]
55
+
56
+ assert_evalutes_false "array", 'contains', '0'
57
+ assert_evalutes_true "array", 'contains', '1'
58
+ assert_evalutes_true "array", 'contains', '2'
59
+ assert_evalutes_true "array", 'contains', '3'
60
+ assert_evalutes_true "array", 'contains', '4'
61
+ assert_evalutes_true "array", 'contains', '5'
62
+ assert_evalutes_false "array", 'contains', '6'
63
+ assert_evalutes_false "array", 'contains', '"1"'
64
+ end
65
+
66
+ def test_contains_returns_false_for_nil_operands
67
+ @context = Liquid::Context.new
68
+ assert_evalutes_false "not_assigned", 'contains', '0'
69
+ assert_evalutes_false "0", 'contains', 'not_assigned'
70
+ end
71
+
72
+ def test_or_condition
73
+ condition = Condition.new('1', '==', '2')
74
+
75
+ assert_equal false, condition.evaluate
76
+
77
+ condition.or Condition.new('2', '==', '1')
78
+
79
+ assert_equal false, condition.evaluate
80
+
81
+ condition.or Condition.new('1', '==', '1')
82
+
83
+ assert_equal true, condition.evaluate
84
+ end
85
+
86
+ def test_and_condition
87
+ condition = Condition.new('1', '==', '1')
88
+
89
+ assert_equal true, condition.evaluate
90
+
91
+ condition.and Condition.new('2', '==', '2')
92
+
93
+ assert_equal true, condition.evaluate
94
+
95
+ condition.and Condition.new('2', '==', '1')
96
+
97
+ assert_equal false, condition.evaluate
98
+ end
99
+
100
+ def test_should_allow_custom_proc_operator
101
+ Condition.operators['starts_with'] = Proc.new { |cond, left, right| left =~ %r{^#{right}} }
102
+
103
+ assert_evalutes_true "'bob'", 'starts_with', "'b'"
104
+ assert_evalutes_false "'bob'", 'starts_with', "'o'"
105
+
106
+ ensure
107
+ Condition.operators.delete 'starts_with'
108
+ end
109
+
110
+ def test_left_or_right_may_contain_operators
111
+ @context = Liquid::Context.new
112
+ @context['one'] = @context['another'] = "gnomeslab-and-or-liquid"
113
+
114
+ assert_evalutes_true "one", '==', "another"
115
+ end
116
+
117
+ private
118
+ def assert_evalutes_true(left, op, right)
119
+ assert Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
120
+ "Evaluated false: #{left} #{op} #{right}"
121
+ end
122
+
123
+ def assert_evalutes_false(left, op, right)
124
+ assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
125
+ "Evaluated true: #{left} #{op} #{right}"
126
+ end
127
+ end # ConditionTest
@@ -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