liquid 4.0.3 → 5.4.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +89 -0
  3. data/README.md +10 -4
  4. data/lib/liquid/block.rb +31 -14
  5. data/lib/liquid/block_body.rb +169 -57
  6. data/lib/liquid/condition.rb +48 -21
  7. data/lib/liquid/context.rb +111 -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 +28 -32
  12. data/lib/liquid/extensions.rb +2 -0
  13. data/lib/liquid/file_system.rb +6 -4
  14. data/lib/liquid/forloop_drop.rb +54 -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 +8 -5
  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/registers.rb +51 -0
  28. data/lib/liquid/resource_limits.rb +47 -8
  29. data/lib/liquid/standardfilters.rb +551 -114
  30. data/lib/liquid/strainer_factory.rb +41 -0
  31. data/lib/liquid/strainer_template.rb +62 -0
  32. data/lib/liquid/tablerowloop_drop.rb +64 -5
  33. data/lib/liquid/tag/disableable.rb +22 -0
  34. data/lib/liquid/tag/disabler.rb +21 -0
  35. data/lib/liquid/tag.rb +28 -6
  36. data/lib/liquid/tags/assign.rb +36 -18
  37. data/lib/liquid/tags/break.rb +16 -3
  38. data/lib/liquid/tags/capture.rb +24 -18
  39. data/lib/liquid/tags/case.rb +61 -27
  40. data/lib/liquid/tags/comment.rb +18 -3
  41. data/lib/liquid/tags/continue.rb +16 -12
  42. data/lib/liquid/tags/cycle.rb +37 -25
  43. data/lib/liquid/tags/decrement.rb +22 -20
  44. data/lib/liquid/tags/echo.rb +41 -0
  45. data/lib/liquid/tags/for.rb +90 -87
  46. data/lib/liquid/tags/if.rb +50 -32
  47. data/lib/liquid/tags/ifchanged.rb +11 -10
  48. data/lib/liquid/tags/include.rb +49 -60
  49. data/lib/liquid/tags/increment.rb +23 -17
  50. data/lib/liquid/tags/inline_comment.rb +43 -0
  51. data/lib/liquid/tags/raw.rb +25 -11
  52. data/lib/liquid/tags/render.rb +109 -0
  53. data/lib/liquid/tags/table_row.rb +45 -19
  54. data/lib/liquid/tags/unless.rb +38 -19
  55. data/lib/liquid/template.rb +52 -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 +49 -44
  61. data/lib/liquid/variable_lookup.rb +18 -10
  62. data/lib/liquid/version.rb +2 -1
  63. data/lib/liquid.rb +18 -6
  64. metadata +20 -108
  65. data/lib/liquid/strainer.rb +0 -66
  66. data/lib/liquid/truffle.rb +0 -5
  67. data/test/fixtures/en_locale.yml +0 -9
  68. data/test/integration/assign_test.rb +0 -48
  69. data/test/integration/blank_test.rb +0 -106
  70. data/test/integration/block_test.rb +0 -12
  71. data/test/integration/capture_test.rb +0 -50
  72. data/test/integration/context_test.rb +0 -32
  73. data/test/integration/document_test.rb +0 -19
  74. data/test/integration/drop_test.rb +0 -273
  75. data/test/integration/error_handling_test.rb +0 -260
  76. data/test/integration/filter_test.rb +0 -178
  77. data/test/integration/hash_ordering_test.rb +0 -23
  78. data/test/integration/output_test.rb +0 -123
  79. data/test/integration/parse_tree_visitor_test.rb +0 -247
  80. data/test/integration/parsing_quirks_test.rb +0 -122
  81. data/test/integration/render_profiling_test.rb +0 -154
  82. data/test/integration/security_test.rb +0 -80
  83. data/test/integration/standard_filter_test.rb +0 -776
  84. data/test/integration/tags/break_tag_test.rb +0 -15
  85. data/test/integration/tags/continue_tag_test.rb +0 -15
  86. data/test/integration/tags/for_tag_test.rb +0 -410
  87. data/test/integration/tags/if_else_tag_test.rb +0 -188
  88. data/test/integration/tags/include_tag_test.rb +0 -253
  89. data/test/integration/tags/increment_tag_test.rb +0 -23
  90. data/test/integration/tags/raw_tag_test.rb +0 -31
  91. data/test/integration/tags/standard_tag_test.rb +0 -296
  92. data/test/integration/tags/statements_test.rb +0 -111
  93. data/test/integration/tags/table_row_test.rb +0 -64
  94. data/test/integration/tags/unless_else_tag_test.rb +0 -26
  95. data/test/integration/template_test.rb +0 -332
  96. data/test/integration/trim_mode_test.rb +0 -529
  97. data/test/integration/variable_test.rb +0 -96
  98. data/test/test_helper.rb +0 -116
  99. data/test/truffle/truffle_test.rb +0 -9
  100. data/test/unit/block_unit_test.rb +0 -58
  101. data/test/unit/condition_unit_test.rb +0 -166
  102. data/test/unit/context_unit_test.rb +0 -489
  103. data/test/unit/file_system_unit_test.rb +0 -35
  104. data/test/unit/i18n_unit_test.rb +0 -37
  105. data/test/unit/lexer_unit_test.rb +0 -51
  106. data/test/unit/parser_unit_test.rb +0 -82
  107. data/test/unit/regexp_unit_test.rb +0 -44
  108. data/test/unit/strainer_unit_test.rb +0 -164
  109. data/test/unit/tag_unit_test.rb +0 -21
  110. data/test/unit/tags/case_tag_unit_test.rb +0 -10
  111. data/test/unit/tags/for_tag_unit_test.rb +0 -13
  112. data/test/unit/tags/if_tag_unit_test.rb +0 -8
  113. data/test/unit/template_unit_test.rb +0 -78
  114. data/test/unit/tokenizer_unit_test.rb +0 -55
  115. data/test/unit/variable_unit_test.rb +0 -162
data/test/test_helper.rb DELETED
@@ -1,116 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- ENV["MT_NO_EXPECTATIONS"] = "1"
4
- require 'minitest/autorun'
5
-
6
- $LOAD_PATH.unshift(File.join(File.expand_path(__dir__), '..', 'lib'))
7
- require 'liquid.rb'
8
- require 'liquid/profiler'
9
-
10
- mode = :strict
11
- if env_mode = ENV['LIQUID_PARSER_MODE']
12
- puts "-- #{env_mode.upcase} ERROR MODE"
13
- mode = env_mode.to_sym
14
- end
15
- Liquid::Template.error_mode = mode
16
-
17
- if ENV['LIQUID-C'] == '1'
18
- puts "-- LIQUID C"
19
- require 'liquid/c'
20
- end
21
-
22
- if Minitest.const_defined?('Test')
23
- # We're on Minitest 5+. Nothing to do here.
24
- else
25
- # Minitest 4 doesn't have Minitest::Test yet.
26
- Minitest::Test = MiniTest::Unit::TestCase
27
- end
28
-
29
- module Minitest
30
- class Test
31
- def fixture(name)
32
- File.join(File.expand_path(__dir__), "fixtures", name)
33
- end
34
- end
35
-
36
- module Assertions
37
- include Liquid
38
-
39
- def assert_template_result(expected, template, assigns = {}, message = nil)
40
- assert_equal expected, Template.parse(template).render!(assigns), message
41
- end
42
-
43
- def assert_template_result_matches(expected, template, assigns = {}, message = nil)
44
- return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
45
-
46
- assert_match expected, Template.parse(template).render!(assigns), message
47
- end
48
-
49
- def assert_match_syntax_error(match, template, assigns = {})
50
- exception = assert_raises(Liquid::SyntaxError) do
51
- Template.parse(template).render(assigns)
52
- end
53
- assert_match match, exception.message
54
- end
55
-
56
- def with_global_filter(*globals)
57
- original_global_strainer = Liquid::Strainer.class_variable_get(:@@global_strainer)
58
- Liquid::Strainer.class_variable_set(:@@global_strainer, Class.new(Liquid::Strainer) do
59
- @filter_methods = Set.new
60
- end)
61
- Liquid::Strainer.class_variable_get(:@@strainer_class_cache).clear
62
-
63
- globals.each do |global|
64
- Liquid::Template.register_filter(global)
65
- end
66
- yield
67
- ensure
68
- Liquid::Strainer.class_variable_get(:@@strainer_class_cache).clear
69
- Liquid::Strainer.class_variable_set(:@@global_strainer, original_global_strainer)
70
- end
71
-
72
- def with_taint_mode(mode)
73
- old_mode = Liquid::Template.taint_mode
74
- Liquid::Template.taint_mode = mode
75
- yield
76
- ensure
77
- Liquid::Template.taint_mode = old_mode
78
- end
79
-
80
- def with_error_mode(mode)
81
- old_mode = Liquid::Template.error_mode
82
- Liquid::Template.error_mode = mode
83
- yield
84
- ensure
85
- Liquid::Template.error_mode = old_mode
86
- end
87
- end
88
- end
89
-
90
- class ThingWithToLiquid
91
- def to_liquid
92
- 'foobar'
93
- end
94
- end
95
-
96
- class ErrorDrop < Liquid::Drop
97
- def standard_error
98
- raise Liquid::StandardError, 'standard error'
99
- end
100
-
101
- def argument_error
102
- raise Liquid::ArgumentError, 'argument error'
103
- end
104
-
105
- def syntax_error
106
- raise Liquid::SyntaxError, 'syntax error'
107
- end
108
-
109
- def runtime_error
110
- raise 'runtime error'
111
- end
112
-
113
- def exception
114
- raise Exception, 'exception'
115
- end
116
- end
@@ -1,9 +0,0 @@
1
- require 'test_helper'
2
-
3
- class TruffleTest < Minitest::Test
4
- include Liquid
5
-
6
- def test_truffle_works
7
-
8
- end
9
- end
@@ -1,58 +0,0 @@
1
- require 'test_helper'
2
-
3
- class BlockUnitTest < Minitest::Test
4
- include Liquid
5
-
6
- def test_blankspace
7
- template = Liquid::Template.parse(" ")
8
- assert_equal [" "], template.root.nodelist
9
- end
10
-
11
- def test_variable_beginning
12
- template = Liquid::Template.parse("{{funk}} ")
13
- assert_equal 2, template.root.nodelist.size
14
- assert_equal Variable, template.root.nodelist[0].class
15
- assert_equal String, template.root.nodelist[1].class
16
- end
17
-
18
- def test_variable_end
19
- template = Liquid::Template.parse(" {{funk}}")
20
- assert_equal 2, template.root.nodelist.size
21
- assert_equal String, template.root.nodelist[0].class
22
- assert_equal Variable, template.root.nodelist[1].class
23
- end
24
-
25
- def test_variable_middle
26
- template = Liquid::Template.parse(" {{funk}} ")
27
- assert_equal 3, template.root.nodelist.size
28
- assert_equal String, template.root.nodelist[0].class
29
- assert_equal Variable, template.root.nodelist[1].class
30
- assert_equal String, template.root.nodelist[2].class
31
- end
32
-
33
- def test_variable_many_embedded_fragments
34
- template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
35
- assert_equal 7, template.root.nodelist.size
36
- assert_equal [String, Variable, String, Variable, String, Variable, String],
37
- block_types(template.root.nodelist)
38
- end
39
-
40
- def test_with_block
41
- template = Liquid::Template.parse(" {% comment %} {% endcomment %} ")
42
- assert_equal [String, Comment, String], block_types(template.root.nodelist)
43
- assert_equal 3, template.root.nodelist.size
44
- end
45
-
46
- def test_with_custom_tag
47
- Liquid::Template.register_tag("testtag", Block)
48
- assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
49
- ensure
50
- Liquid::Template.tags.delete('testtag')
51
- end
52
-
53
- private
54
-
55
- def block_types(nodelist)
56
- nodelist.collect(&:class)
57
- end
58
- end # VariableTest
@@ -1,166 +0,0 @@
1
- require 'test_helper'
2
-
3
- class ConditionUnitTest < Minitest::Test
4
- include Liquid
5
-
6
- def setup
7
- @context = Liquid::Context.new
8
- end
9
-
10
- def test_basic_condition
11
- assert_equal false, Condition.new(1, '==', 2).evaluate
12
- assert_equal true, Condition.new(1, '==', 1).evaluate
13
- end
14
-
15
- 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
25
- # 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
30
- end
31
-
32
- 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
42
- end
43
-
44
- 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'
50
-
51
- assert_evaluates_false 'bob', 'contains', 'bob2'
52
- assert_evaluates_false 'bob', 'contains', 'a'
53
- assert_evaluates_false 'bob', 'contains', '---'
54
- end
55
-
56
- def test_invalid_comparation_operator
57
- assert_evaluates_argument_error 1, '~~', 0
58
- end
59
-
60
- 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
65
- end
66
-
67
- 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
73
- end
74
-
75
- def test_contains_works_on_arrays
76
- @context = Liquid::Context.new
77
- @context['array'] = [1, 2, 3, 4, 5]
78
- array_expr = VariableLookup.new("array")
79
-
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"
88
- end
89
-
90
- def test_contains_returns_false_for_nil_operands
91
- @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
- end
95
-
96
- def test_contains_return_false_on_wrong_data_type
97
- assert_evaluates_false 1, 'contains', 0
98
- end
99
-
100
- 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
- end
104
-
105
- def test_or_condition
106
- condition = Condition.new(1, '==', 2)
107
-
108
- assert_equal false, condition.evaluate
109
-
110
- condition.or Condition.new(2, '==', 1)
111
-
112
- assert_equal false, condition.evaluate
113
-
114
- condition.or Condition.new(1, '==', 1)
115
-
116
- assert_equal true, condition.evaluate
117
- end
118
-
119
- def test_and_condition
120
- condition = Condition.new(1, '==', 1)
121
-
122
- assert_equal true, condition.evaluate
123
-
124
- condition.and Condition.new(2, '==', 2)
125
-
126
- assert_equal true, condition.evaluate
127
-
128
- condition.and Condition.new(2, '==', 1)
129
-
130
- assert_equal false, condition.evaluate
131
- end
132
-
133
- def test_should_allow_custom_proc_operator
134
- Condition.operators['starts_with'] = proc { |cond, left, right| left =~ %r{^#{right}} }
135
-
136
- assert_evaluates_true 'bob', 'starts_with', 'b'
137
- assert_evaluates_false 'bob', 'starts_with', 'o'
138
- ensure
139
- Condition.operators.delete 'starts_with'
140
- end
141
-
142
- def test_left_or_right_may_contain_operators
143
- @context = Liquid::Context.new
144
- @context['one'] = @context['another'] = "gnomeslab-and-or-liquid"
145
-
146
- assert_evaluates_true VariableLookup.new("one"), '==', VariableLookup.new("another")
147
- end
148
-
149
- private
150
-
151
- def assert_evaluates_true(left, op, right)
152
- assert Condition.new(left, op, right).evaluate(@context),
153
- "Evaluated false: #{left} #{op} #{right}"
154
- end
155
-
156
- def assert_evaluates_false(left, op, right)
157
- assert !Condition.new(left, op, right).evaluate(@context),
158
- "Evaluated true: #{left} #{op} #{right}"
159
- end
160
-
161
- def assert_evaluates_argument_error(left, op, right)
162
- assert_raises(Liquid::ArgumentError) do
163
- Condition.new(left, op, right).evaluate(@context)
164
- end
165
- end
166
- end # ConditionTest