liquid 3.0.0 → 4.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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +130 -62
  3. data/README.md +31 -0
  4. data/lib/liquid/block.rb +31 -124
  5. data/lib/liquid/block_body.rb +75 -59
  6. data/lib/liquid/condition.rb +23 -22
  7. data/lib/liquid/context.rb +51 -60
  8. data/lib/liquid/document.rb +19 -9
  9. data/lib/liquid/drop.rb +17 -16
  10. data/lib/liquid/errors.rb +20 -24
  11. data/lib/liquid/expression.rb +15 -3
  12. data/lib/liquid/extensions.rb +13 -7
  13. data/lib/liquid/file_system.rb +11 -11
  14. data/lib/liquid/forloop_drop.rb +42 -0
  15. data/lib/liquid/i18n.rb +5 -5
  16. data/lib/liquid/interrupts.rb +1 -2
  17. data/lib/liquid/lexer.rb +6 -4
  18. data/lib/liquid/locales/en.yml +5 -1
  19. data/lib/liquid/parse_context.rb +37 -0
  20. data/lib/liquid/parser.rb +1 -1
  21. data/lib/liquid/parser_switching.rb +4 -4
  22. data/lib/liquid/profiler/hooks.rb +7 -7
  23. data/lib/liquid/profiler.rb +18 -19
  24. data/lib/liquid/range_lookup.rb +16 -1
  25. data/lib/liquid/resource_limits.rb +23 -0
  26. data/lib/liquid/standardfilters.rb +121 -61
  27. data/lib/liquid/strainer.rb +32 -29
  28. data/lib/liquid/tablerowloop_drop.rb +62 -0
  29. data/lib/liquid/tag.rb +9 -8
  30. data/lib/liquid/tags/assign.rb +17 -4
  31. data/lib/liquid/tags/break.rb +0 -3
  32. data/lib/liquid/tags/capture.rb +2 -2
  33. data/lib/liquid/tags/case.rb +19 -12
  34. data/lib/liquid/tags/comment.rb +2 -2
  35. data/lib/liquid/tags/cycle.rb +6 -6
  36. data/lib/liquid/tags/decrement.rb +1 -4
  37. data/lib/liquid/tags/for.rb +95 -75
  38. data/lib/liquid/tags/if.rb +48 -43
  39. data/lib/liquid/tags/ifchanged.rb +0 -2
  40. data/lib/liquid/tags/include.rb +61 -52
  41. data/lib/liquid/tags/raw.rb +32 -4
  42. data/lib/liquid/tags/table_row.rb +12 -31
  43. data/lib/liquid/tags/unless.rb +4 -5
  44. data/lib/liquid/template.rb +42 -54
  45. data/lib/liquid/tokenizer.rb +31 -0
  46. data/lib/liquid/utils.rb +52 -8
  47. data/lib/liquid/variable.rb +46 -45
  48. data/lib/liquid/variable_lookup.rb +9 -5
  49. data/lib/liquid/version.rb +1 -1
  50. data/lib/liquid.rb +9 -7
  51. data/test/integration/assign_test.rb +18 -8
  52. data/test/integration/blank_test.rb +14 -14
  53. data/test/integration/capture_test.rb +10 -0
  54. data/test/integration/context_test.rb +2 -2
  55. data/test/integration/document_test.rb +19 -0
  56. data/test/integration/drop_test.rb +42 -40
  57. data/test/integration/error_handling_test.rb +99 -46
  58. data/test/integration/filter_test.rb +72 -19
  59. data/test/integration/hash_ordering_test.rb +9 -9
  60. data/test/integration/output_test.rb +34 -27
  61. data/test/integration/parsing_quirks_test.rb +15 -13
  62. data/test/integration/render_profiling_test.rb +20 -20
  63. data/test/integration/security_test.rb +9 -7
  64. data/test/integration/standard_filter_test.rb +198 -42
  65. data/test/integration/tags/break_tag_test.rb +1 -2
  66. data/test/integration/tags/continue_tag_test.rb +0 -1
  67. data/test/integration/tags/for_tag_test.rb +133 -98
  68. data/test/integration/tags/if_else_tag_test.rb +96 -77
  69. data/test/integration/tags/include_tag_test.rb +34 -30
  70. data/test/integration/tags/increment_tag_test.rb +10 -11
  71. data/test/integration/tags/raw_tag_test.rb +7 -1
  72. data/test/integration/tags/standard_tag_test.rb +121 -122
  73. data/test/integration/tags/statements_test.rb +3 -5
  74. data/test/integration/tags/table_row_test.rb +20 -19
  75. data/test/integration/tags/unless_else_tag_test.rb +6 -6
  76. data/test/integration/template_test.rb +190 -49
  77. data/test/integration/trim_mode_test.rb +525 -0
  78. data/test/integration/variable_test.rb +23 -13
  79. data/test/test_helper.rb +44 -9
  80. data/test/unit/block_unit_test.rb +8 -5
  81. data/test/unit/condition_unit_test.rb +86 -77
  82. data/test/unit/context_unit_test.rb +48 -57
  83. data/test/unit/file_system_unit_test.rb +3 -3
  84. data/test/unit/i18n_unit_test.rb +2 -2
  85. data/test/unit/lexer_unit_test.rb +11 -8
  86. data/test/unit/parser_unit_test.rb +2 -2
  87. data/test/unit/regexp_unit_test.rb +1 -1
  88. data/test/unit/strainer_unit_test.rb +85 -8
  89. data/test/unit/tag_unit_test.rb +7 -2
  90. data/test/unit/tags/case_tag_unit_test.rb +1 -1
  91. data/test/unit/tags/for_tag_unit_test.rb +2 -2
  92. data/test/unit/tags/if_tag_unit_test.rb +1 -1
  93. data/test/unit/template_unit_test.rb +14 -5
  94. data/test/unit/tokenizer_unit_test.rb +24 -7
  95. data/test/unit/variable_unit_test.rb +66 -43
  96. metadata +55 -50
  97. data/lib/liquid/module_ex.rb +0 -62
  98. data/lib/liquid/token.rb +0 -18
  99. data/test/unit/module_ex_unit_test.rb +0 -87
  100. /data/{MIT-LICENSE → LICENSE} +0 -0
@@ -20,10 +20,10 @@ module Liquid
20
20
  case markup
21
21
  when NamedSyntax
22
22
  @variables = variables_from_string($2)
23
- @name = $1
23
+ @name = Expression.parse($1)
24
24
  when SimpleSyntax
25
25
  @variables = variables_from_string(markup)
26
- @name = "'#{@variables.to_s}'"
26
+ @name = @variables.to_s
27
27
  else
28
28
  raise SyntaxError.new(options[:locale].t("errors.syntax.cycle".freeze))
29
29
  end
@@ -33,11 +33,11 @@ module Liquid
33
33
  context.registers[:cycle] ||= Hash.new(0)
34
34
 
35
35
  context.stack do
36
- key = context[@name]
36
+ key = context.evaluate(@name)
37
37
  iteration = context.registers[:cycle][key]
38
- result = context[@variables[iteration]]
38
+ result = context.evaluate(@variables[iteration])
39
39
  iteration += 1
40
- iteration = 0 if iteration >= @variables.size
40
+ iteration = 0 if iteration >= @variables.size
41
41
  context.registers[:cycle][key] = iteration
42
42
  result
43
43
  end
@@ -48,7 +48,7 @@ module Liquid
48
48
  def variables_from_string(markup)
49
49
  markup.split(',').collect do |var|
50
50
  var =~ /\s*(#{QuotedFragment})\s*/o
51
- $1 ? $1 : nil
51
+ $1 ? Expression.parse($1) : nil
52
52
  end.compact
53
53
  end
54
54
  end
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # decrement is used in a place where one needs to insert a counter
4
3
  # into a template, and needs the counter to survive across
5
4
  # multiple instantiations of the template.
@@ -26,12 +25,10 @@ module Liquid
26
25
 
27
26
  def render(context)
28
27
  value = context.environments.first[@variable] ||= 0
29
- value = value - 1
28
+ value -= 1
30
29
  context.environments.first[@variable] = value
31
30
  value.to_s
32
31
  end
33
-
34
- private
35
32
  end
36
33
 
37
34
  Template.register_tag('decrement'.freeze, Decrement)
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # "For" iterates over an array or collection.
4
3
  # Several useful variables are available to you within the loop.
5
4
  #
@@ -42,85 +41,41 @@ module Liquid
42
41
  # where 0 is the last item.
43
42
  # forloop.first:: Returns true if the item is the first item.
44
43
  # forloop.last:: Returns true if the item is the last item.
44
+ # forloop.parentloop:: Provides access to the parent loop, if present.
45
45
  #
46
46
  class For < Block
47
47
  Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
48
48
 
49
49
  def initialize(tag_name, markup, options)
50
50
  super
51
+ @from = @limit = nil
51
52
  parse_with_selected_parser(markup)
52
- @nodelist = @for_block = []
53
+ @for_block = BlockBody.new
54
+ @else_block = nil
55
+ end
56
+
57
+ def parse(tokens)
58
+ return unless parse_body(@for_block, tokens)
59
+ parse_body(@else_block, tokens)
53
60
  end
54
61
 
55
62
  def nodelist
56
- if @else_block
57
- @for_block + @else_block
58
- else
59
- @for_block
60
- end
63
+ @else_block ? [@for_block, @else_block] : [@for_block]
61
64
  end
62
65
 
63
66
  def unknown_tag(tag, markup, tokens)
64
67
  return super unless tag == 'else'.freeze
65
- @nodelist = @else_block = []
68
+ @else_block = BlockBody.new
66
69
  end
67
70
 
68
71
  def render(context)
69
- context.registers[:for] ||= Hash.new(0)
70
-
71
- collection = context[@collection_name]
72
- collection = collection.to_a if collection.is_a?(Range)
73
-
74
- # Maintains Ruby 1.8.7 String#each behaviour on 1.9
75
- return render_else(context) unless iterable?(collection)
72
+ segment = collection_segment(context)
76
73
 
77
- from = if @attributes['offset'.freeze] == 'continue'.freeze
78
- context.registers[:for][@name].to_i
74
+ if segment.empty?
75
+ render_else(context)
79
76
  else
80
- context[@attributes['offset'.freeze]].to_i
77
+ render_segment(context, segment)
81
78
  end
82
-
83
- limit = context[@attributes['limit'.freeze]]
84
- to = limit ? limit.to_i + from : nil
85
-
86
- segment = Utils.slice_collection(collection, from, to)
87
-
88
- return render_else(context) if segment.empty?
89
-
90
- segment.reverse! if @reversed
91
-
92
- result = ''
93
-
94
- length = segment.length
95
-
96
- # Store our progress through the collection for the continue flag
97
- context.registers[:for][@name] = from + segment.length
98
-
99
- context.stack do
100
- segment.each_with_index do |item, index|
101
- context[@variable_name] = item
102
- context['forloop'.freeze] = {
103
- 'name'.freeze => @name,
104
- 'length'.freeze => length,
105
- 'index'.freeze => index + 1,
106
- 'index0'.freeze => index,
107
- 'rindex'.freeze => length - index,
108
- 'rindex0'.freeze => length - index - 1,
109
- 'first'.freeze => (index == 0),
110
- 'last'.freeze => (index == length - 1)
111
- }
112
-
113
- result << render_all(@for_block, context)
114
-
115
- # Handle any interrupts if they exist.
116
- if context.has_interrupt?
117
- interrupt = context.pop_interrupt
118
- break if interrupt.is_a? BreakInterrupt
119
- next if interrupt.is_a? ContinueInterrupt
120
- end
121
- end
122
- end
123
- result
124
79
  end
125
80
 
126
81
  protected
@@ -128,12 +83,12 @@ module Liquid
128
83
  def lax_parse(markup)
129
84
  if markup =~ Syntax
130
85
  @variable_name = $1
131
- @collection_name = $2
132
- @name = "#{$1}-#{$2}"
133
- @reversed = $3
134
- @attributes = {}
86
+ collection_name = $2
87
+ @reversed = !!$3
88
+ @name = "#{@variable_name}-#{collection_name}"
89
+ @collection_name = Expression.parse(collection_name)
135
90
  markup.scan(TagAttributes) do |key, value|
136
- @attributes[key] = value
91
+ set_attribute(key, value)
137
92
  end
138
93
  else
139
94
  raise SyntaxError.new(options[:locale].t("errors.syntax.for".freeze))
@@ -143,31 +98,96 @@ module Liquid
143
98
  def strict_parse(markup)
144
99
  p = Parser.new(markup)
145
100
  @variable_name = p.consume(:id)
146
- raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
147
- @collection_name = p.expression
148
- @name = "#{@variable_name}-#{@collection_name}"
101
+ raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
102
+ collection_name = p.expression
103
+ @name = "#{@variable_name}-#{collection_name}"
104
+ @collection_name = Expression.parse(collection_name)
149
105
  @reversed = p.id?('reversed'.freeze)
150
106
 
151
- @attributes = {}
152
107
  while p.look(:id) && p.look(:colon, 1)
153
108
  unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
154
109
  raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze))
155
110
  end
156
111
  p.consume
157
- val = p.expression
158
- @attributes[attribute] = val
112
+ set_attribute(attribute, p.expression)
159
113
  end
160
114
  p.consume(:end_of_string)
161
115
  end
162
116
 
163
117
  private
164
118
 
165
- def render_else(context)
166
- return @else_block ? [render_all(@else_block, context)] : ''.freeze
119
+ def collection_segment(context)
120
+ offsets = context.registers[:for] ||= Hash.new(0)
121
+
122
+ from = if @from == :continue
123
+ offsets[@name].to_i
124
+ else
125
+ context.evaluate(@from).to_i
126
+ end
127
+
128
+ collection = context.evaluate(@collection_name)
129
+ collection = collection.to_a if collection.is_a?(Range)
130
+
131
+ limit = context.evaluate(@limit)
132
+ to = limit ? limit.to_i + from : nil
133
+
134
+ segment = Utils.slice_collection(collection, from, to)
135
+ segment.reverse! if @reversed
136
+
137
+ offsets[@name] = from + segment.length
138
+
139
+ segment
167
140
  end
168
141
 
169
- def iterable?(collection)
170
- collection.respond_to?(:each) || Utils.non_blank_string?(collection)
142
+ def render_segment(context, segment)
143
+ for_stack = context.registers[:for_stack] ||= []
144
+ length = segment.length
145
+
146
+ result = ''
147
+
148
+ context.stack do
149
+ loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
150
+
151
+ for_stack.push(loop_vars)
152
+
153
+ begin
154
+ context['forloop'.freeze] = loop_vars
155
+
156
+ segment.each_with_index do |item, index|
157
+ context[@variable_name] = item
158
+ result << @for_block.render(context)
159
+ loop_vars.send(:increment!)
160
+
161
+ # Handle any interrupts if they exist.
162
+ if context.interrupt?
163
+ interrupt = context.pop_interrupt
164
+ break if interrupt.is_a? BreakInterrupt
165
+ next if interrupt.is_a? ContinueInterrupt
166
+ end
167
+ end
168
+ ensure
169
+ for_stack.pop
170
+ end
171
+ end
172
+
173
+ result
174
+ end
175
+
176
+ def set_attribute(key, expr)
177
+ case key
178
+ when 'offset'.freeze
179
+ @from = if expr == 'continue'.freeze
180
+ :continue
181
+ else
182
+ Expression.parse(expr)
183
+ end
184
+ when 'limit'.freeze
185
+ @limit = Expression.parse(expr)
186
+ end
187
+ end
188
+
189
+ def render_else(context)
190
+ @else_block ? @else_block.render(context) : ''.freeze
171
191
  end
172
192
  end
173
193
 
@@ -20,8 +20,13 @@ module Liquid
20
20
  push_block('if'.freeze, markup)
21
21
  end
22
22
 
23
+ def parse(tokens)
24
+ while parse_body(@blocks.last.attachment, tokens)
25
+ end
26
+ end
27
+
23
28
  def nodelist
24
- @blocks.flat_map(&:attachment)
29
+ @blocks.map(&:attachment)
25
30
  end
26
31
 
27
32
  def unknown_tag(tag, markup, tokens)
@@ -36,7 +41,7 @@ module Liquid
36
41
  context.stack do
37
42
  @blocks.each do |block|
38
43
  if block.evaluate(context)
39
- return render_all(block.attachment, context)
44
+ return block.attachment.render(context)
40
45
  end
41
46
  end
42
47
  ''.freeze
@@ -45,61 +50,61 @@ module Liquid
45
50
 
46
51
  private
47
52
 
48
- def push_block(tag, markup)
49
- block = if tag == 'else'.freeze
50
- ElseCondition.new
51
- else
52
- parse_with_selected_parser(markup)
53
- end
54
-
55
- @blocks.push(block)
56
- @nodelist = block.attach(Array.new)
53
+ def push_block(tag, markup)
54
+ block = if tag == 'else'.freeze
55
+ ElseCondition.new
56
+ else
57
+ parse_with_selected_parser(markup)
57
58
  end
58
59
 
59
- def lax_parse(markup)
60
- expressions = markup.scan(ExpressionsAndOperators)
61
- raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop =~ Syntax
60
+ @blocks.push(block)
61
+ block.attach(BlockBody.new)
62
+ end
62
63
 
63
- condition = Condition.new($1, $2, $3)
64
+ def lax_parse(markup)
65
+ expressions = markup.scan(ExpressionsAndOperators)
66
+ raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop =~ Syntax
64
67
 
65
- while not expressions.empty?
66
- operator = expressions.pop.to_s.strip
68
+ condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
67
69
 
68
- raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop.to_s =~ Syntax
70
+ until expressions.empty?
71
+ operator = expressions.pop.to_s.strip
69
72
 
70
- new_condition = Condition.new($1, $2, $3)
71
- raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator)
72
- new_condition.send(operator, condition)
73
- condition = new_condition
74
- end
73
+ raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop.to_s =~ Syntax
75
74
 
76
- condition
75
+ new_condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
76
+ raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator)
77
+ new_condition.send(operator, condition)
78
+ condition = new_condition
77
79
  end
78
80
 
79
- def strict_parse(markup)
80
- p = Parser.new(markup)
81
-
82
- condition = parse_comparison(p)
81
+ condition
82
+ end
83
83
 
84
- while op = (p.id?('and'.freeze) || p.id?('or'.freeze))
85
- new_cond = parse_comparison(p)
86
- new_cond.send(op, condition)
87
- condition = new_cond
88
- end
89
- p.consume(:end_of_string)
84
+ def strict_parse(markup)
85
+ p = Parser.new(markup)
86
+ condition = parse_binary_comparison(p)
87
+ p.consume(:end_of_string)
88
+ condition
89
+ end
90
90
 
91
- condition
91
+ def parse_binary_comparison(p)
92
+ condition = parse_comparison(p)
93
+ if op = (p.id?('and'.freeze) || p.id?('or'.freeze))
94
+ condition.send(op, parse_binary_comparison(p))
92
95
  end
96
+ condition
97
+ end
93
98
 
94
- def parse_comparison(p)
95
- a = p.expression
96
- if op = p.consume?(:comparison)
97
- b = p.expression
98
- Condition.new(a, op, b)
99
- else
100
- Condition.new(a)
101
- end
99
+ def parse_comparison(p)
100
+ a = Expression.parse(p.expression)
101
+ if op = p.consume?(:comparison)
102
+ b = Expression.parse(p.expression)
103
+ Condition.new(a, op, b)
104
+ else
105
+ Condition.new(a)
102
106
  end
107
+ end
103
108
  end
104
109
 
105
110
  Template.register_tag('if'.freeze, If)
@@ -1,9 +1,7 @@
1
1
  module Liquid
2
2
  class Ifchanged < Block
3
-
4
3
  def render(context)
5
4
  context.stack do
6
-
7
5
  output = super
8
6
 
9
7
  if output != context.registers[:ifchanged]
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # Include allows templates to relate with other templates
4
3
  #
5
4
  # Simply include another template:
@@ -22,12 +21,15 @@ module Liquid
22
21
 
23
22
  if markup =~ Syntax
24
23
 
25
- @template_name = $1
26
- @variable_name = $3
27
- @attributes = {}
24
+ template_name = $1
25
+ variable_name = $3
26
+
27
+ @variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
28
+ @template_name_expr = Expression.parse(template_name)
29
+ @attributes = {}
28
30
 
29
31
  markup.scan(TagAttributes) do |key, value|
30
- @attributes[key] = value
32
+ @attributes[key] = Expression.parse(value)
31
33
  end
32
34
 
33
35
  else
@@ -35,69 +37,76 @@ module Liquid
35
37
  end
36
38
  end
37
39
 
38
- def parse(tokens)
40
+ def parse(_tokens)
39
41
  end
40
42
 
41
43
  def render(context)
42
- partial = load_cached_partial(context)
43
- variable = context[@variable_name || @template_name[1..-2]]
44
+ template_name = context.evaluate(@template_name_expr)
45
+ raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
44
46
 
45
- context.stack do
46
- @attributes.each do |key, value|
47
- context[key] = context[value]
48
- end
47
+ partial = load_cached_partial(template_name, context)
48
+ context_variable_name = template_name.split('/'.freeze).last
49
49
 
50
- context_variable_name = @template_name[1..-2].split('/'.freeze).last
51
- if variable.is_a?(Array)
52
- variable.collect do |var|
53
- context[context_variable_name] = var
50
+ variable = if @variable_name_expr
51
+ context.evaluate(@variable_name_expr)
52
+ else
53
+ context.find_variable(template_name)
54
+ end
55
+
56
+ old_template_name = context.template_name
57
+ old_partial = context.partial
58
+ begin
59
+ context.template_name = template_name
60
+ context.partial = true
61
+ context.stack do
62
+ @attributes.each do |key, value|
63
+ context[key] = context.evaluate(value)
64
+ end
65
+
66
+ if variable.is_a?(Array)
67
+ variable.collect do |var|
68
+ context[context_variable_name] = var
69
+ partial.render(context)
70
+ end
71
+ else
72
+ context[context_variable_name] = variable
54
73
  partial.render(context)
55
74
  end
56
- else
57
- context[context_variable_name] = variable
58
- partial.render(context)
59
75
  end
76
+ ensure
77
+ context.template_name = old_template_name
78
+ context.partial = old_partial
60
79
  end
61
80
  end
62
81
 
63
82
  private
64
- def load_cached_partial(context)
65
- cached_partials = context.registers[:cached_partials] || {}
66
- template_name = context[@template_name]
67
83
 
68
- if cached = cached_partials[template_name]
69
- return cached
70
- end
71
- source = read_template_from_file_system(context)
72
- partial = Liquid::Template.parse(source, pass_options)
73
- cached_partials[template_name] = partial
74
- context.registers[:cached_partials] = cached_partials
75
- partial
76
- end
84
+ alias_method :parse_context, :options
85
+ private :parse_context
77
86
 
78
- def read_template_from_file_system(context)
79
- file_system = context.registers[:file_system] || Liquid::Template.file_system
80
-
81
- # make read_template_file call backwards-compatible.
82
- case file_system.method(:read_template_file).arity
83
- when 1
84
- file_system.read_template_file(context[@template_name])
85
- when 2
86
- file_system.read_template_file(context[@template_name], context)
87
- else
88
- raise ArgumentError, "file_system.read_template_file expects two parameters: (template_name, context)"
89
- end
90
- end
87
+ def load_cached_partial(template_name, context)
88
+ cached_partials = context.registers[:cached_partials] || {}
91
89
 
92
- def pass_options
93
- dont_pass = @options[:include_options_blacklist]
94
- return {locale: @options[:locale]} if dont_pass == true
95
- opts = @options.merge(included: true, include_options_blacklist: false)
96
- if dont_pass.is_a?(Array)
97
- dont_pass.each {|o| opts.delete(o)}
98
- end
99
- opts
90
+ if cached = cached_partials[template_name]
91
+ return cached
92
+ end
93
+ source = read_template_from_file_system(context)
94
+ begin
95
+ parse_context.partial = true
96
+ partial = Liquid::Template.parse(source, parse_context)
97
+ ensure
98
+ parse_context.partial = false
100
99
  end
100
+ cached_partials[template_name] = partial
101
+ context.registers[:cached_partials] = cached_partials
102
+ partial
103
+ end
104
+
105
+ def read_template_from_file_system(context)
106
+ file_system = context.registers[:file_system] || Liquid::Template.file_system
107
+
108
+ file_system.read_template_file(context.evaluate(@template_name_expr))
109
+ end
101
110
  end
102
111
 
103
112
  Template.register_tag('include'.freeze, Include)
@@ -1,16 +1,44 @@
1
1
  module Liquid
2
2
  class Raw < Block
3
+ Syntax = /\A\s*\z/
3
4
  FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
4
5
 
6
+ def initialize(tag_name, markup, parse_context)
7
+ super
8
+
9
+ ensure_valid_markup(tag_name, markup, parse_context)
10
+ end
11
+
5
12
  def parse(tokens)
6
- @nodelist ||= []
7
- @nodelist.clear
13
+ @body = ''
8
14
  while token = tokens.shift
9
15
  if token =~ FullTokenPossiblyInvalid
10
- @nodelist << $1 if $1 != "".freeze
16
+ @body << $1 if $1 != "".freeze
11
17
  return if block_delimiter == $2
12
18
  end
13
- @nodelist << token if not token.empty?
19
+ @body << token unless token.empty?
20
+ end
21
+
22
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
23
+ end
24
+
25
+ def render(_context)
26
+ @body
27
+ end
28
+
29
+ def nodelist
30
+ [@body]
31
+ end
32
+
33
+ def blank?
34
+ @body.empty?
35
+ end
36
+
37
+ protected
38
+
39
+ def ensure_valid_markup(tag_name, markup, parse_context)
40
+ unless markup =~ Syntax
41
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_unexpected_args".freeze, tag: tag_name))
14
42
  end
15
43
  end
16
44
  end