liquid 4.0.4 → 5.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +32 -4
  3. data/README.md +6 -0
  4. data/lib/liquid/block.rb +31 -14
  5. data/lib/liquid/block_body.rb +164 -54
  6. data/lib/liquid/condition.rb +39 -18
  7. data/lib/liquid/context.rb +106 -51
  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 +16 -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 +5 -3
  27. data/lib/liquid/register.rb +6 -0
  28. data/lib/liquid/resource_limits.rb +47 -8
  29. data/lib/liquid/standardfilters.rb +62 -43
  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 +33 -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 +26 -0
  46. data/lib/liquid/tags/for.rb +68 -44
  47. data/lib/liquid/tags/if.rb +35 -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 +15 -15
  55. data/lib/liquid/template.rb +55 -69
  56. data/lib/liquid/template_factory.rb +9 -0
  57. data/lib/liquid/tokenizer.rb +17 -9
  58. data/lib/liquid/usage.rb +8 -0
  59. data/lib/liquid/utils.rb +5 -3
  60. data/lib/liquid/variable.rb +47 -19
  61. data/lib/liquid/variable_lookup.rb +8 -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 +608 -5
  69. data/test/integration/document_test.rb +4 -2
  70. data/test/integration/drop_test.rb +67 -57
  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 +339 -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 +118 -124
  97. data/test/integration/trim_mode_test.rb +78 -44
  98. data/test/integration/variable_test.rb +43 -32
  99. data/test/test_helper.rb +75 -14
  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 +1 -1
  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 +19 -17
  119. data/test/unit/variable_unit_test.rb +51 -49
  120. metadata +75 -47
  121. data/lib/liquid/strainer.rb +0 -66
  122. data/test/unit/context_unit_test.rb +0 -490
  123. data/test/unit/strainer_unit_test.rb +0 -164
@@ -1,29 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
4
  class Tokenizer
3
- attr_reader :line_number
5
+ attr_reader :line_number, :for_liquid_tag
4
6
 
5
- def initialize(source, line_numbers = false)
6
- @source = source
7
- @line_number = line_numbers ? 1 : nil
8
- @tokens = tokenize
7
+ def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
8
+ @source = source
9
+ @line_number = line_number || (line_numbers ? 1 : nil)
10
+ @for_liquid_tag = for_liquid_tag
11
+ @tokens = tokenize
9
12
  end
10
13
 
11
14
  def shift
12
- token = @tokens.shift
13
- @line_number += token.count("\n") if @line_number && token
15
+ (token = @tokens.shift) || return
16
+
17
+ if @line_number
18
+ @line_number += @for_liquid_tag ? 1 : token.count("\n")
19
+ end
20
+
14
21
  token
15
22
  end
16
23
 
17
24
  private
18
25
 
19
26
  def tokenize
20
- @source = @source.source if @source.respond_to?(:source)
21
27
  return [] if @source.to_s.empty?
22
28
 
29
+ return @source.split("\n") if @for_liquid_tag
30
+
23
31
  tokens = @source.split(TemplateParser)
24
32
 
25
33
  # removes the rogue empty element at the beginning of the array
26
- tokens.shift if tokens[0] && tokens[0].empty?
34
+ tokens.shift if tokens[0]&.empty?
27
35
 
28
36
  tokens
29
37
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Liquid
4
+ module Usage
5
+ def self.increment(name)
6
+ end
7
+ end
8
+ end
data/lib/liquid/utils.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
4
  module Utils
3
5
  def self.slice_collection(collection, from, to)
@@ -10,7 +12,7 @@ module Liquid
10
12
 
11
13
  def self.slice_collection_using_each(collection, from, to)
12
14
  segments = []
13
- index = 0
15
+ index = 0
14
16
 
15
17
  # Maintains Ruby 1.8.7 String#each behaviour on 1.9
16
18
  if collection.is_a?(String)
@@ -50,7 +52,7 @@ module Liquid
50
52
  when Numeric
51
53
  obj
52
54
  when String
53
- (obj.strip =~ /\A-?\d+\.\d+\z/) ? BigDecimal(obj) : obj.to_i
55
+ /\A-?\d+\.\d+\z/.match?(obj.strip) ? BigDecimal(obj) : obj.to_i
54
56
  else
55
57
  if obj.respond_to?(:to_number)
56
58
  obj.to_number
@@ -69,7 +71,7 @@ module Liquid
69
71
  end
70
72
 
71
73
  case obj
72
- when 'now'.freeze, 'today'.freeze
74
+ when 'now', 'today'
73
75
  Time.now
74
76
  when /\A\d+\z/, Integer
75
77
  Time.at(obj.to_i)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
4
  # Holds variables. Variables are only loaded "just in time"
3
5
  # and are not evaluated as part of the render stage
@@ -10,10 +12,10 @@ module Liquid
10
12
  # {{ user | link }}
11
13
  #
12
14
  class Variable
13
- FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
14
- FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
15
- FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
16
- JustTagAttributes = /\A#{TagAttributes}\z/o
15
+ FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
16
+ FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
17
+ FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
18
+ JustTagAttributes = /\A#{TagAttributes}\z/o
17
19
  MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
18
20
 
19
21
  attr_accessor :filters, :name, :line_number
@@ -23,12 +25,12 @@ module Liquid
23
25
  include ParserSwitching
24
26
 
25
27
  def initialize(markup, parse_context)
26
- @markup = markup
27
- @name = nil
28
+ @markup = markup
29
+ @name = nil
28
30
  @parse_context = parse_context
29
- @line_number = parse_context.line_number
31
+ @line_number = parse_context.line_number
30
32
 
31
- parse_with_selected_parser(markup)
33
+ strict_parse_with_error_mode_fallback(markup)
32
34
  end
33
35
 
34
36
  def raw
@@ -43,11 +45,11 @@ module Liquid
43
45
  @filters = []
44
46
  return unless markup =~ MarkupWithQuotedFragment
45
47
 
46
- name_markup = $1
47
- filter_markup = $2
48
- @name = Expression.parse(name_markup)
48
+ name_markup = Regexp.last_match(1)
49
+ filter_markup = Regexp.last_match(2)
50
+ @name = Expression.parse(name_markup)
49
51
  if filter_markup =~ FilterMarkupRegex
50
- filters = $1.scan(FilterParser)
52
+ filters = Regexp.last_match(1).scan(FilterParser)
51
53
  filters.each do |f|
52
54
  next unless f =~ /\w+/
53
55
  filtername = Regexp.last_match(0)
@@ -61,6 +63,8 @@ module Liquid
61
63
  @filters = []
62
64
  p = Parser.new(markup)
63
65
 
66
+ return if p.look(:end_of_string)
67
+
64
68
  @name = Expression.parse(p.expression)
65
69
  while p.consume?(:pipe)
66
70
  filtername = p.consume(:id)
@@ -79,33 +83,57 @@ module Liquid
79
83
  end
80
84
 
81
85
  def render(context)
82
- obj = @filters.inject(context.evaluate(@name)) do |output, (filter_name, filter_args, filter_kwargs)|
86
+ obj = context.evaluate(@name)
87
+
88
+ @filters.each do |filter_name, filter_args, filter_kwargs|
83
89
  filter_args = evaluate_filter_expressions(context, filter_args, filter_kwargs)
84
- context.invoke(filter_name, output, *filter_args)
90
+ obj = context.invoke(filter_name, obj, *filter_args)
85
91
  end
86
92
 
87
93
  context.apply_global_filter(obj)
88
94
  end
89
95
 
96
+ def render_to_output_buffer(context, output)
97
+ obj = render(context)
98
+
99
+ if obj.is_a?(Array)
100
+ output << obj.join
101
+ elsif obj.nil?
102
+ else
103
+ output << obj.to_s
104
+ end
105
+
106
+ output
107
+ end
108
+
109
+ def disabled?(_context)
110
+ false
111
+ end
112
+
113
+ def disabled_tags
114
+ []
115
+ end
116
+
90
117
  private
91
118
 
92
119
  def parse_filter_expressions(filter_name, unparsed_args)
93
- filter_args = []
94
- keyword_args = {}
120
+ filter_args = []
121
+ keyword_args = nil
95
122
  unparsed_args.each do |a|
96
- if matches = a.match(JustTagAttributes)
123
+ if (matches = a.match(JustTagAttributes))
124
+ keyword_args ||= {}
97
125
  keyword_args[matches[1]] = Expression.parse(matches[2])
98
126
  else
99
127
  filter_args << Expression.parse(a)
100
128
  end
101
129
  end
102
130
  result = [filter_name, filter_args]
103
- result << keyword_args unless keyword_args.empty?
131
+ result << keyword_args if keyword_args
104
132
  result
105
133
  end
106
134
 
107
135
  def evaluate_filter_expressions(context, filter_args, filter_kwargs)
108
- parsed_args = filter_args.map{ |expr| context.evaluate(expr) }
136
+ parsed_args = filter_args.map { |expr| context.evaluate(expr) }
109
137
  if filter_kwargs
110
138
  parsed_kwargs = {}
111
139
  filter_kwargs.each do |key, expr|
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
4
  class VariableLookup
3
5
  SQUARE_BRACKETED = /\A\[(.*)\]\z/m
4
- COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
6
+ COMMAND_METHODS = ['size', 'first', 'last'].freeze
5
7
 
6
8
  attr_reader :name, :lookups
7
9
 
@@ -14,17 +16,17 @@ module Liquid
14
16
 
15
17
  name = lookups.shift
16
18
  if name =~ SQUARE_BRACKETED
17
- name = Expression.parse($1)
19
+ name = Expression.parse(Regexp.last_match(1))
18
20
  end
19
21
  @name = name
20
22
 
21
- @lookups = lookups
23
+ @lookups = lookups
22
24
  @command_flags = 0
23
25
 
24
26
  @lookups.each_index do |i|
25
27
  lookup = lookups[i]
26
28
  if lookup =~ SQUARE_BRACKETED
27
- lookups[i] = Expression.parse($1)
29
+ lookups[i] = Expression.parse(Regexp.last_match(1))
28
30
  elsif COMMAND_METHODS.include?(lookup)
29
31
  @command_flags |= 1 << i
30
32
  end
@@ -32,7 +34,7 @@ module Liquid
32
34
  end
33
35
 
34
36
  def evaluate(context)
35
- name = context.evaluate(@name)
37
+ name = context.evaluate(@name)
36
38
  object = context.find_variable(name)
37
39
 
38
40
  @lookups.each_index do |i|
@@ -45,7 +47,7 @@ module Liquid
45
47
  (object.respond_to?(:fetch) && key.is_a?(Integer)))
46
48
 
47
49
  # if its a proc we will replace the entry with the proc
48
- res = context.lookup_and_evaluate(object, key)
50
+ res = context.lookup_and_evaluate(object, key)
49
51
  object = res.to_liquid
50
52
 
51
53
  # Some special cases. If the part wasn't in square brackets and
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Liquid
4
- VERSION = "4.0.4".freeze
5
+ VERSION = "5.0.0"
5
6
  end
data/lib/liquid.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2005 Tobias Luetke
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -21,10 +23,10 @@
21
23
 
22
24
  module Liquid
23
25
  FilterSeparator = /\|/
24
- ArgumentSeparator = ','.freeze
25
- FilterArgumentSeparator = ':'.freeze
26
- VariableAttributeSeparator = '.'.freeze
27
- WhitespaceControl = '-'.freeze
26
+ ArgumentSeparator = ','
27
+ FilterArgumentSeparator = ':'
28
+ VariableAttributeSeparator = '.'
29
+ WhitespaceControl = '-'
28
30
  TagStart = /\{\%/
29
31
  TagEnd = /\%\}/
30
32
  VariableSignature = /\(?[\w\-\.\[\]]\)?/
@@ -40,6 +42,8 @@ module Liquid
40
42
  TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
41
43
  VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
42
44
 
45
+ RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }
46
+
43
47
  singleton_class.send(:attr_accessor, :cache_classes)
44
48
  self.cache_classes = true
45
49
  end
@@ -55,11 +59,14 @@ require 'liquid/forloop_drop'
55
59
  require 'liquid/extensions'
56
60
  require 'liquid/errors'
57
61
  require 'liquid/interrupts'
58
- require 'liquid/strainer'
62
+ require 'liquid/strainer_factory'
63
+ require 'liquid/strainer_template'
59
64
  require 'liquid/expression'
60
65
  require 'liquid/context'
61
66
  require 'liquid/parser_switching'
62
67
  require 'liquid/tag'
68
+ require 'liquid/tag/disabler'
69
+ require 'liquid/tag/disableable'
63
70
  require 'liquid/block'
64
71
  require 'liquid/block_body'
65
72
  require 'liquid/document'
@@ -74,6 +81,11 @@ require 'liquid/condition'
74
81
  require 'liquid/utils'
75
82
  require 'liquid/tokenizer'
76
83
  require 'liquid/parse_context'
84
+ require 'liquid/partial_cache'
85
+ require 'liquid/usage'
86
+ require 'liquid/register'
87
+ require 'liquid/static_registers'
88
+ require 'liquid/template_factory'
77
89
 
78
90
  # Load all the tags of the standard library
79
91
  #
@@ -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