liquid 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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