liquid 4.0.0 → 5.0.1

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 +5 -5
  2. data/History.md +101 -2
  3. data/README.md +8 -0
  4. data/lib/liquid.rb +18 -5
  5. data/lib/liquid/block.rb +47 -20
  6. data/lib/liquid/block_body.rb +192 -76
  7. data/lib/liquid/condition.rb +69 -29
  8. data/lib/liquid/context.rb +110 -53
  9. data/lib/liquid/document.rb +47 -9
  10. data/lib/liquid/drop.rb +4 -2
  11. data/lib/liquid/errors.rb +20 -18
  12. data/lib/liquid/expression.rb +30 -31
  13. data/lib/liquid/extensions.rb +8 -0
  14. data/lib/liquid/file_system.rb +6 -4
  15. data/lib/liquid/forloop_drop.rb +11 -4
  16. data/lib/liquid/i18n.rb +5 -3
  17. data/lib/liquid/interrupts.rb +3 -1
  18. data/lib/liquid/lexer.rb +35 -26
  19. data/lib/liquid/locales/en.yml +4 -2
  20. data/lib/liquid/parse_context.rb +21 -4
  21. data/lib/liquid/parse_tree_visitor.rb +42 -0
  22. data/lib/liquid/parser.rb +30 -18
  23. data/lib/liquid/parser_switching.rb +17 -3
  24. data/lib/liquid/partial_cache.rb +24 -0
  25. data/lib/liquid/profiler.rb +67 -86
  26. data/lib/liquid/profiler/hooks.rb +26 -14
  27. data/lib/liquid/range_lookup.rb +5 -3
  28. data/lib/liquid/register.rb +6 -0
  29. data/lib/liquid/resource_limits.rb +47 -8
  30. data/lib/liquid/standardfilters.rb +170 -63
  31. data/lib/liquid/static_registers.rb +44 -0
  32. data/lib/liquid/strainer_factory.rb +36 -0
  33. data/lib/liquid/strainer_template.rb +53 -0
  34. data/lib/liquid/tablerowloop_drop.rb +6 -4
  35. data/lib/liquid/tag.rb +28 -6
  36. data/lib/liquid/tag/disableable.rb +22 -0
  37. data/lib/liquid/tag/disabler.rb +21 -0
  38. data/lib/liquid/tags/assign.rb +32 -10
  39. data/lib/liquid/tags/break.rb +8 -3
  40. data/lib/liquid/tags/capture.rb +11 -8
  41. data/lib/liquid/tags/case.rb +41 -27
  42. data/lib/liquid/tags/comment.rb +5 -3
  43. data/lib/liquid/tags/continue.rb +8 -3
  44. data/lib/liquid/tags/cycle.rb +35 -16
  45. data/lib/liquid/tags/decrement.rb +6 -3
  46. data/lib/liquid/tags/echo.rb +34 -0
  47. data/lib/liquid/tags/for.rb +79 -47
  48. data/lib/liquid/tags/if.rb +53 -30
  49. data/lib/liquid/tags/ifchanged.rb +11 -10
  50. data/lib/liquid/tags/include.rb +42 -44
  51. data/lib/liquid/tags/increment.rb +7 -3
  52. data/lib/liquid/tags/raw.rb +14 -11
  53. data/lib/liquid/tags/render.rb +84 -0
  54. data/lib/liquid/tags/table_row.rb +32 -20
  55. data/lib/liquid/tags/unless.rb +15 -15
  56. data/lib/liquid/template.rb +53 -72
  57. data/lib/liquid/template_factory.rb +9 -0
  58. data/lib/liquid/tokenizer.rb +17 -9
  59. data/lib/liquid/usage.rb +8 -0
  60. data/lib/liquid/utils.rb +6 -4
  61. data/lib/liquid/variable.rb +55 -38
  62. data/lib/liquid/variable_lookup.rb +14 -6
  63. data/lib/liquid/version.rb +3 -1
  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 +58 -0
  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 +24 -8
  77. data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
  78. data/test/integration/security_test.rb +41 -18
  79. data/test/integration/standard_filter_test.rb +513 -210
  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 +109 -53
  86. data/test/integration/tags/if_else_tag_test.rb +5 -3
  87. data/test/integration/tags/include_tag_test.rb +83 -52
  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 +123 -120
  97. data/test/integration/trim_mode_test.rb +82 -44
  98. data/test/integration/variable_test.rb +46 -31
  99. data/test/test_helper.rb +75 -23
  100. data/test/unit/block_unit_test.rb +19 -24
  101. data/test/unit/condition_unit_test.rb +82 -72
  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 +12 -10
  105. data/test/unit/parse_tree_visitor_test.rb +254 -0
  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 +79 -46
  121. data/lib/liquid/strainer.rb +0 -66
  122. data/test/unit/context_unit_test.rb +0 -483
  123. data/test/unit/strainer_unit_test.rb +0 -148
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
4
  # Container for liquid nodes which conveniently wraps decision making logic
3
5
  #
@@ -8,58 +10,83 @@ module Liquid
8
10
  #
9
11
  class Condition #:nodoc:
10
12
  @@operators = {
11
- '=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
12
- '!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
13
- '<>'.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
14
- '<'.freeze => :<,
15
- '>'.freeze => :>,
16
- '>='.freeze => :>=,
17
- '<='.freeze => :<=,
18
- 'contains'.freeze => lambda do |cond, left, right|
13
+ '==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
14
+ '!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
15
+ '<>' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
16
+ '<' => :<,
17
+ '>' => :>,
18
+ '>=' => :>=,
19
+ '<=' => :<=,
20
+ 'contains' => lambda do |_cond, left, right|
19
21
  if left && right && left.respond_to?(:include?)
20
22
  right = right.to_s if left.is_a?(String)
21
23
  left.include?(right)
22
24
  else
23
25
  false
24
26
  end
27
+ end,
28
+ }
29
+
30
+ class MethodLiteral
31
+ attr_reader :method_name, :to_s
32
+
33
+ def initialize(method_name, to_s)
34
+ @method_name = method_name
35
+ @to_s = to_s
25
36
  end
37
+ end
38
+
39
+ @@method_literals = {
40
+ 'blank' => MethodLiteral.new(:blank?, '').freeze,
41
+ 'empty' => MethodLiteral.new(:empty?, '').freeze,
26
42
  }
27
43
 
28
44
  def self.operators
29
45
  @@operators
30
46
  end
31
47
 
32
- attr_reader :attachment
48
+ def self.parse_expression(parse_context, markup)
49
+ @@method_literals[markup] || parse_context.parse_expression(markup)
50
+ end
51
+
52
+ attr_reader :attachment, :child_condition
33
53
  attr_accessor :left, :operator, :right
34
54
 
35
55
  def initialize(left = nil, operator = nil, right = nil)
36
- @left = left
56
+ @left = left
37
57
  @operator = operator
38
- @right = right
58
+ @right = right
59
+
39
60
  @child_relation = nil
40
61
  @child_condition = nil
41
62
  end
42
63
 
43
64
  def evaluate(context = Context.new)
44
- result = interpret_condition(left, right, operator, context)
45
-
46
- case @child_relation
47
- when :or
48
- result || @child_condition.evaluate(context)
49
- when :and
50
- result && @child_condition.evaluate(context)
51
- else
52
- result
65
+ condition = self
66
+ result = nil
67
+ loop do
68
+ result = interpret_condition(condition.left, condition.right, condition.operator, context)
69
+
70
+ case condition.child_relation
71
+ when :or
72
+ break if result
73
+ when :and
74
+ break unless result
75
+ else
76
+ break
77
+ end
78
+ condition = condition.child_condition
53
79
  end
80
+ result
54
81
  end
55
82
 
56
83
  def or(condition)
57
- @child_relation = :or
84
+ @child_relation = :or
58
85
  @child_condition = condition
59
86
  end
60
87
 
61
88
  def and(condition)
62
- @child_relation = :and
89
+ @child_relation = :and
63
90
  @child_condition = condition
64
91
  end
65
92
 
@@ -72,13 +99,17 @@ module Liquid
72
99
  end
73
100
 
74
101
  def inspect
75
- "#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
102
+ "#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
76
103
  end
77
104
 
105
+ protected
106
+
107
+ attr_reader :child_relation
108
+
78
109
  private
79
110
 
80
111
  def equal_variables(left, right)
81
- if left.is_a?(Liquid::Expression::MethodLiteral)
112
+ if left.is_a?(MethodLiteral)
82
113
  if right.respond_to?(left.method_name)
83
114
  return right.send(left.method_name)
84
115
  else
@@ -86,7 +117,7 @@ module Liquid
86
117
  end
87
118
  end
88
119
 
89
- if right.is_a?(Liquid::Expression::MethodLiteral)
120
+ if right.is_a?(MethodLiteral)
90
121
  if left.respond_to?(right.method_name)
91
122
  return left.send(right.method_name)
92
123
  else
@@ -103,21 +134,30 @@ module Liquid
103
134
  # return this as the result.
104
135
  return context.evaluate(left) if op.nil?
105
136
 
106
- left = context.evaluate(left)
137
+ left = context.evaluate(left)
107
138
  right = context.evaluate(right)
108
139
 
109
- operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
140
+ operation = self.class.operators[op] || raise(Liquid::ArgumentError, "Unknown operator #{op}")
110
141
 
111
142
  if operation.respond_to?(:call)
112
143
  operation.call(self, left, right)
113
- elsif left.respond_to?(operation) && right.respond_to?(operation)
144
+ elsif left.respond_to?(operation) && right.respond_to?(operation) && !left.is_a?(Hash) && !right.is_a?(Hash)
114
145
  begin
115
146
  left.send(operation, right)
116
147
  rescue ::ArgumentError => e
117
- raise Liquid::ArgumentError.new(e.message)
148
+ raise Liquid::ArgumentError, e.message
118
149
  end
119
150
  end
120
151
  end
152
+
153
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
154
+ def children
155
+ [
156
+ @node.left, @node.right,
157
+ @node.child_condition, @node.attachment
158
+ ].compact
159
+ end
160
+ end
121
161
  end
122
162
 
123
163
  class ElseCondition < Condition
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
4
  # Context keeps the variable stack and resolves variables, as well as keywords
3
5
  #
@@ -12,37 +14,49 @@ module Liquid
12
14
  #
13
15
  # context['bob'] #=> nil class Context
14
16
  class Context
15
- attr_reader :scopes, :errors, :registers, :environments, :resource_limits
17
+ attr_reader :scopes, :errors, :registers, :environments, :resource_limits, :static_registers, :static_environments
16
18
  attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
17
19
 
18
- def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
19
- @environments = [environments].flatten
20
- @scopes = [(outer_scope || {})]
21
- @registers = registers
22
- @errors = []
23
- @partial = false
24
- @strict_variables = false
25
- @resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
26
- squash_instance_assigns_with_environments
20
+ # rubocop:disable Metrics/ParameterLists
21
+ def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {}, &block)
22
+ new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments, &block)
23
+ end
27
24
 
28
- @this_stack_used = false
25
+ def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})
26
+ @environments = [environments]
27
+ @environments.flatten!
28
+
29
+ @static_environments = [static_environments].flat_map(&:freeze).freeze
30
+ @scopes = [(outer_scope || {})]
31
+ @registers = registers
32
+ @errors = []
33
+ @partial = false
34
+ @strict_variables = false
35
+ @resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
36
+ @base_scope_depth = 0
37
+ @interrupts = []
38
+ @filters = []
39
+ @global_filter = nil
40
+ @disabled_tags = {}
29
41
 
30
42
  self.exception_renderer = Template.default_exception_renderer
31
43
  if rethrow_errors
32
- self.exception_renderer = ->(e) { raise }
44
+ self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
33
45
  end
34
46
 
35
- @interrupts = []
36
- @filters = []
37
- @global_filter = nil
47
+ yield self if block_given?
48
+
49
+ # Do this last, since it could result in this object being passed to a Proc in the environment
50
+ squash_instance_assigns_with_environments
38
51
  end
52
+ # rubocop:enable Metrics/ParameterLists
39
53
 
40
54
  def warnings
41
55
  @warnings ||= []
42
56
  end
43
57
 
44
58
  def strainer
45
- @strainer ||= Strainer.create(self, @filters)
59
+ @strainer ||= StrainerFactory.create(self, @filters)
46
60
  end
47
61
 
48
62
  # Adds filters to this context.
@@ -74,10 +88,10 @@ module Liquid
74
88
  @interrupts.pop
75
89
  end
76
90
 
77
- def handle_error(e, line_number = nil, raw_token = nil)
91
+ def handle_error(e, line_number = nil)
78
92
  e = internal_error unless e.is_a?(Liquid::Error)
79
93
  e.template_name ||= template_name
80
- e.line_number ||= line_number
94
+ e.line_number ||= line_number
81
95
  errors.push(e)
82
96
  exception_renderer.call(e).to_s
83
97
  end
@@ -89,7 +103,7 @@ module Liquid
89
103
  # Push new local scope on the stack. use <tt>Context#stack</tt> instead
90
104
  def push(new_scope = {})
91
105
  @scopes.unshift(new_scope)
92
- raise StackLevelError, "Nesting too deep".freeze if @scopes.length > 100
106
+ check_overflow
93
107
  end
94
108
 
95
109
  # Merge a hash of variables in the current local scope
@@ -111,19 +125,31 @@ module Liquid
111
125
  # end
112
126
  #
113
127
  # context['var] #=> nil
114
- def stack(new_scope = nil)
115
- old_stack_used = @this_stack_used
116
- if new_scope
117
- push(new_scope)
118
- @this_stack_used = true
119
- else
120
- @this_stack_used = false
121
- end
122
-
128
+ def stack(new_scope = {})
129
+ push(new_scope)
123
130
  yield
124
131
  ensure
125
- pop if @this_stack_used
126
- @this_stack_used = old_stack_used
132
+ pop
133
+ end
134
+
135
+ # Creates a new context inheriting resource limits, filters, environment etc.,
136
+ # but with an isolated scope.
137
+ def new_isolated_subcontext
138
+ check_overflow
139
+
140
+ self.class.build(
141
+ resource_limits: resource_limits,
142
+ static_environments: static_environments,
143
+ registers: StaticRegisters.new(registers)
144
+ ).tap do |subcontext|
145
+ subcontext.base_scope_depth = base_scope_depth + 1
146
+ subcontext.exception_renderer = exception_renderer
147
+ subcontext.filters = @filters
148
+ subcontext.strainer = nil
149
+ subcontext.errors = errors
150
+ subcontext.warnings = warnings
151
+ subcontext.disabled_tags = @disabled_tags
152
+ end
127
153
  end
128
154
 
129
155
  def clear_instance_assigns
@@ -132,10 +158,6 @@ module Liquid
132
158
 
133
159
  # Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
134
160
  def []=(key, value)
135
- unless @this_stack_used
136
- @this_stack_used = true
137
- push({})
138
- end
139
161
  @scopes[0][key] = value
140
162
  end
141
163
 
@@ -160,49 +182,84 @@ module Liquid
160
182
  end
161
183
 
162
184
  # Fetches an object starting at the local scope and then moving up the hierachy
163
- def find_variable(key)
185
+ def find_variable(key, raise_on_not_found: true)
164
186
  # This was changed from find() to find_index() because this is a very hot
165
187
  # path and find_index() is optimized in MRI to reduce object allocation
166
188
  index = @scopes.find_index { |s| s.key?(key) }
167
- scope = @scopes[index] if index
168
-
169
- variable = nil
170
189
 
171
- if scope.nil?
172
- @environments.each do |e|
173
- variable = lookup_and_evaluate(e, key)
174
- unless variable.nil?
175
- scope = e
176
- break
177
- end
178
- end
190
+ variable = if index
191
+ lookup_and_evaluate(@scopes[index], key, raise_on_not_found: raise_on_not_found)
192
+ else
193
+ try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
179
194
  end
180
195
 
181
- scope ||= @environments.last || @scopes.last
182
- variable ||= lookup_and_evaluate(scope, key)
183
-
184
- variable = variable.to_liquid
196
+ variable = variable.to_liquid
185
197
  variable.context = self if variable.respond_to?(:context=)
186
198
 
187
199
  variable
188
200
  end
189
201
 
190
- def lookup_and_evaluate(obj, key)
191
- if @strict_variables && obj.respond_to?(:key?) && !obj.key?(key)
202
+ def lookup_and_evaluate(obj, key, raise_on_not_found: true)
203
+ if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
192
204
  raise Liquid::UndefinedVariable, "undefined variable #{key}"
193
205
  end
194
206
 
195
207
  value = obj[key]
196
208
 
197
209
  if value.is_a?(Proc) && obj.respond_to?(:[]=)
198
- obj[key] = (value.arity == 0) ? value.call : value.call(self)
210
+ obj[key] = value.arity == 0 ? value.call : value.call(self)
199
211
  else
200
212
  value
201
213
  end
202
214
  end
203
215
 
216
+ def with_disabled_tags(tag_names)
217
+ tag_names.each do |name|
218
+ @disabled_tags[name] = @disabled_tags.fetch(name, 0) + 1
219
+ end
220
+ yield
221
+ ensure
222
+ tag_names.each do |name|
223
+ @disabled_tags[name] -= 1
224
+ end
225
+ end
226
+
227
+ def tag_disabled?(tag_name)
228
+ @disabled_tags.fetch(tag_name, 0) > 0
229
+ end
230
+
231
+ protected
232
+
233
+ attr_writer :base_scope_depth, :warnings, :errors, :strainer, :filters, :disabled_tags
234
+
204
235
  private
205
236
 
237
+ attr_reader :base_scope_depth
238
+
239
+ def try_variable_find_in_environments(key, raise_on_not_found:)
240
+ @environments.each do |environment|
241
+ found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
242
+ if !found_variable.nil? || @strict_variables && raise_on_not_found
243
+ return found_variable
244
+ end
245
+ end
246
+ @static_environments.each do |environment|
247
+ found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
248
+ if !found_variable.nil? || @strict_variables && raise_on_not_found
249
+ return found_variable
250
+ end
251
+ end
252
+ nil
253
+ end
254
+
255
+ def check_overflow
256
+ raise StackLevelError, "Nesting too deep" if overflow?
257
+ end
258
+
259
+ def overflow?
260
+ base_scope_depth + @scopes.length > Block::MAX_DEPTH
261
+ end
262
+
206
263
  def internal_error
207
264
  # raise and catch to set backtrace and cause on exception
208
265
  raise Liquid::InternalError, 'internal'
@@ -1,26 +1,64 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Liquid
2
- class Document < BlockBody
4
+ class Document
3
5
  def self.parse(tokens, parse_context)
4
- doc = new
6
+ doc = new(parse_context)
5
7
  doc.parse(tokens, parse_context)
6
8
  doc
7
9
  end
8
10
 
9
- def parse(tokens, parse_context)
10
- super do |end_tag_name, end_tag_params|
11
- unknown_tag(end_tag_name, parse_context) if end_tag_name
11
+ attr_reader :parse_context, :body
12
+
13
+ def initialize(parse_context)
14
+ @parse_context = parse_context
15
+ @body = new_body
16
+ end
17
+
18
+ def nodelist
19
+ @body.nodelist
20
+ end
21
+
22
+ def parse(tokenizer, parse_context)
23
+ while parse_body(tokenizer)
12
24
  end
25
+ @body.freeze
13
26
  rescue SyntaxError => e
14
27
  e.line_number ||= parse_context.line_number
15
28
  raise
16
29
  end
17
30
 
18
- def unknown_tag(tag, parse_context)
31
+ def unknown_tag(tag, _markup, _tokenizer)
19
32
  case tag
20
- when 'else'.freeze, 'end'.freeze
21
- raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_outer_tag".freeze, tag: tag))
33
+ when 'else', 'end'
34
+ raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_outer_tag", tag: tag)
22
35
  else
23
- raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
36
+ raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
37
+ end
38
+ end
39
+
40
+ def render_to_output_buffer(context, output)
41
+ @body.render_to_output_buffer(context, output)
42
+ end
43
+
44
+ def render(context)
45
+ render_to_output_buffer(context, +'')
46
+ end
47
+
48
+ private
49
+
50
+ def new_body
51
+ parse_context.new_block_body
52
+ end
53
+
54
+ def parse_body(tokenizer)
55
+ @body.parse(tokenizer, parse_context) do |unknown_tag_name, unknown_tag_markup|
56
+ if unknown_tag_name
57
+ unknown_tag(unknown_tag_name, unknown_tag_markup, tokenizer)
58
+ true
59
+ else
60
+ false
61
+ end
24
62
  end
25
63
  end
26
64
  end