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 AssignTest < Minitest::Test
@@ -8,9 +10,9 @@ class AssignTest < Minitest::Test
8
10
  {% assign this-thing = 'Print this-thing' %}
9
11
  {{ this-thing }}
10
12
  END_TEMPLATE
11
- template = Template.parse(template_source)
12
- rendered = template.render!
13
- assert_equal "Print this-thing", rendered.strip
13
+ template = Template.parse(template_source)
14
+ rendered = template.render!
15
+ assert_equal("Print this-thing", rendered.strip)
14
16
  end
15
17
 
16
18
  def test_assigned_variable
@@ -42,7 +44,74 @@ class AssignTest < Minitest::Test
42
44
  end
43
45
  end
44
46
  with_error_mode(:lax) do
45
- assert Template.parse("{% assign foo = ('X' | downcase) %}")
47
+ assert(Template.parse("{% assign foo = ('X' | downcase) %}"))
48
+ end
49
+ end
50
+
51
+ def test_expression_with_whitespace_in_square_brackets
52
+ source = "{% assign r = a[ 'b' ] %}{{ r }}"
53
+ assert_template_result('result', source, 'a' => { 'b' => 'result' })
54
+ end
55
+
56
+ def test_assign_score_exceeding_resource_limit
57
+ t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}")
58
+ t.resource_limits.assign_score_limit = 1
59
+ assert_equal("Liquid error: Memory limits exceeded", t.render)
60
+ assert(t.resource_limits.reached?)
61
+
62
+ t.resource_limits.assign_score_limit = 2
63
+ assert_equal("", t.render!)
64
+ refute_nil(t.resource_limits.assign_score)
65
+ end
66
+
67
+ def test_assign_score_exceeding_limit_from_composite_object
68
+ t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
69
+
70
+ t.resource_limits.assign_score_limit = 3
71
+ assert_equal("Liquid error: Memory limits exceeded", t.render)
72
+ assert(t.resource_limits.reached?)
73
+
74
+ t.resource_limits.assign_score_limit = 5
75
+ assert_equal("", t.render!)
76
+ end
77
+
78
+ def test_assign_score_of_int
79
+ assert_equal(1, assign_score_of(123))
80
+ end
81
+
82
+ def test_assign_score_of_string_counts_bytes
83
+ assert_equal(3, assign_score_of('123'))
84
+ assert_equal(5, assign_score_of('12345'))
85
+ assert_equal(9, assign_score_of('すごい'))
86
+ end
87
+
88
+ def test_assign_score_of_array
89
+ assert_equal(1, assign_score_of([]))
90
+ assert_equal(2, assign_score_of([123]))
91
+ assert_equal(6, assign_score_of([123, 'abcd']))
92
+ end
93
+
94
+ def test_assign_score_of_hash
95
+ assert_equal(1, assign_score_of({}))
96
+ assert_equal(5, assign_score_of('int' => 123))
97
+ assert_equal(12, assign_score_of('int' => 123, 'str' => 'abcd'))
98
+ end
99
+
100
+ private
101
+
102
+ class ObjectWrapperDrop < Liquid::Drop
103
+ def initialize(obj)
104
+ @obj = obj
105
+ end
106
+
107
+ def value
108
+ @obj
46
109
  end
47
110
  end
48
- end # AssignTest
111
+
112
+ def assign_score_of(obj)
113
+ context = Liquid::Context.new('drop' => ObjectWrapperDrop.new(obj))
114
+ Liquid::Template.parse('{% assign obj = drop.value %}').render!(context)
115
+ context.resource_limits.assign_score
116
+ end
117
+ end
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class FoobarTag < Liquid::Tag
4
- def render(*args)
5
- " "
6
+ def render_to_output_buffer(_context, output)
7
+ output << ' '
8
+ output
6
9
  end
7
-
8
- Liquid::Template.register_tag('foobar', FoobarTag)
9
10
  end
10
11
 
11
12
  class BlankTestFileSystem
@@ -31,7 +32,9 @@ class BlankTest < Minitest::Test
31
32
  end
32
33
 
33
34
  def test_new_tags_are_not_blank_by_default
34
- assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
35
+ with_custom_tag('foobar', FoobarTag) do
36
+ assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
37
+ end
35
38
  end
36
39
 
37
40
  def test_loops_are_blank
@@ -93,9 +96,9 @@ class BlankTest < Minitest::Test
93
96
 
94
97
  def test_include_is_blank
95
98
  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
+ assert_template_result("foobar" * (N + 1), wrap("{% include 'foobar' %}"))
100
+ assert_template_result(" foobar " * (N + 1), wrap("{% include ' foobar ' %}"))
101
+ assert_template_result(" " * (N + 1), wrap(" {% include ' ' %} "))
99
102
  end
100
103
 
101
104
  def test_case_is_blank
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class BlockTest < Minitest::Test
@@ -7,6 +9,50 @@ class BlockTest < Minitest::Test
7
9
  exc = assert_raises(SyntaxError) do
8
10
  Template.parse("{% if true %}{% endunless %}")
9
11
  end
10
- assert_equal exc.message, "Liquid syntax error: 'endunless' is not a valid delimiter for if tags. use endif"
12
+ assert_equal(exc.message, "Liquid syntax error: 'endunless' is not a valid delimiter for if tags. use endif")
13
+ end
14
+
15
+ def test_with_custom_tag
16
+ with_custom_tag('testtag', Block) do
17
+ assert(Liquid::Template.parse("{% testtag %} {% endtesttag %}"))
18
+ end
19
+ end
20
+
21
+ def test_custom_block_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility
22
+ klass1 = Class.new(Block) do
23
+ def render(*)
24
+ 'hello'
25
+ end
26
+ end
27
+
28
+ with_custom_tag('blabla', klass1) do
29
+ template = Liquid::Template.parse("{% blabla %} bla {% endblabla %}")
30
+
31
+ assert_equal('hello', template.render)
32
+
33
+ buf = +''
34
+ output = template.render({}, output: buf)
35
+ assert_equal('hello', output)
36
+ assert_equal('hello', buf)
37
+ assert_equal(buf.object_id, output.object_id)
38
+ end
39
+
40
+ klass2 = Class.new(klass1) do
41
+ def render(*)
42
+ 'foo' + super + 'bar'
43
+ end
44
+ end
45
+
46
+ with_custom_tag('blabla', klass2) do
47
+ template = Liquid::Template.parse("{% blabla %} foo {% endblabla %}")
48
+
49
+ assert_equal('foohellobar', template.render)
50
+
51
+ buf = +''
52
+ output = template.render({}, output: buf)
53
+ assert_equal('foohellobar', output)
54
+ assert_equal('foohellobar', buf)
55
+ assert_equal(buf.object_id, output.object_id)
56
+ end
11
57
  end
12
58
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class CaptureTest < Minitest::Test
@@ -12,9 +14,9 @@ class CaptureTest < Minitest::Test
12
14
  {% capture this-thing %}Print this-thing{% endcapture %}
13
15
  {{ this-thing }}
14
16
  END_TEMPLATE
15
- template = Template.parse(template_source)
16
- rendered = template.render!
17
- assert_equal "Print this-thing", rendered.strip
17
+ template = Template.parse(template_source)
18
+ rendered = template.render!
19
+ assert_equal("Print this-thing", rendered.strip)
18
20
  end
19
21
 
20
22
  def test_capture_to_variable_from_outer_scope_if_existing
@@ -28,9 +30,9 @@ class CaptureTest < Minitest::Test
28
30
  {% endif %}
29
31
  {{var}}
30
32
  END_TEMPLATE
31
- template = Template.parse(template_source)
32
- rendered = template.render!
33
- assert_equal "test-string", rendered.gsub(/\s/, '')
33
+ template = Template.parse(template_source)
34
+ rendered = template.render!
35
+ assert_equal("test-string", rendered.gsub(/\s/, ''))
34
36
  end
35
37
 
36
38
  def test_assigning_from_capture
@@ -43,8 +45,14 @@ class CaptureTest < Minitest::Test
43
45
  {% endfor %}
44
46
  {{ first }}-{{ second }}
45
47
  END_TEMPLATE
46
- template = Template.parse(template_source)
47
- rendered = template.render!
48
- assert_equal "3-3", rendered.gsub(/\s/, '')
48
+ template = Template.parse(template_source)
49
+ rendered = template.render!
50
+ assert_equal("3-3", rendered.gsub(/\s/, ''))
51
+ end
52
+
53
+ def test_increment_assign_score_by_bytes_not_characters
54
+ t = Template.parse("{% capture foo %}すごい{% endcapture %}")
55
+ t.render!
56
+ assert_equal(9, t.resource_limits.assign_score)
49
57
  end
50
- end # CaptureTest
58
+ end