spinto-liquid 2.3.0.1

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.
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