liquor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/.travis.yml +13 -0
  2. data/AUTHORS +2 -0
  3. data/CHANGELOG +46 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +91 -0
  6. data/History.txt +44 -0
  7. data/LICENSE +22 -0
  8. data/MIT-LICENSE +20 -0
  9. data/README.md +148 -0
  10. data/README.rdoc +114 -0
  11. data/Rakefile +33 -0
  12. data/example/server/example_servlet.rb +37 -0
  13. data/example/server/liquid_servlet.rb +28 -0
  14. data/example/server/liquor_servlet.rb +28 -0
  15. data/example/server/server.rb +12 -0
  16. data/example/server/templates/index.liquid +6 -0
  17. data/example/server/templates/index.liquor +6 -0
  18. data/example/server/templates/products.liquid +45 -0
  19. data/example/server/templates/products.liquor +45 -0
  20. data/init.rb +8 -0
  21. data/lib/extras/liquid_view.rb +51 -0
  22. data/lib/extras/liquor_view.rb +51 -0
  23. data/lib/liquor.rb +72 -0
  24. data/lib/liquor/block.rb +101 -0
  25. data/lib/liquor/condition.rb +120 -0
  26. data/lib/liquor/context.rb +294 -0
  27. data/lib/liquor/document.rb +17 -0
  28. data/lib/liquor/drop.rb +256 -0
  29. data/lib/liquor/errors.rb +11 -0
  30. data/lib/liquor/extensions.rb +72 -0
  31. data/lib/liquor/file_system.rb +62 -0
  32. data/lib/liquor/htmltags.rb +74 -0
  33. data/lib/liquor/module_ex.rb +60 -0
  34. data/lib/liquor/standardfilters.rb +315 -0
  35. data/lib/liquor/strainer.rb +58 -0
  36. data/lib/liquor/tag.rb +26 -0
  37. data/lib/liquor/tags/assign.rb +33 -0
  38. data/lib/liquor/tags/capture.rb +35 -0
  39. data/lib/liquor/tags/case.rb +83 -0
  40. data/lib/liquor/tags/comment.rb +9 -0
  41. data/lib/liquor/tags/content_for.rb +54 -0
  42. data/lib/liquor/tags/cycle.rb +59 -0
  43. data/lib/liquor/tags/for.rb +136 -0
  44. data/lib/liquor/tags/if.rb +80 -0
  45. data/lib/liquor/tags/ifchanged.rb +20 -0
  46. data/lib/liquor/tags/include.rb +56 -0
  47. data/lib/liquor/tags/unless.rb +33 -0
  48. data/lib/liquor/tags/yield.rb +49 -0
  49. data/lib/liquor/template.rb +181 -0
  50. data/lib/liquor/variable.rb +52 -0
  51. data/lib/liquor/version.rb +3 -0
  52. data/liquor.gemspec +19 -0
  53. data/performance/shopify.rb +92 -0
  54. data/performance/shopify/comment_form.rb +33 -0
  55. data/performance/shopify/database.rb +45 -0
  56. data/performance/shopify/json_filter.rb +7 -0
  57. data/performance/shopify/liquid.rb +18 -0
  58. data/performance/shopify/liquor.rb +18 -0
  59. data/performance/shopify/money_filter.rb +18 -0
  60. data/performance/shopify/paginate.rb +93 -0
  61. data/performance/shopify/shop_filter.rb +98 -0
  62. data/performance/shopify/tag_filter.rb +25 -0
  63. data/performance/shopify/vision.database.yml +945 -0
  64. data/performance/shopify/weight_filter.rb +11 -0
  65. data/performance/tests/dropify/article.liquid +74 -0
  66. data/performance/tests/dropify/blog.liquid +33 -0
  67. data/performance/tests/dropify/cart.liquid +66 -0
  68. data/performance/tests/dropify/collection.liquid +22 -0
  69. data/performance/tests/dropify/index.liquid +47 -0
  70. data/performance/tests/dropify/page.liquid +8 -0
  71. data/performance/tests/dropify/product.liquid +68 -0
  72. data/performance/tests/dropify/theme.liquid +105 -0
  73. data/performance/tests/ripen/article.liquid +74 -0
  74. data/performance/tests/ripen/blog.liquid +13 -0
  75. data/performance/tests/ripen/cart.liquid +54 -0
  76. data/performance/tests/ripen/collection.liquid +29 -0
  77. data/performance/tests/ripen/index.liquid +32 -0
  78. data/performance/tests/ripen/page.liquid +4 -0
  79. data/performance/tests/ripen/product.liquid +75 -0
  80. data/performance/tests/ripen/theme.liquid +85 -0
  81. data/performance/tests/tribble/404.liquid +56 -0
  82. data/performance/tests/tribble/article.liquid +98 -0
  83. data/performance/tests/tribble/blog.liquid +41 -0
  84. data/performance/tests/tribble/cart.liquid +134 -0
  85. data/performance/tests/tribble/collection.liquid +70 -0
  86. data/performance/tests/tribble/index.liquid +94 -0
  87. data/performance/tests/tribble/page.liquid +56 -0
  88. data/performance/tests/tribble/product.liquid +116 -0
  89. data/performance/tests/tribble/search.liquid +51 -0
  90. data/performance/tests/tribble/theme.liquid +90 -0
  91. data/performance/tests/vogue/article.liquid +66 -0
  92. data/performance/tests/vogue/blog.liquid +32 -0
  93. data/performance/tests/vogue/cart.liquid +58 -0
  94. data/performance/tests/vogue/collection.liquid +19 -0
  95. data/performance/tests/vogue/index.liquid +22 -0
  96. data/performance/tests/vogue/page.liquid +3 -0
  97. data/performance/tests/vogue/product.liquid +62 -0
  98. data/performance/tests/vogue/theme.liquid +122 -0
  99. data/test/assign_test.rb +11 -0
  100. data/test/block_test.rb +58 -0
  101. data/test/capture_test.rb +41 -0
  102. data/test/condition_test.rb +115 -0
  103. data/test/content_for_test.rb +15 -0
  104. data/test/context_test.rb +479 -0
  105. data/test/drop_test.rb +162 -0
  106. data/test/error_handling_test.rb +89 -0
  107. data/test/extra/breakpoint.rb +547 -0
  108. data/test/extra/caller.rb +80 -0
  109. data/test/file_system_test.rb +30 -0
  110. data/test/filter_test.rb +147 -0
  111. data/test/helper.rb +24 -0
  112. data/test/html_tag_test.rb +31 -0
  113. data/test/if_else_test.rb +139 -0
  114. data/test/include_tag_test.rb +129 -0
  115. data/test/module_ex_test.rb +89 -0
  116. data/test/output_test.rb +121 -0
  117. data/test/parsing_quirks_test.rb +54 -0
  118. data/test/regexp_test.rb +45 -0
  119. data/test/security_test.rb +41 -0
  120. data/test/standard_filter_test.rb +170 -0
  121. data/test/standard_tag_test.rb +405 -0
  122. data/test/statements_test.rb +137 -0
  123. data/test/strainer_test.rb +27 -0
  124. data/test/template_test.rb +82 -0
  125. data/test/test_helper.rb +28 -0
  126. data/test/unless_else_test.rb +27 -0
  127. data/test/variable_test.rb +173 -0
  128. data/test/yield_test.rb +24 -0
  129. metadata +237 -0
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class AssignTest < Test::Unit::TestCase
4
+ include Liquor
5
+
6
+ def test_assigned_variable
7
+ assert_template_result('.foo.','{% assign foo = values %}.{{ foo[0] }}.', 'values' => %w{foo bar baz})
8
+ assert_template_result('.bar.','{% assign foo = values %}.{{ foo[1] }}.', 'values' => %w{foo bar baz})
9
+ end
10
+
11
+ end
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class VariableTest < Test::Unit::TestCase
4
+ include Liquor
5
+
6
+ def test_blankspace
7
+ template = Liquor::Template.parse(" ")
8
+ assert_equal [" "], template.root.nodelist
9
+ end
10
+
11
+ def test_variable_beginning
12
+ template = Liquor::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 = Liquor::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 = Liquor::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 = Liquor::Template.parse(" {{funk}} {{so}} {{brother}} ")
35
+ assert_equal 7, template.root.nodelist.size
36
+ assert_equal [String, Variable, String, Variable, String, Variable, String], block_types(template.root.nodelist)
37
+ end
38
+
39
+ def test_with_block
40
+ template = Liquor::Template.parse(" {% comment %} {% endcomment %} ")
41
+ assert_equal [String, Comment, String], block_types(template.root.nodelist)
42
+ assert_equal 3, template.root.nodelist.size
43
+ end
44
+
45
+ def test_with_custom_tag
46
+ Liquor::Template.register_tag("testtag", Block)
47
+
48
+ assert_nothing_thrown do
49
+ template = Liquor::Template.parse( "{% testtag %} {% endtesttag %}")
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def block_types(nodelist)
56
+ nodelist.collect { |node| node.class }
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class CaptureTest < Test::Unit::TestCase
4
+ include Liquor
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
+
41
+ end
@@ -0,0 +1,115 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class ConditionTest < Test::Unit::TestCase
4
+ include Liquor
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
+ end
22
+
23
+ def test_default_operators_evalute_false
24
+ assert_evalutes_false '1', '==', '2'
25
+ assert_evalutes_false '1', '!=', '1'
26
+ assert_evalutes_false '1', '<>', '1'
27
+ assert_evalutes_false '1', '<', '0'
28
+ assert_evalutes_false '2', '>', '4'
29
+ assert_evalutes_false '1', '>=', '3'
30
+ assert_evalutes_false '2', '>=', '4'
31
+ assert_evalutes_false '1', '<=', '0'
32
+ assert_evalutes_false '1', '<=', '0'
33
+ end
34
+
35
+ def test_contains_works_on_strings
36
+ assert_evalutes_true "'bob'", 'contains', "'o'"
37
+ assert_evalutes_true "'bob'", 'contains', "'b'"
38
+ assert_evalutes_true "'bob'", 'contains', "'bo'"
39
+ assert_evalutes_true "'bob'", 'contains', "'ob'"
40
+ assert_evalutes_true "'bob'", 'contains', "'bob'"
41
+
42
+ assert_evalutes_false "'bob'", 'contains', "'bob2'"
43
+ assert_evalutes_false "'bob'", 'contains', "'a'"
44
+ assert_evalutes_false "'bob'", 'contains', "'---'"
45
+ end
46
+
47
+ def test_contains_works_on_arrays
48
+ @context = Liquor::Context.new
49
+ @context['array'] = [1,2,3,4,5]
50
+
51
+ assert_evalutes_false "array", 'contains', '0'
52
+ assert_evalutes_true "array", 'contains', '1'
53
+ assert_evalutes_true "array", 'contains', '2'
54
+ assert_evalutes_true "array", 'contains', '3'
55
+ assert_evalutes_true "array", 'contains', '4'
56
+ assert_evalutes_true "array", 'contains', '5'
57
+ assert_evalutes_false "array", 'contains', '6'
58
+
59
+ assert_evalutes_false "array", 'contains', '"1"'
60
+
61
+ end
62
+
63
+ def test_contains_returns_false_for_nil_operands
64
+ @context = Liquor::Context.new
65
+ assert_evalutes_false "not_assigned", 'contains', '0'
66
+ assert_evalutes_false "0", 'contains', 'not_assigned'
67
+ end
68
+
69
+ def test_or_condition
70
+ condition = Condition.new('1', '==', '2')
71
+
72
+ assert_equal false, condition.evaluate
73
+
74
+ condition.or Condition.new('2', '==', '1')
75
+
76
+ assert_equal false, condition.evaluate
77
+
78
+ condition.or Condition.new('1', '==', '1')
79
+
80
+ assert_equal true, condition.evaluate
81
+ end
82
+
83
+ def test_and_condition
84
+ condition = Condition.new('1', '==', '1')
85
+
86
+ assert_equal true, condition.evaluate
87
+
88
+ condition.and Condition.new('2', '==', '2')
89
+
90
+ assert_equal true, condition.evaluate
91
+
92
+ condition.and Condition.new('2', '==', '1')
93
+
94
+ assert_equal false, condition.evaluate
95
+ end
96
+
97
+
98
+ def test_should_allow_custom_proc_operator
99
+ Condition.operators['starts_with'] = Proc.new { |cond, left, right| left =~ %r{^#{right}}}
100
+
101
+ assert_evalutes_true "'bob'", 'starts_with', "'b'"
102
+ assert_evalutes_false "'bob'", 'starts_with', "'o'"
103
+ ensure
104
+ Condition.operators.delete 'starts_with'
105
+ end
106
+
107
+ private
108
+ def assert_evalutes_true(left, op, right)
109
+ assert Condition.new(left, op, right).evaluate(@context || Liquor::Context.new), "Evaluated false: #{left} #{op} #{right}"
110
+ end
111
+
112
+ def assert_evalutes_false(left, op, right)
113
+ assert !Condition.new(left, op, right).evaluate(@context || Liquor::Context.new), "Evaluated true: #{left} #{op} #{right}"
114
+ end
115
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class ContentForTest < Test::Unit::TestCase
4
+ include Liquor
5
+
6
+ def test_content_for
7
+ layout_template = '<title>{% yield title %}</title> <body>{% yield %}</body>'
8
+ view_template = '{% content_for title %}{% assign text="aaa" %}{{ text }}{% end_content_for %}{% assign text="body" %}{{ text }}'
9
+
10
+ rendered_template = Template.parse(view_template).render({ :layout => Template.parse(layout_template) })
11
+
12
+ assert_equal '<title>aaa</title> <body>body</body>', rendered_template
13
+ end
14
+
15
+ end
@@ -0,0 +1,479 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ class HundredCentes
3
+ def to_liquor
4
+ 100
5
+ end
6
+ end
7
+
8
+ class CentsDrop < Liquor::Drop
9
+ def amount
10
+ HundredCentes.new
11
+ end
12
+
13
+ def non_zero?
14
+ true
15
+ end
16
+ end
17
+
18
+ class ContextSensitiveDrop < Liquor::Drop
19
+ def test
20
+ @context['test']
21
+ end
22
+ end
23
+
24
+ class Category < Liquor::Drop
25
+ attr_accessor :name
26
+
27
+ def initialize(name)
28
+ @name = name
29
+ end
30
+
31
+ def to_liquor
32
+ CategoryDrop.new(self)
33
+ end
34
+ end
35
+
36
+ class CategoryDrop
37
+ attr_accessor :category, :context
38
+ def initialize(category)
39
+ @category = category
40
+ end
41
+ end
42
+
43
+ class CounterDrop < Liquor::Drop
44
+ def count
45
+ @count ||= 0
46
+ @count += 1
47
+ end
48
+ end
49
+
50
+ class ArrayLike
51
+ def fetch(index)
52
+ end
53
+
54
+ def [](index)
55
+ @counts ||= []
56
+ @counts[index] ||= 0
57
+ @counts[index] += 1
58
+ end
59
+
60
+ def to_liquor
61
+ self
62
+ end
63
+ end
64
+
65
+
66
+ class ContextTest < Test::Unit::TestCase
67
+ include Liquor
68
+
69
+ def setup
70
+ @context = Liquor::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(Liquor::ContextError) do
112
+ @context.pop
113
+ end
114
+
115
+ assert_raise(Liquor::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_liquor_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
+
479
+ end