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,44 +1,46 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class RegexpUnitTest < Minitest::Test
4
6
  include Liquid
5
7
 
6
8
  def test_empty
7
- assert_equal [], ''.scan(QuotedFragment)
9
+ assert_equal([], ''.scan(QuotedFragment))
8
10
  end
9
11
 
10
12
  def test_quote
11
- assert_equal ['"arg 1"'], '"arg 1"'.scan(QuotedFragment)
13
+ assert_equal(['"arg 1"'], '"arg 1"'.scan(QuotedFragment))
12
14
  end
13
15
 
14
16
  def test_words
15
- assert_equal ['arg1', 'arg2'], 'arg1 arg2'.scan(QuotedFragment)
17
+ assert_equal(['arg1', 'arg2'], 'arg1 arg2'.scan(QuotedFragment))
16
18
  end
17
19
 
18
20
  def test_tags
19
- assert_equal ['<tr>', '</tr>'], '<tr> </tr>'.scan(QuotedFragment)
20
- assert_equal ['<tr></tr>'], '<tr></tr>'.scan(QuotedFragment)
21
- assert_equal ['<style', 'class="hello">', '</style>'], %(<style class="hello">' </style>).scan(QuotedFragment)
21
+ assert_equal(['<tr>', '</tr>'], '<tr> </tr>'.scan(QuotedFragment))
22
+ assert_equal(['<tr></tr>'], '<tr></tr>'.scan(QuotedFragment))
23
+ assert_equal(['<style', 'class="hello">', '</style>'], %(<style class="hello">' </style>).scan(QuotedFragment))
22
24
  end
23
25
 
24
26
  def test_double_quoted_words
25
- assert_equal ['arg1', 'arg2', '"arg 3"'], 'arg1 arg2 "arg 3"'.scan(QuotedFragment)
27
+ assert_equal(['arg1', 'arg2', '"arg 3"'], 'arg1 arg2 "arg 3"'.scan(QuotedFragment))
26
28
  end
27
29
 
28
30
  def test_single_quoted_words
29
- assert_equal ['arg1', 'arg2', "'arg 3'"], 'arg1 arg2 \'arg 3\''.scan(QuotedFragment)
31
+ assert_equal(['arg1', 'arg2', "'arg 3'"], 'arg1 arg2 \'arg 3\''.scan(QuotedFragment))
30
32
  end
31
33
 
32
34
  def test_quoted_words_in_the_middle
33
- assert_equal ['arg1', 'arg2', '"arg 3"', 'arg4'], 'arg1 arg2 "arg 3" arg4 '.scan(QuotedFragment)
35
+ assert_equal(['arg1', 'arg2', '"arg 3"', 'arg4'], 'arg1 arg2 "arg 3" arg4 '.scan(QuotedFragment))
34
36
  end
35
37
 
36
38
  def test_variable_parser
37
- assert_equal ['var'], 'var'.scan(VariableParser)
38
- assert_equal ['var', 'method'], 'var.method'.scan(VariableParser)
39
- assert_equal ['var', '[method]'], 'var[method]'.scan(VariableParser)
40
- assert_equal ['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser)
41
- assert_equal ['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser)
42
- assert_equal ['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser)
39
+ assert_equal(['var'], 'var'.scan(VariableParser))
40
+ assert_equal(['var', 'method'], 'var.method'.scan(VariableParser))
41
+ assert_equal(['var', '[method]'], 'var[method]'.scan(VariableParser))
42
+ assert_equal(['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser))
43
+ assert_equal(['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser))
44
+ assert_equal(['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser))
43
45
  end
44
46
  end # RegexpTest
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class StaticRegistersUnitTest < Minitest::Test
6
+ include Liquid
7
+
8
+ def test_set
9
+ static_register = StaticRegisters.new(a: 1, b: 2)
10
+ static_register[:b] = 22
11
+ static_register[:c] = 33
12
+
13
+ assert_equal(1, static_register[:a])
14
+ assert_equal(22, static_register[:b])
15
+ assert_equal(33, static_register[:c])
16
+ end
17
+
18
+ def test_get_missing_key
19
+ static_register = StaticRegisters.new
20
+
21
+ assert_nil(static_register[:missing])
22
+ end
23
+
24
+ def test_delete
25
+ static_register = StaticRegisters.new(a: 1, b: 2)
26
+ static_register[:b] = 22
27
+ static_register[:c] = 33
28
+
29
+ assert_nil(static_register.delete(:a))
30
+
31
+ assert_equal(22, static_register.delete(:b))
32
+
33
+ assert_equal(33, static_register.delete(:c))
34
+ assert_nil(static_register[:c])
35
+
36
+ assert_nil(static_register.delete(:d))
37
+ end
38
+
39
+ def test_fetch
40
+ static_register = StaticRegisters.new(a: 1, b: 2)
41
+ static_register[:b] = 22
42
+ static_register[:c] = 33
43
+
44
+ assert_equal(1, static_register.fetch(:a))
45
+ assert_equal(1, static_register.fetch(:a, "default"))
46
+ assert_equal(22, static_register.fetch(:b))
47
+ assert_equal(22, static_register.fetch(:b, "default"))
48
+ assert_equal(33, static_register.fetch(:c))
49
+ assert_equal(33, static_register.fetch(:c, "default"))
50
+
51
+ assert_raises(KeyError) do
52
+ static_register.fetch(:d)
53
+ end
54
+ assert_equal("default", static_register.fetch(:d, "default"))
55
+
56
+ result = static_register.fetch(:d) { "default" }
57
+ assert_equal("default", result)
58
+
59
+ result = static_register.fetch(:d, "default 1") { "default 2" }
60
+ assert_equal("default 2", result)
61
+ end
62
+
63
+ def test_key
64
+ static_register = StaticRegisters.new(a: 1, b: 2)
65
+ static_register[:b] = 22
66
+ static_register[:c] = 33
67
+
68
+ assert_equal(true, static_register.key?(:a))
69
+ assert_equal(true, static_register.key?(:b))
70
+ assert_equal(true, static_register.key?(:c))
71
+ assert_equal(false, static_register.key?(:d))
72
+ end
73
+
74
+ def test_static_register_can_be_frozen
75
+ static_register = StaticRegisters.new(a: 1)
76
+
77
+ static_register.static.freeze
78
+
79
+ assert_raises(RuntimeError) do
80
+ static_register.static[:a] = "foo"
81
+ end
82
+
83
+ assert_raises(RuntimeError) do
84
+ static_register.static[:b] = "foo"
85
+ end
86
+
87
+ assert_raises(RuntimeError) do
88
+ static_register.static.delete(:a)
89
+ end
90
+
91
+ assert_raises(RuntimeError) do
92
+ static_register.static.delete(:c)
93
+ end
94
+ end
95
+
96
+ def test_new_static_retains_static
97
+ static_register = StaticRegisters.new(a: 1, b: 2)
98
+ static_register[:b] = 22
99
+ static_register[:c] = 33
100
+
101
+ new_static_register = StaticRegisters.new(static_register)
102
+ new_static_register[:b] = 222
103
+
104
+ newest_static_register = StaticRegisters.new(new_static_register)
105
+ newest_static_register[:c] = 333
106
+
107
+ assert_equal(1, static_register[:a])
108
+ assert_equal(22, static_register[:b])
109
+ assert_equal(33, static_register[:c])
110
+
111
+ assert_equal(1, new_static_register[:a])
112
+ assert_equal(222, new_static_register[:b])
113
+ assert_nil(new_static_register[:c])
114
+
115
+ assert_equal(1, newest_static_register[:a])
116
+ assert_equal(2, newest_static_register[:b])
117
+ assert_equal(333, newest_static_register[:c])
118
+ end
119
+
120
+ def test_multiple_instances_are_unique
121
+ static_register_1 = StaticRegisters.new(a: 1, b: 2)
122
+ static_register_1[:b] = 22
123
+ static_register_1[:c] = 33
124
+
125
+ static_register_2 = StaticRegisters.new(a: 10, b: 20)
126
+ static_register_2[:b] = 220
127
+ static_register_2[:c] = 330
128
+
129
+ assert_equal({ a: 1, b: 2 }, static_register_1.static)
130
+ assert_equal(1, static_register_1[:a])
131
+ assert_equal(22, static_register_1[:b])
132
+ assert_equal(33, static_register_1[:c])
133
+
134
+ assert_equal({ a: 10, b: 20 }, static_register_2.static)
135
+ assert_equal(10, static_register_2[:a])
136
+ assert_equal(220, static_register_2[:b])
137
+ assert_equal(330, static_register_2[:c])
138
+ end
139
+
140
+ def test_initialization_reused_static_same_memory_object
141
+ static_register_1 = StaticRegisters.new(a: 1, b: 2)
142
+ static_register_1[:b] = 22
143
+ static_register_1[:c] = 33
144
+
145
+ static_register_2 = StaticRegisters.new(static_register_1)
146
+
147
+ assert_equal(1, static_register_2[:a])
148
+ assert_equal(2, static_register_2[:b])
149
+ assert_nil(static_register_2[:c])
150
+
151
+ static_register_1.static[:b] = 222
152
+ static_register_1.static[:c] = 333
153
+
154
+ assert_same(static_register_1.static, static_register_2.static)
155
+ end
156
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class StrainerFactoryUnitTest < Minitest::Test
6
+ include Liquid
7
+
8
+ module AccessScopeFilters
9
+ def public_filter
10
+ "public"
11
+ end
12
+
13
+ def private_filter
14
+ "private"
15
+ end
16
+ private :private_filter
17
+ end
18
+
19
+ StrainerFactory.add_global_filter(AccessScopeFilters)
20
+
21
+ module LateAddedFilter
22
+ def late_added_filter(_input)
23
+ "filtered"
24
+ end
25
+ end
26
+
27
+ def setup
28
+ @context = Context.build
29
+ end
30
+
31
+ def test_strainer
32
+ strainer = StrainerFactory.create(@context)
33
+ assert_equal(5, strainer.invoke('size', 'input'))
34
+ assert_equal("public", strainer.invoke("public_filter"))
35
+ end
36
+
37
+ def test_stainer_raises_argument_error
38
+ strainer = StrainerFactory.create(@context)
39
+ assert_raises(Liquid::ArgumentError) do
40
+ strainer.invoke("public_filter", 1)
41
+ end
42
+ end
43
+
44
+ def test_stainer_argument_error_contains_backtrace
45
+ strainer = StrainerFactory.create(@context)
46
+
47
+ exception = assert_raises(Liquid::ArgumentError) do
48
+ strainer.invoke("public_filter", 1)
49
+ end
50
+
51
+ assert_match(
52
+ /\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
53
+ exception.message
54
+ )
55
+ assert_equal(exception.backtrace[0].split(':')[0], __FILE__)
56
+ end
57
+
58
+ def test_strainer_only_invokes_public_filter_methods
59
+ strainer = StrainerFactory.create(@context)
60
+ assert_equal(false, strainer.class.invokable?('__test__'))
61
+ assert_equal(false, strainer.class.invokable?('test'))
62
+ assert_equal(false, strainer.class.invokable?('instance_eval'))
63
+ assert_equal(false, strainer.class.invokable?('__send__'))
64
+ assert_equal(true, strainer.class.invokable?('size')) # from the standard lib
65
+ end
66
+
67
+ def test_strainer_returns_nil_if_no_filter_method_found
68
+ strainer = StrainerFactory.create(@context)
69
+ assert_nil(strainer.invoke("private_filter"))
70
+ assert_nil(strainer.invoke("undef_the_filter"))
71
+ end
72
+
73
+ def test_strainer_returns_first_argument_if_no_method_and_arguments_given
74
+ strainer = StrainerFactory.create(@context)
75
+ assert_equal("password", strainer.invoke("undef_the_method", "password"))
76
+ end
77
+
78
+ def test_strainer_only_allows_methods_defined_in_filters
79
+ strainer = StrainerFactory.create(@context)
80
+ assert_equal("1 + 1", strainer.invoke("instance_eval", "1 + 1"))
81
+ assert_equal("puts", strainer.invoke("__send__", "puts", "Hi Mom"))
82
+ assert_equal("has_method?", strainer.invoke("invoke", "has_method?", "invoke"))
83
+ end
84
+
85
+ def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation
86
+ a = Module.new
87
+ b = Module.new
88
+ strainer = StrainerFactory.create(@context, [a, b])
89
+ assert_kind_of(StrainerTemplate, strainer)
90
+ assert_kind_of(a, strainer)
91
+ assert_kind_of(b, strainer)
92
+ assert_kind_of(Liquid::StandardFilters, strainer)
93
+ end
94
+
95
+ def test_add_global_filter_clears_cache
96
+ assert_equal('input', StrainerFactory.create(@context).invoke('late_added_filter', 'input'))
97
+ StrainerFactory.add_global_filter(LateAddedFilter)
98
+ assert_equal('filtered', StrainerFactory.create(nil).invoke('late_added_filter', 'input'))
99
+ end
100
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class StrainerTemplateUnitTest < Minitest::Test
6
+ include Liquid
7
+
8
+ def test_add_filter_when_wrong_filter_class
9
+ c = Context.new
10
+ s = c.strainer
11
+ wrong_filter = ->(v) { v.reverse }
12
+
13
+ exception = assert_raises(TypeError) do
14
+ s.class.add_filter(wrong_filter)
15
+ end
16
+ assert_equal(exception.message, "wrong argument type Proc (expected Module)")
17
+ end
18
+
19
+ module PrivateMethodOverrideFilter
20
+ private
21
+
22
+ def public_filter
23
+ "overriden as private"
24
+ end
25
+ end
26
+
27
+ def test_add_filter_raises_when_module_privately_overrides_registered_public_methods
28
+ strainer = Context.new.strainer
29
+
30
+ error = assert_raises(Liquid::MethodOverrideError) do
31
+ strainer.class.add_filter(PrivateMethodOverrideFilter)
32
+ end
33
+ assert_equal('Liquid error: Filter overrides registered public methods as non public: public_filter', error.message)
34
+ end
35
+
36
+ module ProtectedMethodOverrideFilter
37
+ protected
38
+
39
+ def public_filter
40
+ "overriden as protected"
41
+ end
42
+ end
43
+
44
+ def test_add_filter_raises_when_module_overrides_registered_public_method_as_protected
45
+ strainer = Context.new.strainer
46
+
47
+ error = assert_raises(Liquid::MethodOverrideError) do
48
+ strainer.class.add_filter(ProtectedMethodOverrideFilter)
49
+ end
50
+ assert_equal('Liquid error: Filter overrides registered public methods as non public: public_filter', error.message)
51
+ end
52
+
53
+ module PublicMethodOverrideFilter
54
+ def public_filter
55
+ "public"
56
+ end
57
+ end
58
+
59
+ def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
60
+ strainer = Context.new.strainer
61
+ with_global_filter do
62
+ strainer.class.add_filter(PublicMethodOverrideFilter)
63
+ assert(strainer.class.send(:filter_methods).include?('public_filter'))
64
+ end
65
+ end
66
+
67
+ def test_add_filter_does_not_include_already_included_module
68
+ mod = Module.new do
69
+ class << self
70
+ attr_accessor :include_count
71
+ def included(_mod)
72
+ self.include_count += 1
73
+ end
74
+ end
75
+ self.include_count = 0
76
+ end
77
+ strainer = Context.new.strainer
78
+ strainer.class.add_filter(mod)
79
+ strainer.class.add_filter(mod)
80
+ assert_equal(1, mod.include_count)
81
+ end
82
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class TagUnitTest < Minitest::Test
@@ -5,8 +7,8 @@ class TagUnitTest < Minitest::Test
5
7
 
6
8
  def test_tag
7
9
  tag = Tag.parse('tag', "", Tokenizer.new(""), ParseContext.new)
8
- assert_equal 'liquid::tag', tag.name
9
- assert_equal '', tag.render(Context.new)
10
+ assert_equal('liquid::tag', tag.name)
11
+ assert_equal('', tag.render(Context.new))
10
12
  end
11
13
 
12
14
  def test_return_raw_text_of_tag
@@ -16,6 +18,6 @@ class TagUnitTest < Minitest::Test
16
18
 
17
19
  def test_tag_name_should_return_name_of_the_tag
18
20
  tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
19
- assert_equal 'some_tag', tag.tag_name
21
+ assert_equal('some_tag', tag.tag_name)
20
22
  end
21
23
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class CaseTagUnitTest < Minitest::Test
@@ -5,6 +7,6 @@ class CaseTagUnitTest < Minitest::Test
5
7
 
6
8
  def test_case_nodelist
7
9
  template = Liquid::Template.parse('{% case var %}{% when true %}WHEN{% else %}ELSE{% endcase %}')
8
- assert_equal ['WHEN', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten
10
+ assert_equal(['WHEN', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten)
9
11
  end
10
12
  end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class ForTagUnitTest < Minitest::Test
4
6
  def test_for_nodelist
5
7
  template = Liquid::Template.parse('{% for item in items %}FOR{% endfor %}')
6
- assert_equal ['FOR'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten
8
+ assert_equal(['FOR'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten)
7
9
  end
8
10
 
9
11
  def test_for_else_nodelist
10
12
  template = Liquid::Template.parse('{% for item in items %}FOR{% else %}ELSE{% endfor %}')
11
- assert_equal ['FOR', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten
13
+ assert_equal(['FOR', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten)
12
14
  end
13
15
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class IfTagUnitTest < Minitest::Test
4
6
  def test_if_nodelist
5
7
  template = Liquid::Template.parse('{% if true %}IF{% else %}ELSE{% endif %}')
6
- assert_equal ['IF', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten
8
+ assert_equal(['IF', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten)
7
9
  end
8
10
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TemplateFactoryUnitTest < Minitest::Test
6
+ include Liquid
7
+
8
+ def test_for_returns_liquid_template_instance
9
+ template = TemplateFactory.new.for("anything")
10
+ assert_instance_of(Liquid::Template, template)
11
+ end
12
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class TemplateUnitTest < Minitest::Test
@@ -6,7 +8,7 @@ class TemplateUnitTest < Minitest::Test
6
8
  def test_sets_default_localization_in_document
7
9
  t = Template.new
8
10
  t.parse('{%comment%}{%endcomment%}')
9
- assert_instance_of I18n, t.root.nodelist[0].options[:locale]
11
+ assert_instance_of(I18n, t.root.nodelist[0].options[:locale])
10
12
  end
11
13
 
12
14
  def test_sets_default_localization_in_context_with_quick_initialization
@@ -14,13 +16,13 @@ class TemplateUnitTest < Minitest::Test
14
16
  t.parse('{%comment%}{%endcomment%}', locale: I18n.new(fixture("en_locale.yml")))
15
17
 
16
18
  locale = t.root.nodelist[0].options[:locale]
17
- assert_instance_of I18n, locale
18
- assert_equal fixture("en_locale.yml"), locale.path
19
+ assert_instance_of(I18n, locale)
20
+ assert_equal(fixture("en_locale.yml"), locale.path)
19
21
  end
20
22
 
21
23
  def test_with_cache_classes_tags_returns_the_same_class
22
24
  original_cache_setting = Liquid.cache_classes
23
- Liquid.cache_classes = true
25
+ Liquid.cache_classes = true
24
26
 
25
27
  original_klass = Class.new
26
28
  Object.send(:const_set, :CustomTag, original_klass)
@@ -31,7 +33,7 @@ class TemplateUnitTest < Minitest::Test
31
33
  new_klass = Class.new
32
34
  Object.send(:const_set, :CustomTag, new_klass)
33
35
 
34
- assert Template.tags['custom'].equal?(original_klass)
36
+ assert(Template.tags['custom'].equal?(original_klass))
35
37
  ensure
36
38
  Object.send(:remove_const, :CustomTag)
37
39
  Template.tags.delete('custom')
@@ -40,7 +42,7 @@ class TemplateUnitTest < Minitest::Test
40
42
 
41
43
  def test_without_cache_classes_tags_reloads_the_class
42
44
  original_cache_setting = Liquid.cache_classes
43
- Liquid.cache_classes = false
45
+ Liquid.cache_classes = false
44
46
 
45
47
  original_klass = Class.new
46
48
  Object.send(:const_set, :CustomTag, original_klass)
@@ -51,7 +53,7 @@ class TemplateUnitTest < Minitest::Test
51
53
  new_klass = Class.new
52
54
  Object.send(:const_set, :CustomTag, new_klass)
53
55
 
54
- assert Template.tags['custom'].equal?(new_klass)
56
+ assert(Template.tags['custom'].equal?(new_klass))
55
57
  ensure
56
58
  Object.send(:remove_const, :CustomTag)
57
59
  Template.tags.delete('custom')
@@ -62,17 +64,24 @@ class TemplateUnitTest < Minitest::Test
62
64
 
63
65
  def test_tags_delete
64
66
  Template.register_tag('fake', FakeTag)
65
- assert_equal FakeTag, Template.tags['fake']
67
+ assert_equal(FakeTag, Template.tags['fake'])
66
68
 
67
69
  Template.tags.delete('fake')
68
- assert_nil Template.tags['fake']
70
+ assert_nil(Template.tags['fake'])
69
71
  end
70
72
 
71
73
  def test_tags_can_be_looped_over
72
74
  Template.register_tag('fake', FakeTag)
73
75
  result = Template.tags.map { |name, klass| [name, klass] }
74
- assert result.include?(["fake", "TemplateUnitTest::FakeTag"])
76
+ assert(result.include?(["fake", "TemplateUnitTest::FakeTag"]))
75
77
  ensure
76
78
  Template.tags.delete('fake')
77
79
  end
80
+
81
+ class TemplateSubclass < Liquid::Template
82
+ end
83
+
84
+ def test_template_inheritance
85
+ assert_equal("foo", TemplateSubclass.parse("foo").render)
86
+ end
78
87
  end
@@ -1,50 +1,57 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class TokenizerTest < Minitest::Test
4
6
  def test_tokenize_strings
5
- assert_equal [' '], tokenize(' ')
6
- assert_equal ['hello world'], tokenize('hello world')
7
+ assert_equal([' '], tokenize(' '))
8
+ assert_equal(['hello world'], tokenize('hello world'))
7
9
  end
8
10
 
9
11
  def test_tokenize_variables
10
- assert_equal ['{{funk}}'], tokenize('{{funk}}')
11
- assert_equal [' ', '{{funk}}', ' '], tokenize(' {{funk}} ')
12
- assert_equal [' ', '{{funk}}', ' ', '{{so}}', ' ', '{{brother}}', ' '], tokenize(' {{funk}} {{so}} {{brother}} ')
13
- assert_equal [' ', '{{ funk }}', ' '], tokenize(' {{ funk }} ')
12
+ assert_equal(['{{funk}}'], tokenize('{{funk}}'))
13
+ assert_equal([' ', '{{funk}}', ' '], tokenize(' {{funk}} '))
14
+ assert_equal([' ', '{{funk}}', ' ', '{{so}}', ' ', '{{brother}}', ' '], tokenize(' {{funk}} {{so}} {{brother}} '))
15
+ assert_equal([' ', '{{ funk }}', ' '], tokenize(' {{ funk }} '))
14
16
  end
15
17
 
16
18
  def test_tokenize_blocks
17
- assert_equal ['{%comment%}'], tokenize('{%comment%}')
18
- assert_equal [' ', '{%comment%}', ' '], tokenize(' {%comment%} ')
19
+ assert_equal(['{%comment%}'], tokenize('{%comment%}'))
20
+ assert_equal([' ', '{%comment%}', ' '], tokenize(' {%comment%} '))
19
21
 
20
- assert_equal [' ', '{%comment%}', ' ', '{%endcomment%}', ' '], tokenize(' {%comment%} {%endcomment%} ')
21
- assert_equal [' ', '{% comment %}', ' ', '{% endcomment %}', ' '], tokenize(" {% comment %} {% endcomment %} ")
22
+ assert_equal([' ', '{%comment%}', ' ', '{%endcomment%}', ' '], tokenize(' {%comment%} {%endcomment%} '))
23
+ assert_equal([' ', '{% comment %}', ' ', '{% endcomment %}', ' '], tokenize(" {% comment %} {% endcomment %} "))
22
24
  end
23
25
 
24
26
  def test_calculate_line_numbers_per_token_with_profiling
25
- assert_equal [1], tokenize_line_numbers("{{funk}}")
26
- assert_equal [1, 1, 1], tokenize_line_numbers(" {{funk}} ")
27
- assert_equal [1, 2, 2], tokenize_line_numbers("\n{{funk}}\n")
28
- assert_equal [1, 1, 3], tokenize_line_numbers(" {{\n funk \n}} ")
27
+ assert_equal([1], tokenize_line_numbers("{{funk}}"))
28
+ assert_equal([1, 1, 1], tokenize_line_numbers(" {{funk}} "))
29
+ assert_equal([1, 2, 2], tokenize_line_numbers("\n{{funk}}\n"))
30
+ assert_equal([1, 1, 3], tokenize_line_numbers(" {{\n funk \n}} "))
29
31
  end
30
32
 
31
33
  private
32
34
 
35
+ def new_tokenizer(source, parse_context: Liquid::ParseContext.new, start_line_number: nil)
36
+ parse_context.new_tokenizer(source, start_line_number: start_line_number)
37
+ end
38
+
33
39
  def tokenize(source)
34
- tokenizer = Liquid::Tokenizer.new(source)
35
- tokens = []
36
- while t = tokenizer.shift
40
+ tokenizer = new_tokenizer(source)
41
+ tokens = []
42
+ # shift is private in Liquid::C::Tokenizer, since it is only for unit testing
43
+ while (t = tokenizer.send(:shift))
37
44
  tokens << t
38
45
  end
39
46
  tokens
40
47
  end
41
48
 
42
49
  def tokenize_line_numbers(source)
43
- tokenizer = Liquid::Tokenizer.new(source, true)
50
+ tokenizer = new_tokenizer(source, start_line_number: 1)
44
51
  line_numbers = []
45
52
  loop do
46
53
  line_number = tokenizer.line_number
47
- if tokenizer.shift
54
+ if tokenizer.send(:shift)
48
55
  line_numbers << line_number
49
56
  else
50
57
  break