liquid 4.0.3 → 5.1.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +54 -0
  3. data/README.md +6 -0
  4. data/lib/liquid/block.rb +31 -14
  5. data/lib/liquid/block_body.rb +166 -54
  6. data/lib/liquid/condition.rb +41 -20
  7. data/lib/liquid/context.rb +107 -52
  8. data/lib/liquid/document.rb +47 -9
  9. data/lib/liquid/drop.rb +4 -2
  10. data/lib/liquid/errors.rb +20 -18
  11. data/lib/liquid/expression.rb +29 -34
  12. data/lib/liquid/extensions.rb +2 -0
  13. data/lib/liquid/file_system.rb +6 -4
  14. data/lib/liquid/forloop_drop.rb +11 -4
  15. data/lib/liquid/i18n.rb +5 -3
  16. data/lib/liquid/interrupts.rb +3 -1
  17. data/lib/liquid/lexer.rb +30 -23
  18. data/lib/liquid/locales/en.yml +3 -1
  19. data/lib/liquid/parse_context.rb +20 -4
  20. data/lib/liquid/parse_tree_visitor.rb +2 -2
  21. data/lib/liquid/parser.rb +30 -18
  22. data/lib/liquid/parser_switching.rb +17 -3
  23. data/lib/liquid/partial_cache.rb +24 -0
  24. data/lib/liquid/profiler/hooks.rb +26 -14
  25. data/lib/liquid/profiler.rb +67 -86
  26. data/lib/liquid/range_lookup.rb +13 -3
  27. data/lib/liquid/register.rb +6 -0
  28. data/lib/liquid/resource_limits.rb +47 -8
  29. data/lib/liquid/standardfilters.rb +95 -46
  30. data/lib/liquid/static_registers.rb +44 -0
  31. data/lib/liquid/strainer_factory.rb +36 -0
  32. data/lib/liquid/strainer_template.rb +53 -0
  33. data/lib/liquid/tablerowloop_drop.rb +6 -4
  34. data/lib/liquid/tag/disableable.rb +22 -0
  35. data/lib/liquid/tag/disabler.rb +21 -0
  36. data/lib/liquid/tag.rb +28 -6
  37. data/lib/liquid/tags/assign.rb +24 -10
  38. data/lib/liquid/tags/break.rb +8 -3
  39. data/lib/liquid/tags/capture.rb +11 -8
  40. data/lib/liquid/tags/case.rb +40 -27
  41. data/lib/liquid/tags/comment.rb +5 -3
  42. data/lib/liquid/tags/continue.rb +8 -3
  43. data/lib/liquid/tags/cycle.rb +25 -14
  44. data/lib/liquid/tags/decrement.rb +6 -3
  45. data/lib/liquid/tags/echo.rb +34 -0
  46. data/lib/liquid/tags/for.rb +68 -44
  47. data/lib/liquid/tags/if.rb +39 -23
  48. data/lib/liquid/tags/ifchanged.rb +11 -10
  49. data/lib/liquid/tags/include.rb +34 -47
  50. data/lib/liquid/tags/increment.rb +7 -3
  51. data/lib/liquid/tags/raw.rb +14 -11
  52. data/lib/liquid/tags/render.rb +84 -0
  53. data/lib/liquid/tags/table_row.rb +23 -19
  54. data/lib/liquid/tags/unless.rb +23 -15
  55. data/lib/liquid/template.rb +53 -72
  56. data/lib/liquid/template_factory.rb +9 -0
  57. data/lib/liquid/tokenizer.rb +18 -10
  58. data/lib/liquid/usage.rb +8 -0
  59. data/lib/liquid/utils.rb +13 -3
  60. data/lib/liquid/variable.rb +46 -41
  61. data/lib/liquid/variable_lookup.rb +11 -6
  62. data/lib/liquid/version.rb +2 -1
  63. data/lib/liquid.rb +17 -5
  64. data/test/integration/assign_test.rb +74 -5
  65. data/test/integration/blank_test.rb +11 -8
  66. data/test/integration/block_test.rb +47 -1
  67. data/test/integration/capture_test.rb +18 -10
  68. data/test/integration/context_test.rb +609 -5
  69. data/test/integration/document_test.rb +4 -2
  70. data/test/integration/drop_test.rb +67 -83
  71. data/test/integration/error_handling_test.rb +73 -61
  72. data/test/integration/expression_test.rb +46 -0
  73. data/test/integration/filter_test.rb +53 -42
  74. data/test/integration/hash_ordering_test.rb +5 -3
  75. data/test/integration/output_test.rb +26 -24
  76. data/test/integration/parsing_quirks_test.rb +19 -7
  77. data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
  78. data/test/integration/security_test.rb +30 -21
  79. data/test/integration/standard_filter_test.rb +385 -281
  80. data/test/integration/tag/disableable_test.rb +59 -0
  81. data/test/integration/tag_test.rb +45 -0
  82. data/test/integration/tags/break_tag_test.rb +4 -2
  83. data/test/integration/tags/continue_tag_test.rb +4 -2
  84. data/test/integration/tags/echo_test.rb +13 -0
  85. data/test/integration/tags/for_tag_test.rb +107 -51
  86. data/test/integration/tags/if_else_tag_test.rb +5 -3
  87. data/test/integration/tags/include_tag_test.rb +70 -54
  88. data/test/integration/tags/increment_tag_test.rb +4 -2
  89. data/test/integration/tags/liquid_tag_test.rb +116 -0
  90. data/test/integration/tags/raw_tag_test.rb +14 -11
  91. data/test/integration/tags/render_tag_test.rb +213 -0
  92. data/test/integration/tags/standard_tag_test.rb +38 -31
  93. data/test/integration/tags/statements_test.rb +23 -21
  94. data/test/integration/tags/table_row_test.rb +2 -0
  95. data/test/integration/tags/unless_else_tag_test.rb +4 -2
  96. data/test/integration/template_test.rb +132 -124
  97. data/test/integration/trim_mode_test.rb +78 -44
  98. data/test/integration/variable_test.rb +74 -32
  99. data/test/test_helper.rb +113 -22
  100. data/test/unit/block_unit_test.rb +19 -24
  101. data/test/unit/condition_unit_test.rb +79 -77
  102. data/test/unit/file_system_unit_test.rb +6 -4
  103. data/test/unit/i18n_unit_test.rb +7 -5
  104. data/test/unit/lexer_unit_test.rb +11 -9
  105. data/test/{integration → unit}/parse_tree_visitor_test.rb +16 -2
  106. data/test/unit/parser_unit_test.rb +37 -35
  107. data/test/unit/partial_cache_unit_test.rb +128 -0
  108. data/test/unit/regexp_unit_test.rb +17 -15
  109. data/test/unit/static_registers_unit_test.rb +156 -0
  110. data/test/unit/strainer_factory_unit_test.rb +100 -0
  111. data/test/unit/strainer_template_unit_test.rb +82 -0
  112. data/test/unit/tag_unit_test.rb +5 -3
  113. data/test/unit/tags/case_tag_unit_test.rb +3 -1
  114. data/test/unit/tags/for_tag_unit_test.rb +4 -2
  115. data/test/unit/tags/if_tag_unit_test.rb +3 -1
  116. data/test/unit/template_factory_unit_test.rb +12 -0
  117. data/test/unit/template_unit_test.rb +19 -10
  118. data/test/unit/tokenizer_unit_test.rb +26 -19
  119. data/test/unit/variable_unit_test.rb +51 -49
  120. metadata +76 -50
  121. data/lib/liquid/strainer.rb +0 -66
  122. data/lib/liquid/truffle.rb +0 -5
  123. data/test/truffle/truffle_test.rb +0 -9
  124. data/test/unit/context_unit_test.rb +0 -489
  125. data/test/unit/strainer_unit_test.rb +0 -164
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class ConditionUnitTest < Minitest::Test
@@ -8,154 +10,154 @@ class ConditionUnitTest < Minitest::Test
8
10
  end
9
11
 
10
12
  def test_basic_condition
11
- assert_equal false, Condition.new(1, '==', 2).evaluate
12
- assert_equal true, Condition.new(1, '==', 1).evaluate
13
+ assert_equal(false, Condition.new(1, '==', 2).evaluate)
14
+ assert_equal(true, Condition.new(1, '==', 1).evaluate)
13
15
  end
14
16
 
15
17
  def test_default_operators_evalute_true
16
- assert_evaluates_true 1, '==', 1
17
- assert_evaluates_true 1, '!=', 2
18
- assert_evaluates_true 1, '<>', 2
19
- assert_evaluates_true 1, '<', 2
20
- assert_evaluates_true 2, '>', 1
21
- assert_evaluates_true 1, '>=', 1
22
- assert_evaluates_true 2, '>=', 1
23
- assert_evaluates_true 1, '<=', 2
24
- assert_evaluates_true 1, '<=', 1
18
+ assert_evaluates_true(1, '==', 1)
19
+ assert_evaluates_true(1, '!=', 2)
20
+ assert_evaluates_true(1, '<>', 2)
21
+ assert_evaluates_true(1, '<', 2)
22
+ assert_evaluates_true(2, '>', 1)
23
+ assert_evaluates_true(1, '>=', 1)
24
+ assert_evaluates_true(2, '>=', 1)
25
+ assert_evaluates_true(1, '<=', 2)
26
+ assert_evaluates_true(1, '<=', 1)
25
27
  # negative numbers
26
- assert_evaluates_true 1, '>', -1
27
- assert_evaluates_true -1, '<', 1
28
- assert_evaluates_true 1.0, '>', -1.0
29
- assert_evaluates_true -1.0, '<', 1.0
28
+ assert_evaluates_true(1, '>', -1)
29
+ assert_evaluates_true(-1, '<', 1)
30
+ assert_evaluates_true(1.0, '>', -1.0)
31
+ assert_evaluates_true(-1.0, '<', 1.0)
30
32
  end
31
33
 
32
34
  def test_default_operators_evalute_false
33
- assert_evaluates_false 1, '==', 2
34
- assert_evaluates_false 1, '!=', 1
35
- assert_evaluates_false 1, '<>', 1
36
- assert_evaluates_false 1, '<', 0
37
- assert_evaluates_false 2, '>', 4
38
- assert_evaluates_false 1, '>=', 3
39
- assert_evaluates_false 2, '>=', 4
40
- assert_evaluates_false 1, '<=', 0
41
- assert_evaluates_false 1, '<=', 0
35
+ assert_evaluates_false(1, '==', 2)
36
+ assert_evaluates_false(1, '!=', 1)
37
+ assert_evaluates_false(1, '<>', 1)
38
+ assert_evaluates_false(1, '<', 0)
39
+ assert_evaluates_false(2, '>', 4)
40
+ assert_evaluates_false(1, '>=', 3)
41
+ assert_evaluates_false(2, '>=', 4)
42
+ assert_evaluates_false(1, '<=', 0)
43
+ assert_evaluates_false(1, '<=', 0)
42
44
  end
43
45
 
44
46
  def test_contains_works_on_strings
45
- assert_evaluates_true 'bob', 'contains', 'o'
46
- assert_evaluates_true 'bob', 'contains', 'b'
47
- assert_evaluates_true 'bob', 'contains', 'bo'
48
- assert_evaluates_true 'bob', 'contains', 'ob'
49
- assert_evaluates_true 'bob', 'contains', 'bob'
47
+ assert_evaluates_true('bob', 'contains', 'o')
48
+ assert_evaluates_true('bob', 'contains', 'b')
49
+ assert_evaluates_true('bob', 'contains', 'bo')
50
+ assert_evaluates_true('bob', 'contains', 'ob')
51
+ assert_evaluates_true('bob', 'contains', 'bob')
50
52
 
51
- assert_evaluates_false 'bob', 'contains', 'bob2'
52
- assert_evaluates_false 'bob', 'contains', 'a'
53
- assert_evaluates_false 'bob', 'contains', '---'
53
+ assert_evaluates_false('bob', 'contains', 'bob2')
54
+ assert_evaluates_false('bob', 'contains', 'a')
55
+ assert_evaluates_false('bob', 'contains', '---')
54
56
  end
55
57
 
56
58
  def test_invalid_comparation_operator
57
- assert_evaluates_argument_error 1, '~~', 0
59
+ assert_evaluates_argument_error(1, '~~', 0)
58
60
  end
59
61
 
60
62
  def test_comparation_of_int_and_str
61
- assert_evaluates_argument_error '1', '>', 0
62
- assert_evaluates_argument_error '1', '<', 0
63
- assert_evaluates_argument_error '1', '>=', 0
64
- assert_evaluates_argument_error '1', '<=', 0
63
+ assert_evaluates_argument_error('1', '>', 0)
64
+ assert_evaluates_argument_error('1', '<', 0)
65
+ assert_evaluates_argument_error('1', '>=', 0)
66
+ assert_evaluates_argument_error('1', '<=', 0)
65
67
  end
66
68
 
67
69
  def test_hash_compare_backwards_compatibility
68
- assert_nil Condition.new({}, '>', 2).evaluate
69
- assert_nil Condition.new(2, '>', {}).evaluate
70
- assert_equal false, Condition.new({}, '==', 2).evaluate
71
- assert_equal true, Condition.new({ 'a' => 1 }, '==', { 'a' => 1 }).evaluate
72
- assert_equal true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate
70
+ assert_nil(Condition.new({}, '>', 2).evaluate)
71
+ assert_nil(Condition.new(2, '>', {}).evaluate)
72
+ assert_equal(false, Condition.new({}, '==', 2).evaluate)
73
+ assert_equal(true, Condition.new({ 'a' => 1 }, '==', 'a' => 1).evaluate)
74
+ assert_equal(true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate)
73
75
  end
74
76
 
75
77
  def test_contains_works_on_arrays
76
- @context = Liquid::Context.new
78
+ @context = Liquid::Context.new
77
79
  @context['array'] = [1, 2, 3, 4, 5]
78
- array_expr = VariableLookup.new("array")
80
+ array_expr = VariableLookup.new("array")
79
81
 
80
- assert_evaluates_false array_expr, 'contains', 0
81
- assert_evaluates_true array_expr, 'contains', 1
82
- assert_evaluates_true array_expr, 'contains', 2
83
- assert_evaluates_true array_expr, 'contains', 3
84
- assert_evaluates_true array_expr, 'contains', 4
85
- assert_evaluates_true array_expr, 'contains', 5
86
- assert_evaluates_false array_expr, 'contains', 6
87
- assert_evaluates_false array_expr, 'contains', "1"
82
+ assert_evaluates_false(array_expr, 'contains', 0)
83
+ assert_evaluates_true(array_expr, 'contains', 1)
84
+ assert_evaluates_true(array_expr, 'contains', 2)
85
+ assert_evaluates_true(array_expr, 'contains', 3)
86
+ assert_evaluates_true(array_expr, 'contains', 4)
87
+ assert_evaluates_true(array_expr, 'contains', 5)
88
+ assert_evaluates_false(array_expr, 'contains', 6)
89
+ assert_evaluates_false(array_expr, 'contains', "1")
88
90
  end
89
91
 
90
92
  def test_contains_returns_false_for_nil_operands
91
93
  @context = Liquid::Context.new
92
- assert_evaluates_false VariableLookup.new('not_assigned'), 'contains', '0'
93
- assert_evaluates_false 0, 'contains', VariableLookup.new('not_assigned')
94
+ assert_evaluates_false(VariableLookup.new('not_assigned'), 'contains', '0')
95
+ assert_evaluates_false(0, 'contains', VariableLookup.new('not_assigned'))
94
96
  end
95
97
 
96
98
  def test_contains_return_false_on_wrong_data_type
97
- assert_evaluates_false 1, 'contains', 0
99
+ assert_evaluates_false(1, 'contains', 0)
98
100
  end
99
101
 
100
102
  def test_contains_with_string_left_operand_coerces_right_operand_to_string
101
- assert_evaluates_true ' 1 ', 'contains', 1
102
- assert_evaluates_false ' 1 ', 'contains', 2
103
+ assert_evaluates_true(' 1 ', 'contains', 1)
104
+ assert_evaluates_false(' 1 ', 'contains', 2)
103
105
  end
104
106
 
105
107
  def test_or_condition
106
108
  condition = Condition.new(1, '==', 2)
107
109
 
108
- assert_equal false, condition.evaluate
110
+ assert_equal(false, condition.evaluate)
109
111
 
110
- condition.or Condition.new(2, '==', 1)
112
+ condition.or(Condition.new(2, '==', 1))
111
113
 
112
- assert_equal false, condition.evaluate
114
+ assert_equal(false, condition.evaluate)
113
115
 
114
- condition.or Condition.new(1, '==', 1)
116
+ condition.or(Condition.new(1, '==', 1))
115
117
 
116
- assert_equal true, condition.evaluate
118
+ assert_equal(true, condition.evaluate)
117
119
  end
118
120
 
119
121
  def test_and_condition
120
122
  condition = Condition.new(1, '==', 1)
121
123
 
122
- assert_equal true, condition.evaluate
124
+ assert_equal(true, condition.evaluate)
123
125
 
124
- condition.and Condition.new(2, '==', 2)
126
+ condition.and(Condition.new(2, '==', 2))
125
127
 
126
- assert_equal true, condition.evaluate
128
+ assert_equal(true, condition.evaluate)
127
129
 
128
- condition.and Condition.new(2, '==', 1)
130
+ condition.and(Condition.new(2, '==', 1))
129
131
 
130
- assert_equal false, condition.evaluate
132
+ assert_equal(false, condition.evaluate)
131
133
  end
132
134
 
133
135
  def test_should_allow_custom_proc_operator
134
- Condition.operators['starts_with'] = proc { |cond, left, right| left =~ %r{^#{right}} }
136
+ Condition.operators['starts_with'] = proc { |_cond, left, right| left =~ /^#{right}/ }
135
137
 
136
- assert_evaluates_true 'bob', 'starts_with', 'b'
137
- assert_evaluates_false 'bob', 'starts_with', 'o'
138
+ assert_evaluates_true('bob', 'starts_with', 'b')
139
+ assert_evaluates_false('bob', 'starts_with', 'o')
138
140
  ensure
139
- Condition.operators.delete 'starts_with'
141
+ Condition.operators.delete('starts_with')
140
142
  end
141
143
 
142
144
  def test_left_or_right_may_contain_operators
143
- @context = Liquid::Context.new
145
+ @context = Liquid::Context.new
144
146
  @context['one'] = @context['another'] = "gnomeslab-and-or-liquid"
145
147
 
146
- assert_evaluates_true VariableLookup.new("one"), '==', VariableLookup.new("another")
148
+ assert_evaluates_true(VariableLookup.new("one"), '==', VariableLookup.new("another"))
147
149
  end
148
150
 
149
151
  private
150
152
 
151
153
  def assert_evaluates_true(left, op, right)
152
- assert Condition.new(left, op, right).evaluate(@context),
153
- "Evaluated false: #{left} #{op} #{right}"
154
+ assert(Condition.new(left, op, right).evaluate(@context),
155
+ "Evaluated false: #{left} #{op} #{right}")
154
156
  end
155
157
 
156
158
  def assert_evaluates_false(left, op, right)
157
- assert !Condition.new(left, op, right).evaluate(@context),
158
- "Evaluated true: #{left} #{op} #{right}"
159
+ assert(!Condition.new(left, op, right).evaluate(@context),
160
+ "Evaluated true: #{left} #{op} #{right}")
159
161
  end
160
162
 
161
163
  def assert_evaluates_argument_error(left, op, right)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class FileSystemUnitTest < Minitest::Test
@@ -11,8 +13,8 @@ class FileSystemUnitTest < Minitest::Test
11
13
 
12
14
  def test_local
13
15
  file_system = Liquid::LocalFileSystem.new("/some/path")
14
- assert_equal "/some/path/_mypartial.liquid", file_system.full_path("mypartial")
15
- assert_equal "/some/path/dir/_mypartial.liquid", file_system.full_path("dir/mypartial")
16
+ assert_equal("/some/path/_mypartial.liquid", file_system.full_path("mypartial"))
17
+ assert_equal("/some/path/dir/_mypartial.liquid", file_system.full_path("dir/mypartial"))
16
18
 
17
19
  assert_raises(FileSystemError) do
18
20
  file_system.full_path("../dir/mypartial")
@@ -29,7 +31,7 @@ class FileSystemUnitTest < Minitest::Test
29
31
 
30
32
  def test_custom_template_filename_patterns
31
33
  file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html")
32
- assert_equal "/some/path/mypartial.html", file_system.full_path("mypartial")
33
- assert_equal "/some/path/dir/mypartial.html", file_system.full_path("dir/mypartial")
34
+ assert_equal("/some/path/mypartial.html", file_system.full_path("mypartial"))
35
+ assert_equal("/some/path/dir/mypartial.html", file_system.full_path("dir/mypartial"))
34
36
  end
35
37
  end # FileSystemTest
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class I18nUnitTest < Minitest::Test
@@ -8,15 +10,15 @@ class I18nUnitTest < Minitest::Test
8
10
  end
9
11
 
10
12
  def test_simple_translate_string
11
- assert_equal "less is more", @i18n.translate("simple")
13
+ assert_equal("less is more", @i18n.translate("simple"))
12
14
  end
13
15
 
14
16
  def test_nested_translate_string
15
- assert_equal "something wasn't right", @i18n.translate("errors.syntax.oops")
17
+ assert_equal("something wasn't right", @i18n.translate("errors.syntax.oops"))
16
18
  end
17
19
 
18
20
  def test_single_string_interpolation
19
- assert_equal "something different", @i18n.translate("whatever", something: "different")
21
+ assert_equal("something different", @i18n.translate("whatever", something: "different"))
20
22
  end
21
23
 
22
24
  # def test_raises_translation_error_on_undefined_interpolation_key
@@ -26,12 +28,12 @@ class I18nUnitTest < Minitest::Test
26
28
  # end
27
29
 
28
30
  def test_raises_unknown_translation
29
- assert_raises I18n::TranslationError do
31
+ assert_raises(I18n::TranslationError) do
30
32
  @i18n.translate("doesnt_exist")
31
33
  end
32
34
  end
33
35
 
34
36
  def test_sets_default_path_to_en
35
- assert_equal I18n::DEFAULT_LOCALE, I18n.new.path
37
+ assert_equal(I18n::DEFAULT_LOCALE, I18n.new.path)
36
38
  end
37
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class LexerUnitTest < Minitest::Test
@@ -5,42 +7,42 @@ class LexerUnitTest < Minitest::Test
5
7
 
6
8
  def test_strings
7
9
  tokens = Lexer.new(%( 'this is a test""' "wat 'lol'")).tokenize
8
- assert_equal [[:string, %('this is a test""')], [:string, %("wat 'lol'")], [:end_of_string]], tokens
10
+ assert_equal([[:string, %('this is a test""')], [:string, %("wat 'lol'")], [:end_of_string]], tokens)
9
11
  end
10
12
 
11
13
  def test_integer
12
14
  tokens = Lexer.new('hi 50').tokenize
13
- assert_equal [[:id, 'hi'], [:number, '50'], [:end_of_string]], tokens
15
+ assert_equal([[:id, 'hi'], [:number, '50'], [:end_of_string]], tokens)
14
16
  end
15
17
 
16
18
  def test_float
17
19
  tokens = Lexer.new('hi 5.0').tokenize
18
- assert_equal [[:id, 'hi'], [:number, '5.0'], [:end_of_string]], tokens
20
+ assert_equal([[:id, 'hi'], [:number, '5.0'], [:end_of_string]], tokens)
19
21
  end
20
22
 
21
23
  def test_comparison
22
24
  tokens = Lexer.new('== <> contains ').tokenize
23
- assert_equal [[:comparison, '=='], [:comparison, '<>'], [:comparison, 'contains'], [:end_of_string]], tokens
25
+ assert_equal([[:comparison, '=='], [:comparison, '<>'], [:comparison, 'contains'], [:end_of_string]], tokens)
24
26
  end
25
27
 
26
28
  def test_specials
27
29
  tokens = Lexer.new('| .:').tokenize
28
- assert_equal [[:pipe, '|'], [:dot, '.'], [:colon, ':'], [:end_of_string]], tokens
30
+ assert_equal([[:pipe, '|'], [:dot, '.'], [:colon, ':'], [:end_of_string]], tokens)
29
31
  tokens = Lexer.new('[,]').tokenize
30
- assert_equal [[:open_square, '['], [:comma, ','], [:close_square, ']'], [:end_of_string]], tokens
32
+ assert_equal([[:open_square, '['], [:comma, ','], [:close_square, ']'], [:end_of_string]], tokens)
31
33
  end
32
34
 
33
35
  def test_fancy_identifiers
34
36
  tokens = Lexer.new('hi five?').tokenize
35
- assert_equal [[:id, 'hi'], [:id, 'five?'], [:end_of_string]], tokens
37
+ assert_equal([[:id, 'hi'], [:id, 'five?'], [:end_of_string]], tokens)
36
38
 
37
39
  tokens = Lexer.new('2foo').tokenize
38
- assert_equal [[:number, '2'], [:id, 'foo'], [:end_of_string]], tokens
40
+ assert_equal([[:number, '2'], [:id, 'foo'], [:end_of_string]], tokens)
39
41
  end
40
42
 
41
43
  def test_whitespace
42
44
  tokens = Lexer.new("five|\n\t ==").tokenize
43
- assert_equal [[:id, 'five'], [:pipe, '|'], [:comparison, '=='], [:end_of_string]], tokens
45
+ assert_equal([[:id, 'five'], [:pipe, '|'], [:comparison, '=='], [:end_of_string]], tokens)
44
46
  end
45
47
 
46
48
  def test_unexpected_character
@@ -26,6 +26,13 @@ class ParseTreeVisitorTest < Minitest::Test
26
26
  )
27
27
  end
28
28
 
29
+ def test_echo
30
+ assert_equal(
31
+ ["test"],
32
+ visit(%({% echo test %}))
33
+ )
34
+ end
35
+
29
36
  def test_if_condition
30
37
  assert_equal(
31
38
  ["test"],
@@ -152,6 +159,13 @@ class ParseTreeVisitorTest < Minitest::Test
152
159
  )
153
160
  end
154
161
 
162
+ def test_for_range
163
+ assert_equal(
164
+ ["test"],
165
+ visit(%({% for x in (1..test) %}{% endfor %}))
166
+ )
167
+ end
168
+
155
169
  def test_tablerow_in
156
170
  assert_equal(
157
171
  ["test"],
@@ -227,7 +241,7 @@ class ParseTreeVisitorTest < Minitest::Test
227
241
  [[nil, [
228
242
  [nil, [[nil, [["other", []]]]]],
229
243
  ["test", []],
230
- ["xs", []]
244
+ ["xs", []],
231
245
  ]]],
232
246
  traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit
233
247
  )
@@ -238,7 +252,7 @@ class ParseTreeVisitorTest < Minitest::Test
238
252
  def traversal(template)
239
253
  ParseTreeVisitor
240
254
  .for(Template.parse(template).root)
241
- .add_callback_for(VariableLookup, &:name)
255
+ .add_callback_for(VariableLookup) { |node| node.name } # rubocop:disable Style/SymbolProc
242
256
  end
243
257
 
244
258
  def visit(template)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class ParserUnitTest < Minitest::Test
@@ -5,72 +7,72 @@ class ParserUnitTest < Minitest::Test
5
7
 
6
8
  def test_consume
7
9
  p = Parser.new("wat: 7")
8
- assert_equal 'wat', p.consume(:id)
9
- assert_equal ':', p.consume(:colon)
10
- assert_equal '7', p.consume(:number)
10
+ assert_equal('wat', p.consume(:id))
11
+ assert_equal(':', p.consume(:colon))
12
+ assert_equal('7', p.consume(:number))
11
13
  end
12
14
 
13
15
  def test_jump
14
16
  p = Parser.new("wat: 7")
15
17
  p.jump(2)
16
- assert_equal '7', p.consume(:number)
18
+ assert_equal('7', p.consume(:number))
17
19
  end
18
20
 
19
21
  def test_consume?
20
22
  p = Parser.new("wat: 7")
21
- assert_equal 'wat', p.consume?(:id)
22
- assert_equal false, p.consume?(:dot)
23
- assert_equal ':', p.consume(:colon)
24
- assert_equal '7', p.consume?(:number)
23
+ assert_equal('wat', p.consume?(:id))
24
+ assert_equal(false, p.consume?(:dot))
25
+ assert_equal(':', p.consume(:colon))
26
+ assert_equal('7', p.consume?(:number))
25
27
  end
26
28
 
27
29
  def test_id?
28
30
  p = Parser.new("wat 6 Peter Hegemon")
29
- assert_equal 'wat', p.id?('wat')
30
- assert_equal false, p.id?('endgame')
31
- assert_equal '6', p.consume(:number)
32
- assert_equal 'Peter', p.id?('Peter')
33
- assert_equal false, p.id?('Achilles')
31
+ assert_equal('wat', p.id?('wat'))
32
+ assert_equal(false, p.id?('endgame'))
33
+ assert_equal('6', p.consume(:number))
34
+ assert_equal('Peter', p.id?('Peter'))
35
+ assert_equal(false, p.id?('Achilles'))
34
36
  end
35
37
 
36
38
  def test_look
37
39
  p = Parser.new("wat 6 Peter Hegemon")
38
- assert_equal true, p.look(:id)
39
- assert_equal 'wat', p.consume(:id)
40
- assert_equal false, p.look(:comparison)
41
- assert_equal true, p.look(:number)
42
- assert_equal true, p.look(:id, 1)
43
- assert_equal false, p.look(:number, 1)
40
+ assert_equal(true, p.look(:id))
41
+ assert_equal('wat', p.consume(:id))
42
+ assert_equal(false, p.look(:comparison))
43
+ assert_equal(true, p.look(:number))
44
+ assert_equal(true, p.look(:id, 1))
45
+ assert_equal(false, p.look(:number, 1))
44
46
  end
45
47
 
46
48
  def test_expressions
47
49
  p = Parser.new("hi.there hi?[5].there? hi.there.bob")
48
- assert_equal 'hi.there', p.expression
49
- assert_equal 'hi?[5].there?', p.expression
50
- assert_equal 'hi.there.bob', p.expression
50
+ assert_equal('hi.there', p.expression)
51
+ assert_equal('hi?[5].there?', p.expression)
52
+ assert_equal('hi.there.bob', p.expression)
51
53
 
52
54
  p = Parser.new("567 6.0 'lol' \"wut\"")
53
- assert_equal '567', p.expression
54
- assert_equal '6.0', p.expression
55
- assert_equal "'lol'", p.expression
56
- assert_equal '"wut"', p.expression
55
+ assert_equal('567', p.expression)
56
+ assert_equal('6.0', p.expression)
57
+ assert_equal("'lol'", p.expression)
58
+ assert_equal('"wut"', p.expression)
57
59
  end
58
60
 
59
61
  def test_ranges
60
62
  p = Parser.new("(5..7) (1.5..9.6) (young..old) (hi[5].wat..old)")
61
- assert_equal '(5..7)', p.expression
62
- assert_equal '(1.5..9.6)', p.expression
63
- assert_equal '(young..old)', p.expression
64
- assert_equal '(hi[5].wat..old)', p.expression
63
+ assert_equal('(5..7)', p.expression)
64
+ assert_equal('(1.5..9.6)', p.expression)
65
+ assert_equal('(young..old)', p.expression)
66
+ assert_equal('(hi[5].wat..old)', p.expression)
65
67
  end
66
68
 
67
69
  def test_arguments
68
70
  p = Parser.new("filter: hi.there[5], keyarg: 7")
69
- assert_equal 'filter', p.consume(:id)
70
- assert_equal ':', p.consume(:colon)
71
- assert_equal 'hi.there[5]', p.argument
72
- assert_equal ',', p.consume(:comma)
73
- assert_equal 'keyarg: 7', p.argument
71
+ assert_equal('filter', p.consume(:id))
72
+ assert_equal(':', p.consume(:colon))
73
+ assert_equal('hi.there[5]', p.argument)
74
+ assert_equal(',', p.consume(:comma))
75
+ assert_equal('keyarg: 7', p.argument)
74
76
  end
75
77
 
76
78
  def test_invalid_expression
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class PartialCacheUnitTest < Minitest::Test
6
+ def test_uses_the_file_system_register_if_present
7
+ context = Liquid::Context.build(
8
+ registers: {
9
+ file_system: StubFileSystem.new('my_partial' => 'my partial body'),
10
+ }
11
+ )
12
+
13
+ partial = Liquid::PartialCache.load(
14
+ 'my_partial',
15
+ context: context,
16
+ parse_context: Liquid::ParseContext.new
17
+ )
18
+
19
+ assert_equal('my partial body', partial.render)
20
+ end
21
+
22
+ def test_reads_from_the_file_system_only_once_per_file
23
+ file_system = StubFileSystem.new('my_partial' => 'some partial body')
24
+ context = Liquid::Context.build(
25
+ registers: { file_system: file_system }
26
+ )
27
+
28
+ 2.times do
29
+ Liquid::PartialCache.load(
30
+ 'my_partial',
31
+ context: context,
32
+ parse_context: Liquid::ParseContext.new
33
+ )
34
+ end
35
+
36
+ assert_equal(1, file_system.file_read_count)
37
+ end
38
+
39
+ def test_cache_state_is_stored_per_context
40
+ parse_context = Liquid::ParseContext.new
41
+ shared_file_system = StubFileSystem.new(
42
+ 'my_partial' => 'my shared value'
43
+ )
44
+ context_one = Liquid::Context.build(
45
+ registers: {
46
+ file_system: shared_file_system,
47
+ }
48
+ )
49
+ context_two = Liquid::Context.build(
50
+ registers: {
51
+ file_system: shared_file_system,
52
+ }
53
+ )
54
+
55
+ 2.times do
56
+ Liquid::PartialCache.load(
57
+ 'my_partial',
58
+ context: context_one,
59
+ parse_context: parse_context
60
+ )
61
+ end
62
+
63
+ Liquid::PartialCache.load(
64
+ 'my_partial',
65
+ context: context_two,
66
+ parse_context: parse_context
67
+ )
68
+
69
+ assert_equal(2, shared_file_system.file_read_count)
70
+ end
71
+
72
+ def test_cache_is_not_broken_when_a_different_parse_context_is_used
73
+ file_system = StubFileSystem.new('my_partial' => 'some partial body')
74
+ context = Liquid::Context.build(
75
+ registers: { file_system: file_system }
76
+ )
77
+
78
+ Liquid::PartialCache.load(
79
+ 'my_partial',
80
+ context: context,
81
+ parse_context: Liquid::ParseContext.new(my_key: 'value one')
82
+ )
83
+ Liquid::PartialCache.load(
84
+ 'my_partial',
85
+ context: context,
86
+ parse_context: Liquid::ParseContext.new(my_key: 'value two')
87
+ )
88
+
89
+ # Technically what we care about is that the file was parsed twice,
90
+ # but measuring file reads is an OK proxy for this.
91
+ assert_equal(1, file_system.file_read_count)
92
+ end
93
+
94
+ def test_uses_default_template_factory_when_no_template_factory_found_in_register
95
+ context = Liquid::Context.build(
96
+ registers: {
97
+ file_system: StubFileSystem.new('my_partial' => 'my partial body'),
98
+ }
99
+ )
100
+
101
+ partial = Liquid::PartialCache.load(
102
+ 'my_partial',
103
+ context: context,
104
+ parse_context: Liquid::ParseContext.new
105
+ )
106
+
107
+ assert_equal('my partial body', partial.render)
108
+ end
109
+
110
+ def test_uses_template_factory_register_if_present
111
+ template_factory = StubTemplateFactory.new
112
+ context = Liquid::Context.build(
113
+ registers: {
114
+ file_system: StubFileSystem.new('my_partial' => 'my partial body'),
115
+ template_factory: template_factory,
116
+ }
117
+ )
118
+
119
+ partial = Liquid::PartialCache.load(
120
+ 'my_partial',
121
+ context: context,
122
+ parse_context: Liquid::ParseContext.new
123
+ )
124
+
125
+ assert_equal('my partial body', partial.render)
126
+ assert_equal(1, template_factory.count)
127
+ end
128
+ end