liquid 2.6.3 → 3.0.0

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +46 -13
  3. data/README.md +27 -2
  4. data/lib/liquid/block.rb +85 -51
  5. data/lib/liquid/block_body.rb +123 -0
  6. data/lib/liquid/condition.rb +26 -15
  7. data/lib/liquid/context.rb +106 -140
  8. data/lib/liquid/document.rb +3 -3
  9. data/lib/liquid/drop.rb +17 -1
  10. data/lib/liquid/errors.rb +50 -2
  11. data/lib/liquid/expression.rb +33 -0
  12. data/lib/liquid/file_system.rb +17 -6
  13. data/lib/liquid/i18n.rb +39 -0
  14. data/lib/liquid/interrupts.rb +1 -1
  15. data/lib/liquid/lexer.rb +51 -0
  16. data/lib/liquid/locales/en.yml +22 -0
  17. data/lib/liquid/parser.rb +90 -0
  18. data/lib/liquid/parser_switching.rb +31 -0
  19. data/lib/liquid/profiler/hooks.rb +23 -0
  20. data/lib/liquid/profiler.rb +159 -0
  21. data/lib/liquid/range_lookup.rb +22 -0
  22. data/lib/liquid/standardfilters.rb +143 -55
  23. data/lib/liquid/strainer.rb +14 -4
  24. data/lib/liquid/tag.rb +25 -9
  25. data/lib/liquid/tags/assign.rb +12 -9
  26. data/lib/liquid/tags/break.rb +1 -1
  27. data/lib/liquid/tags/capture.rb +10 -8
  28. data/lib/liquid/tags/case.rb +13 -13
  29. data/lib/liquid/tags/comment.rb +9 -2
  30. data/lib/liquid/tags/continue.rb +1 -4
  31. data/lib/liquid/tags/cycle.rb +5 -7
  32. data/lib/liquid/tags/decrement.rb +3 -4
  33. data/lib/liquid/tags/for.rb +69 -36
  34. data/lib/liquid/tags/if.rb +52 -25
  35. data/lib/liquid/tags/ifchanged.rb +3 -3
  36. data/lib/liquid/tags/include.rb +19 -8
  37. data/lib/liquid/tags/increment.rb +4 -8
  38. data/lib/liquid/tags/raw.rb +4 -7
  39. data/lib/liquid/tags/table_row.rb +73 -0
  40. data/lib/liquid/tags/unless.rb +2 -4
  41. data/lib/liquid/template.rb +124 -14
  42. data/lib/liquid/token.rb +18 -0
  43. data/lib/liquid/utils.rb +13 -4
  44. data/lib/liquid/variable.rb +103 -25
  45. data/lib/liquid/variable_lookup.rb +78 -0
  46. data/lib/liquid/version.rb +1 -1
  47. data/lib/liquid.rb +19 -11
  48. data/test/fixtures/en_locale.yml +9 -0
  49. data/test/{liquid → integration}/assign_test.rb +18 -1
  50. data/test/integration/blank_test.rb +106 -0
  51. data/test/{liquid → integration}/capture_test.rb +3 -3
  52. data/test/integration/context_test.rb +32 -0
  53. data/test/integration/drop_test.rb +271 -0
  54. data/test/integration/error_handling_test.rb +207 -0
  55. data/test/{liquid → integration}/filter_test.rb +11 -11
  56. data/test/integration/hash_ordering_test.rb +23 -0
  57. data/test/{liquid → integration}/output_test.rb +13 -13
  58. data/test/integration/parsing_quirks_test.rb +116 -0
  59. data/test/integration/render_profiling_test.rb +154 -0
  60. data/test/{liquid → integration}/security_test.rb +10 -10
  61. data/test/{liquid → integration}/standard_filter_test.rb +148 -32
  62. data/test/{liquid → integration}/tags/break_tag_test.rb +1 -1
  63. data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -1
  64. data/test/{liquid → integration}/tags/for_tag_test.rb +80 -2
  65. data/test/{liquid → integration}/tags/if_else_tag_test.rb +24 -21
  66. data/test/integration/tags/include_tag_test.rb +234 -0
  67. data/test/{liquid → integration}/tags/increment_tag_test.rb +1 -1
  68. data/test/{liquid → integration}/tags/raw_tag_test.rb +2 -1
  69. data/test/{liquid → integration}/tags/standard_tag_test.rb +28 -26
  70. data/test/integration/tags/statements_test.rb +113 -0
  71. data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +5 -5
  72. data/test/{liquid → integration}/tags/unless_else_tag_test.rb +1 -1
  73. data/test/{liquid → integration}/template_test.rb +81 -45
  74. data/test/integration/variable_test.rb +82 -0
  75. data/test/test_helper.rb +73 -20
  76. data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +2 -5
  77. data/test/{liquid/condition_test.rb → unit/condition_unit_test.rb} +23 -1
  78. data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +39 -25
  79. data/test/{liquid/file_system_test.rb → unit/file_system_unit_test.rb} +11 -5
  80. data/test/unit/i18n_unit_test.rb +37 -0
  81. data/test/unit/lexer_unit_test.rb +48 -0
  82. data/test/{liquid/module_ex_test.rb → unit/module_ex_unit_test.rb} +7 -7
  83. data/test/unit/parser_unit_test.rb +82 -0
  84. data/test/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +3 -3
  85. data/test/{liquid/strainer_test.rb → unit/strainer_unit_test.rb} +20 -1
  86. data/test/unit/tag_unit_test.rb +16 -0
  87. data/test/unit/tags/case_tag_unit_test.rb +10 -0
  88. data/test/unit/tags/for_tag_unit_test.rb +13 -0
  89. data/test/unit/tags/if_tag_unit_test.rb +8 -0
  90. data/test/unit/template_unit_test.rb +69 -0
  91. data/test/unit/tokenizer_unit_test.rb +38 -0
  92. data/test/unit/variable_unit_test.rb +139 -0
  93. metadata +135 -67
  94. data/lib/extras/liquid_view.rb +0 -51
  95. data/lib/liquid/htmltags.rb +0 -73
  96. data/test/liquid/drop_test.rb +0 -180
  97. data/test/liquid/error_handling_test.rb +0 -81
  98. data/test/liquid/hash_ordering_test.rb +0 -25
  99. data/test/liquid/parsing_quirks_test.rb +0 -52
  100. data/test/liquid/tags/include_tag_test.rb +0 -166
  101. data/test/liquid/tags/statements_test.rb +0 -134
  102. data/test/liquid/variable_test.rb +0 -186
data/lib/liquid.rb CHANGED
@@ -21,9 +21,9 @@
21
21
 
22
22
  module Liquid
23
23
  FilterSeparator = /\|/
24
- ArgumentSeparator = ','
25
- FilterArgumentSeparator = ':'
26
- VariableAttributeSeparator = '.'
24
+ ArgumentSeparator = ','.freeze
25
+ FilterArgumentSeparator = ':'.freeze
26
+ VariableAttributeSeparator = '.'.freeze
27
27
  TagStart = /\{\%/
28
28
  TagEnd = /\%\}/
29
29
  VariableSignature = /\(?[\w\-\.\[\]]\)?/
@@ -33,37 +33,45 @@ module Liquid
33
33
  VariableIncompleteEnd = /\}\}?/
34
34
  QuotedString = /"[^"]*"|'[^']*'/
35
35
  QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
36
- StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s|:,]+/
37
- FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/o
38
- OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/o
39
- SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/o
40
- Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/o
41
36
  TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o
42
37
  AnyStartingTag = /\{\{|\{\%/
43
- PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/o
44
- TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/o
38
+ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
39
+ TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
45
40
  VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
41
+
42
+ singleton_class.send(:attr_accessor, :cache_classes)
43
+ self.cache_classes = true
46
44
  end
47
45
 
48
46
  require "liquid/version"
47
+ require 'liquid/lexer'
48
+ require 'liquid/parser'
49
+ require 'liquid/i18n'
49
50
  require 'liquid/drop'
50
51
  require 'liquid/extensions'
51
52
  require 'liquid/errors'
52
53
  require 'liquid/interrupts'
53
54
  require 'liquid/strainer'
55
+ require 'liquid/expression'
54
56
  require 'liquid/context'
57
+ require 'liquid/parser_switching'
55
58
  require 'liquid/tag'
56
59
  require 'liquid/block'
57
60
  require 'liquid/document'
58
61
  require 'liquid/variable'
62
+ require 'liquid/variable_lookup'
63
+ require 'liquid/range_lookup'
59
64
  require 'liquid/file_system'
60
65
  require 'liquid/template'
61
- require 'liquid/htmltags'
62
66
  require 'liquid/standardfilters'
63
67
  require 'liquid/condition'
64
68
  require 'liquid/module_ex'
65
69
  require 'liquid/utils'
70
+ require 'liquid/token'
66
71
 
67
72
  # Load all the tags of the standard library
68
73
  #
69
74
  Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
75
+
76
+ require 'liquid/profiler'
77
+ require 'liquid/profiler/hooks'
@@ -0,0 +1,9 @@
1
+ ---
2
+ simple: "less is more"
3
+ whatever: "something %{something}"
4
+ errors:
5
+ i18n:
6
+ undefined_interpolation: "undefined key %{key}"
7
+ unknown_translation: "translation '%{name}' wasn't found"
8
+ syntax:
9
+ oops: "something wasn't right"
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class AssignTest < Test::Unit::TestCase
3
+ class AssignTest < Minitest::Test
4
4
  include Liquid
5
5
 
6
6
  def test_assigned_variable
@@ -18,4 +18,21 @@ class AssignTest < Test::Unit::TestCase
18
18
  '{% assign foo = values | split: "," %}.{{ foo[1] }}.',
19
19
  'values' => "foo,bar,baz")
20
20
  end
21
+
22
+ def test_assign_syntax_error
23
+ assert_match_syntax_error(/assign/,
24
+ '{% assign foo not values %}.',
25
+ 'values' => "foo,bar,baz")
26
+ end
27
+
28
+ def test_assign_uses_error_mode
29
+ with_error_mode(:strict) do
30
+ assert_raises(SyntaxError) do
31
+ Template.parse("{% assign foo = ('X' | downcase) %}")
32
+ end
33
+ end
34
+ with_error_mode(:lax) do
35
+ assert Template.parse("{% assign foo = ('X' | downcase) %}")
36
+ end
37
+ end
21
38
  end # AssignTest
@@ -0,0 +1,106 @@
1
+ require 'test_helper'
2
+
3
+ class FoobarTag < Liquid::Tag
4
+ def render(*args)
5
+ " "
6
+ end
7
+
8
+ Liquid::Template.register_tag('foobar', FoobarTag)
9
+ end
10
+
11
+ class BlankTestFileSystem
12
+ def read_template_file(template_path, context)
13
+ template_path
14
+ end
15
+ end
16
+
17
+ class BlankTest < Minitest::Test
18
+ include Liquid
19
+ N = 10
20
+
21
+ def wrap_in_for(body)
22
+ "{% for i in (1..#{N}) %}#{body}{% endfor %}"
23
+ end
24
+
25
+ def wrap_in_if(body)
26
+ "{% if true %}#{body}{% endif %}"
27
+ end
28
+
29
+ def wrap(body)
30
+ wrap_in_for(body) + wrap_in_if(body)
31
+ end
32
+
33
+ def test_new_tags_are_not_blank_by_default
34
+ assert_template_result(" "*N, wrap_in_for("{% foobar %}"))
35
+ end
36
+
37
+ def test_loops_are_blank
38
+ assert_template_result("", wrap_in_for(" "))
39
+ end
40
+
41
+ def test_if_else_are_blank
42
+ assert_template_result("", "{% if true %} {% elsif false %} {% else %} {% endif %}")
43
+ end
44
+
45
+ def test_unless_is_blank
46
+ assert_template_result("", wrap("{% unless true %} {% endunless %}"))
47
+ end
48
+
49
+ def test_mark_as_blank_only_during_parsing
50
+ assert_template_result(" "*(N+1), wrap(" {% if false %} this never happens, but still, this block is not blank {% endif %}"))
51
+ end
52
+
53
+ def test_comments_are_blank
54
+ assert_template_result("", wrap(" {% comment %} whatever {% endcomment %} "))
55
+ end
56
+
57
+ def test_captures_are_blank
58
+ assert_template_result("", wrap(" {% capture foo %} whatever {% endcapture %} "))
59
+ end
60
+
61
+ def test_nested_blocks_are_blank_but_only_if_all_children_are
62
+ assert_template_result("", wrap(wrap(" ")))
63
+ assert_template_result("\n but this is not "*(N+1),
64
+ wrap(%q{{% if true %} {% comment %} this is blank {% endcomment %} {% endif %}
65
+ {% if true %} but this is not {% endif %}}))
66
+ end
67
+
68
+ def test_assigns_are_blank
69
+ assert_template_result("", wrap(' {% assign foo = "bar" %} '))
70
+ end
71
+
72
+ def test_whitespace_is_blank
73
+ assert_template_result("", wrap(" "))
74
+ assert_template_result("", wrap("\t"))
75
+ end
76
+
77
+ def test_whitespace_is_not_blank_if_other_stuff_is_present
78
+ body = " x "
79
+ assert_template_result(body*(N+1), wrap(body))
80
+ end
81
+
82
+ def test_increment_is_not_blank
83
+ assert_template_result(" 0"*2*(N+1), wrap("{% assign foo = 0 %} {% increment foo %} {% decrement foo %}"))
84
+ end
85
+
86
+ def test_cycle_is_not_blank
87
+ assert_template_result(" "*((N+1)/2)+" ", wrap("{% cycle ' ', ' ' %}"))
88
+ end
89
+
90
+ def test_raw_is_not_blank
91
+ assert_template_result(" "*(N+1), wrap(" {% raw %} {% endraw %}"))
92
+ end
93
+
94
+ def test_include_is_blank
95
+ Liquid::Template.file_system = BlankTestFileSystem.new
96
+ assert_template_result "foobar"*(N+1), wrap("{% include 'foobar' %}")
97
+ assert_template_result " foobar "*(N+1), wrap("{% include ' foobar ' %}")
98
+ assert_template_result " "*(N+1), wrap(" {% include ' ' %} ")
99
+ end
100
+
101
+ def test_case_is_blank
102
+ assert_template_result("", wrap(" {% assign foo = 'bar' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} {% endcase %} "))
103
+ assert_template_result("", wrap(" {% assign foo = 'else' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} {% endcase %} "))
104
+ assert_template_result(" x "*(N+1), wrap(" {% assign foo = 'else' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} x {% endcase %} "))
105
+ end
106
+ end
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class CaptureTest < Test::Unit::TestCase
3
+ class CaptureTest < Minitest::Test
4
4
  include Liquid
5
5
 
6
6
  def test_captures_block_content_in_variable
@@ -19,7 +19,7 @@ class CaptureTest < Test::Unit::TestCase
19
19
  {{var}}
20
20
  END_TEMPLATE
21
21
  template = Template.parse(template_source)
22
- rendered = template.render
22
+ rendered = template.render!
23
23
  assert_equal "test-string", rendered.gsub(/\s/, '')
24
24
  end
25
25
 
@@ -34,7 +34,7 @@ class CaptureTest < Test::Unit::TestCase
34
34
  {{ first }}-{{ second }}
35
35
  END_TEMPLATE
36
36
  template = Template.parse(template_source)
37
- rendered = template.render
37
+ rendered = template.render!
38
38
  assert_equal "3-3", rendered.gsub(/\s/, '')
39
39
  end
40
40
  end # CaptureTest
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ class ContextTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_override_global_filter
7
+ global = Module.new do
8
+ def notice(output)
9
+ "Global #{output}"
10
+ end
11
+ end
12
+
13
+ local = Module.new do
14
+ def notice(output)
15
+ "Local #{output}"
16
+ end
17
+ end
18
+
19
+ with_global_filter(global) do
20
+ assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
21
+ assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, :filters => [local])
22
+ end
23
+ end
24
+
25
+ def test_has_key_will_not_add_an_error_for_missing_keys
26
+ with_error_mode :strict do
27
+ context = Context.new
28
+ context.has_key?('unknown')
29
+ assert_empty context.errors
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,271 @@
1
+ require 'test_helper'
2
+
3
+ class ContextDrop < Liquid::Drop
4
+ def scopes
5
+ @context.scopes.size
6
+ end
7
+
8
+ def scopes_as_array
9
+ (1..@context.scopes.size).to_a
10
+ end
11
+
12
+ def loop_pos
13
+ @context['forloop.index']
14
+ end
15
+
16
+ def before_method(method)
17
+ return @context[method]
18
+ end
19
+ end
20
+
21
+ class ProductDrop < Liquid::Drop
22
+
23
+ class TextDrop < Liquid::Drop
24
+ def array
25
+ ['text1', 'text2']
26
+ end
27
+
28
+ def text
29
+ 'text1'
30
+ end
31
+ end
32
+
33
+ class CatchallDrop < Liquid::Drop
34
+ def before_method(method)
35
+ return 'method: ' << method.to_s
36
+ end
37
+ end
38
+
39
+ def texts
40
+ TextDrop.new
41
+ end
42
+
43
+ def catchall
44
+ CatchallDrop.new
45
+ end
46
+
47
+ def context
48
+ ContextDrop.new
49
+ end
50
+
51
+ def user_input
52
+ "foo".taint
53
+ end
54
+
55
+ protected
56
+ def callmenot
57
+ "protected"
58
+ end
59
+ end
60
+
61
+ class EnumerableDrop < Liquid::Drop
62
+ def before_method(method)
63
+ method
64
+ end
65
+
66
+ def size
67
+ 3
68
+ end
69
+
70
+ def first
71
+ 1
72
+ end
73
+
74
+ def count
75
+ 3
76
+ end
77
+
78
+ def min
79
+ 1
80
+ end
81
+
82
+ def max
83
+ 3
84
+ end
85
+
86
+ def each
87
+ yield 1
88
+ yield 2
89
+ yield 3
90
+ end
91
+ end
92
+
93
+ class RealEnumerableDrop < Liquid::Drop
94
+ include Enumerable
95
+
96
+ def before_method(method)
97
+ method
98
+ end
99
+
100
+ def each
101
+ yield 1
102
+ yield 2
103
+ yield 3
104
+ end
105
+ end
106
+
107
+ class DropsTest < Minitest::Test
108
+ include Liquid
109
+
110
+ def test_product_drop
111
+ tpl = Liquid::Template.parse(' ')
112
+ assert_equal ' ', tpl.render!('product' => ProductDrop.new)
113
+ end
114
+
115
+ def test_rendering_raises_on_tainted_attr
116
+ with_taint_mode(:error) do
117
+ tpl = Liquid::Template.parse('{{ product.user_input }}')
118
+ assert_raises TaintedError do
119
+ tpl.render!('product' => ProductDrop.new)
120
+ end
121
+ end
122
+ end
123
+
124
+ def test_rendering_warns_on_tainted_attr
125
+ with_taint_mode(:warn) do
126
+ tpl = Liquid::Template.parse('{{ product.user_input }}')
127
+ tpl.render!('product' => ProductDrop.new)
128
+ assert_match /tainted/, tpl.warnings.first
129
+ end
130
+ end
131
+
132
+ def test_rendering_doesnt_raise_on_escaped_tainted_attr
133
+ with_taint_mode(:error) do
134
+ tpl = Liquid::Template.parse('{{ product.user_input | escape }}')
135
+ tpl.render!('product' => ProductDrop.new)
136
+ end
137
+ end
138
+
139
+ def test_drop_does_only_respond_to_whitelisted_methods
140
+ assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new)
141
+ assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new)
142
+ assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new)
143
+ assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new)
144
+ assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new)
145
+ assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new)
146
+ end
147
+
148
+ def test_drops_respond_to_to_liquid
149
+ assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new)
150
+ assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new)
151
+ end
152
+
153
+ def test_text_drop
154
+ output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render!('product' => ProductDrop.new)
155
+ assert_equal ' text1 ', output
156
+ end
157
+
158
+ def test_unknown_method
159
+ output = Liquid::Template.parse( ' {{ product.catchall.unknown }} ' ).render!('product' => ProductDrop.new)
160
+ assert_equal ' method: unknown ', output
161
+ end
162
+
163
+ def test_integer_argument_drop
164
+ output = Liquid::Template.parse( ' {{ product.catchall[8] }} ' ).render!('product' => ProductDrop.new)
165
+ assert_equal ' method: 8 ', output
166
+ end
167
+
168
+ def test_text_array_drop
169
+ output = Liquid::Template.parse( '{% for text in product.texts.array %} {{text}} {% endfor %}' ).render!('product' => ProductDrop.new)
170
+ assert_equal ' text1 text2 ', output
171
+ end
172
+
173
+ def test_context_drop
174
+ output = Liquid::Template.parse( ' {{ context.bar }} ' ).render!('context' => ContextDrop.new, 'bar' => "carrot")
175
+ assert_equal ' carrot ', output
176
+ end
177
+
178
+ def test_nested_context_drop
179
+ output = Liquid::Template.parse( ' {{ product.context.foo }} ' ).render!('product' => ProductDrop.new, 'foo' => "monkey")
180
+ assert_equal ' monkey ', output
181
+ end
182
+
183
+ def test_protected
184
+ output = Liquid::Template.parse( ' {{ product.callmenot }} ' ).render!('product' => ProductDrop.new)
185
+ assert_equal ' ', output
186
+ end
187
+
188
+ def test_object_methods_not_allowed
189
+ [:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
190
+ output = Liquid::Template.parse(" {{ product.#{method} }} ").render!('product' => ProductDrop.new)
191
+ assert_equal ' ', output
192
+ end
193
+ end
194
+
195
+ def test_scope
196
+ assert_equal '1', Liquid::Template.parse( '{{ context.scopes }}' ).render!('context' => ContextDrop.new)
197
+ assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ context.scopes }}{%endfor%}' ).render!('context' => ContextDrop.new, 'dummy' => [1])
198
+ assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}' ).render!('context' => ContextDrop.new, 'dummy' => [1])
199
+ end
200
+
201
+ def test_scope_though_proc
202
+ assert_equal '1', Liquid::Template.parse( '{{ s }}' ).render!('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] })
203
+ assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ s }}{%endfor%}' ).render!('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1])
204
+ assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}' ).render!('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1])
205
+ end
206
+
207
+ def test_scope_with_assigns
208
+ assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render!('context' => ContextDrop.new)
209
+ assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}' ).render!('context' => ContextDrop.new, 'dummy' => [1])
210
+ assert_equal 'test', Liquid::Template.parse( '{% assign header_gif = "test"%}{{header_gif}}' ).render!('context' => ContextDrop.new)
211
+ assert_equal 'test', Liquid::Template.parse( "{% assign header_gif = 'test'%}{{header_gif}}" ).render!('context' => ContextDrop.new)
212
+ end
213
+
214
+ def test_scope_from_tags
215
+ assert_equal '1', Liquid::Template.parse( '{% for i in context.scopes_as_array %}{{i}}{% endfor %}' ).render!('context' => ContextDrop.new, 'dummy' => [1])
216
+ assert_equal '12', Liquid::Template.parse( '{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}' ).render!('context' => ContextDrop.new, 'dummy' => [1])
217
+ assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}' ).render!('context' => ContextDrop.new, 'dummy' => [1])
218
+ end
219
+
220
+ def test_access_context_from_drop
221
+ assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render!('context' => ContextDrop.new, 'dummy' => [1,2,3])
222
+ end
223
+
224
+ def test_enumerable_drop
225
+ assert_equal '123', Liquid::Template.parse( '{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new)
226
+ end
227
+
228
+ def test_enumerable_drop_size
229
+ assert_equal '3', Liquid::Template.parse( '{{collection.size}}').render!('collection' => EnumerableDrop.new)
230
+ end
231
+
232
+ def test_enumerable_drop_will_invoke_before_method_for_clashing_method_names
233
+ ["select", "each", "map", "cycle"].each do |method|
234
+ assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
235
+ assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
236
+ assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
237
+ assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
238
+ end
239
+ end
240
+
241
+ def test_some_enumerable_methods_still_get_invoked
242
+ [ :count, :max ].each do |method|
243
+ assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
244
+ assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
245
+ assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
246
+ assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
247
+ end
248
+
249
+ assert_equal "yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new)
250
+
251
+ [ :min, :first ].each do |method|
252
+ assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
253
+ assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
254
+ assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
255
+ assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
256
+ end
257
+ end
258
+
259
+ def test_empty_string_value_access
260
+ assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => '')
261
+ end
262
+
263
+ def test_nil_value_access
264
+ assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil)
265
+ end
266
+
267
+ def test_default_to_s_on_drops
268
+ assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new)
269
+ assert_equal 'EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new)
270
+ end
271
+ end # DropsTest