liquid-4-0-2 4.0.2
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.
- checksums.yaml +7 -0
- data/History.md +235 -0
- data/LICENSE +20 -0
- data/README.md +108 -0
- data/lib/liquid.rb +80 -0
- data/lib/liquid/block.rb +77 -0
- data/lib/liquid/block_body.rb +142 -0
- data/lib/liquid/condition.rb +151 -0
- data/lib/liquid/context.rb +226 -0
- data/lib/liquid/document.rb +27 -0
- data/lib/liquid/drop.rb +78 -0
- data/lib/liquid/errors.rb +56 -0
- data/lib/liquid/expression.rb +49 -0
- data/lib/liquid/extensions.rb +74 -0
- data/lib/liquid/file_system.rb +73 -0
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +39 -0
- data/lib/liquid/interrupts.rb +16 -0
- data/lib/liquid/lexer.rb +55 -0
- data/lib/liquid/locales/en.yml +26 -0
- data/lib/liquid/parse_context.rb +38 -0
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +90 -0
- data/lib/liquid/parser_switching.rb +31 -0
- data/lib/liquid/profiler.rb +158 -0
- data/lib/liquid/profiler/hooks.rb +23 -0
- data/lib/liquid/range_lookup.rb +37 -0
- data/lib/liquid/resource_limits.rb +23 -0
- data/lib/liquid/standardfilters.rb +485 -0
- data/lib/liquid/strainer.rb +66 -0
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +43 -0
- data/lib/liquid/tags/assign.rb +59 -0
- data/lib/liquid/tags/break.rb +18 -0
- data/lib/liquid/tags/capture.rb +38 -0
- data/lib/liquid/tags/case.rb +94 -0
- data/lib/liquid/tags/comment.rb +16 -0
- data/lib/liquid/tags/continue.rb +18 -0
- data/lib/liquid/tags/cycle.rb +65 -0
- data/lib/liquid/tags/decrement.rb +35 -0
- data/lib/liquid/tags/for.rb +203 -0
- data/lib/liquid/tags/if.rb +122 -0
- data/lib/liquid/tags/ifchanged.rb +18 -0
- data/lib/liquid/tags/include.rb +124 -0
- data/lib/liquid/tags/increment.rb +31 -0
- data/lib/liquid/tags/raw.rb +47 -0
- data/lib/liquid/tags/table_row.rb +62 -0
- data/lib/liquid/tags/unless.rb +30 -0
- data/lib/liquid/template.rb +254 -0
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/utils.rb +83 -0
- data/lib/liquid/variable.rb +148 -0
- data/lib/liquid/variable_lookup.rb +88 -0
- data/lib/liquid/version.rb +4 -0
- data/test/fixtures/en_locale.yml +9 -0
- data/test/integration/assign_test.rb +48 -0
- data/test/integration/blank_test.rb +106 -0
- data/test/integration/block_test.rb +12 -0
- data/test/integration/capture_test.rb +50 -0
- data/test/integration/context_test.rb +32 -0
- data/test/integration/document_test.rb +19 -0
- data/test/integration/drop_test.rb +273 -0
- data/test/integration/error_handling_test.rb +260 -0
- data/test/integration/filter_test.rb +178 -0
- data/test/integration/hash_ordering_test.rb +23 -0
- data/test/integration/output_test.rb +123 -0
- data/test/integration/parse_tree_visitor_test.rb +247 -0
- data/test/integration/parsing_quirks_test.rb +122 -0
- data/test/integration/render_profiling_test.rb +154 -0
- data/test/integration/security_test.rb +80 -0
- data/test/integration/standard_filter_test.rb +698 -0
- data/test/integration/tags/break_tag_test.rb +15 -0
- data/test/integration/tags/continue_tag_test.rb +15 -0
- data/test/integration/tags/for_tag_test.rb +410 -0
- data/test/integration/tags/if_else_tag_test.rb +188 -0
- data/test/integration/tags/include_tag_test.rb +245 -0
- data/test/integration/tags/increment_tag_test.rb +23 -0
- data/test/integration/tags/raw_tag_test.rb +31 -0
- data/test/integration/tags/standard_tag_test.rb +296 -0
- data/test/integration/tags/statements_test.rb +111 -0
- data/test/integration/tags/table_row_test.rb +64 -0
- data/test/integration/tags/unless_else_tag_test.rb +26 -0
- data/test/integration/template_test.rb +332 -0
- data/test/integration/trim_mode_test.rb +529 -0
- data/test/integration/variable_test.rb +96 -0
- data/test/test_helper.rb +116 -0
- data/test/unit/block_unit_test.rb +58 -0
- data/test/unit/condition_unit_test.rb +166 -0
- data/test/unit/context_unit_test.rb +489 -0
- data/test/unit/file_system_unit_test.rb +35 -0
- data/test/unit/i18n_unit_test.rb +37 -0
- data/test/unit/lexer_unit_test.rb +51 -0
- data/test/unit/parser_unit_test.rb +82 -0
- data/test/unit/regexp_unit_test.rb +44 -0
- data/test/unit/strainer_unit_test.rb +164 -0
- data/test/unit/tag_unit_test.rb +21 -0
- data/test/unit/tags/case_tag_unit_test.rb +10 -0
- data/test/unit/tags/for_tag_unit_test.rb +13 -0
- data/test/unit/tags/if_tag_unit_test.rb +8 -0
- data/test/unit/template_unit_test.rb +78 -0
- data/test/unit/tokenizer_unit_test.rb +55 -0
- data/test/unit/variable_unit_test.rb +162 -0
- metadata +224 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
module Liquid
|
2
|
+
# decrement is used in a place where one needs to insert a counter
|
3
|
+
# into a template, and needs the counter to survive across
|
4
|
+
# multiple instantiations of the template.
|
5
|
+
# NOTE: decrement is a pre-decrement, --i,
|
6
|
+
# while increment is post: i++.
|
7
|
+
#
|
8
|
+
# (To achieve the survival, the application must keep the context)
|
9
|
+
#
|
10
|
+
# if the variable does not exist, it is created with value 0.
|
11
|
+
|
12
|
+
# Hello: {% decrement variable %}
|
13
|
+
#
|
14
|
+
# gives you:
|
15
|
+
#
|
16
|
+
# Hello: -1
|
17
|
+
# Hello: -2
|
18
|
+
# Hello: -3
|
19
|
+
#
|
20
|
+
class Decrement < Tag
|
21
|
+
def initialize(tag_name, markup, options)
|
22
|
+
super
|
23
|
+
@variable = markup.strip
|
24
|
+
end
|
25
|
+
|
26
|
+
def render(context)
|
27
|
+
value = context.environments.first[@variable] ||= 0
|
28
|
+
value -= 1
|
29
|
+
context.environments.first[@variable] = value
|
30
|
+
value.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Template.register_tag('decrement'.freeze, Decrement)
|
35
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module Liquid
|
2
|
+
# "For" iterates over an array or collection.
|
3
|
+
# Several useful variables are available to you within the loop.
|
4
|
+
#
|
5
|
+
# == Basic usage:
|
6
|
+
# {% for item in collection %}
|
7
|
+
# {{ forloop.index }}: {{ item.name }}
|
8
|
+
# {% endfor %}
|
9
|
+
#
|
10
|
+
# == Advanced usage:
|
11
|
+
# {% for item in collection %}
|
12
|
+
# <div {% if forloop.first %}class="first"{% endif %}>
|
13
|
+
# Item {{ forloop.index }}: {{ item.name }}
|
14
|
+
# </div>
|
15
|
+
# {% else %}
|
16
|
+
# There is nothing in the collection.
|
17
|
+
# {% endfor %}
|
18
|
+
#
|
19
|
+
# You can also define a limit and offset much like SQL. Remember
|
20
|
+
# that offset starts at 0 for the first item.
|
21
|
+
#
|
22
|
+
# {% for item in collection limit:5 offset:10 %}
|
23
|
+
# {{ item.name }}
|
24
|
+
# {% end %}
|
25
|
+
#
|
26
|
+
# To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
|
27
|
+
#
|
28
|
+
# == Available variables:
|
29
|
+
#
|
30
|
+
# forloop.name:: 'item-collection'
|
31
|
+
# forloop.length:: Length of the loop
|
32
|
+
# forloop.index:: The current item's position in the collection;
|
33
|
+
# forloop.index starts at 1.
|
34
|
+
# This is helpful for non-programmers who start believe
|
35
|
+
# the first item in an array is 1, not 0.
|
36
|
+
# forloop.index0:: The current item's position in the collection
|
37
|
+
# where the first item is 0
|
38
|
+
# forloop.rindex:: Number of items remaining in the loop
|
39
|
+
# (length - index) where 1 is the last item.
|
40
|
+
# forloop.rindex0:: Number of items remaining in the loop
|
41
|
+
# where 0 is the last item.
|
42
|
+
# forloop.first:: Returns true if the item is the first item.
|
43
|
+
# forloop.last:: Returns true if the item is the last item.
|
44
|
+
# forloop.parentloop:: Provides access to the parent loop, if present.
|
45
|
+
#
|
46
|
+
class For < Block
|
47
|
+
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
48
|
+
|
49
|
+
attr_reader :collection_name, :variable_name, :limit, :from
|
50
|
+
|
51
|
+
def initialize(tag_name, markup, options)
|
52
|
+
super
|
53
|
+
@from = @limit = nil
|
54
|
+
parse_with_selected_parser(markup)
|
55
|
+
@for_block = BlockBody.new
|
56
|
+
@else_block = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse(tokens)
|
60
|
+
return unless parse_body(@for_block, tokens)
|
61
|
+
parse_body(@else_block, tokens)
|
62
|
+
end
|
63
|
+
|
64
|
+
def nodelist
|
65
|
+
@else_block ? [@for_block, @else_block] : [@for_block]
|
66
|
+
end
|
67
|
+
|
68
|
+
def unknown_tag(tag, markup, tokens)
|
69
|
+
return super unless tag == 'else'.freeze
|
70
|
+
@else_block = BlockBody.new
|
71
|
+
end
|
72
|
+
|
73
|
+
def render(context)
|
74
|
+
segment = collection_segment(context)
|
75
|
+
|
76
|
+
if segment.empty?
|
77
|
+
render_else(context)
|
78
|
+
else
|
79
|
+
render_segment(context, segment)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
def lax_parse(markup)
|
86
|
+
if markup =~ Syntax
|
87
|
+
@variable_name = $1
|
88
|
+
collection_name = $2
|
89
|
+
@reversed = !!$3
|
90
|
+
@name = "#{@variable_name}-#{collection_name}"
|
91
|
+
@collection_name = Expression.parse(collection_name)
|
92
|
+
markup.scan(TagAttributes) do |key, value|
|
93
|
+
set_attribute(key, value)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
raise SyntaxError.new(options[:locale].t("errors.syntax.for".freeze))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def strict_parse(markup)
|
101
|
+
p = Parser.new(markup)
|
102
|
+
@variable_name = p.consume(:id)
|
103
|
+
raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
|
104
|
+
collection_name = p.expression
|
105
|
+
@name = "#{@variable_name}-#{collection_name}"
|
106
|
+
@collection_name = Expression.parse(collection_name)
|
107
|
+
@reversed = p.id?('reversed'.freeze)
|
108
|
+
|
109
|
+
while p.look(:id) && p.look(:colon, 1)
|
110
|
+
unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
|
111
|
+
raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze))
|
112
|
+
end
|
113
|
+
p.consume
|
114
|
+
set_attribute(attribute, p.expression)
|
115
|
+
end
|
116
|
+
p.consume(:end_of_string)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def collection_segment(context)
|
122
|
+
offsets = context.registers[:for] ||= {}
|
123
|
+
|
124
|
+
from = if @from == :continue
|
125
|
+
offsets[@name].to_i
|
126
|
+
else
|
127
|
+
context.evaluate(@from).to_i
|
128
|
+
end
|
129
|
+
|
130
|
+
collection = context.evaluate(@collection_name)
|
131
|
+
collection = collection.to_a if collection.is_a?(Range)
|
132
|
+
|
133
|
+
limit = context.evaluate(@limit)
|
134
|
+
to = limit ? limit.to_i + from : nil
|
135
|
+
|
136
|
+
segment = Utils.slice_collection(collection, from, to)
|
137
|
+
segment.reverse! if @reversed
|
138
|
+
|
139
|
+
offsets[@name] = from + segment.length
|
140
|
+
|
141
|
+
segment
|
142
|
+
end
|
143
|
+
|
144
|
+
def render_segment(context, segment)
|
145
|
+
for_stack = context.registers[:for_stack] ||= []
|
146
|
+
length = segment.length
|
147
|
+
|
148
|
+
result = ''
|
149
|
+
|
150
|
+
context.stack do
|
151
|
+
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
152
|
+
|
153
|
+
for_stack.push(loop_vars)
|
154
|
+
|
155
|
+
begin
|
156
|
+
context['forloop'.freeze] = loop_vars
|
157
|
+
|
158
|
+
segment.each do |item|
|
159
|
+
context[@variable_name] = item
|
160
|
+
result << @for_block.render(context)
|
161
|
+
loop_vars.send(:increment!)
|
162
|
+
|
163
|
+
# Handle any interrupts if they exist.
|
164
|
+
if context.interrupt?
|
165
|
+
interrupt = context.pop_interrupt
|
166
|
+
break if interrupt.is_a? BreakInterrupt
|
167
|
+
next if interrupt.is_a? ContinueInterrupt
|
168
|
+
end
|
169
|
+
end
|
170
|
+
ensure
|
171
|
+
for_stack.pop
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
result
|
176
|
+
end
|
177
|
+
|
178
|
+
def set_attribute(key, expr)
|
179
|
+
case key
|
180
|
+
when 'offset'.freeze
|
181
|
+
@from = if expr == 'continue'.freeze
|
182
|
+
:continue
|
183
|
+
else
|
184
|
+
Expression.parse(expr)
|
185
|
+
end
|
186
|
+
when 'limit'.freeze
|
187
|
+
@limit = Expression.parse(expr)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def render_else(context)
|
192
|
+
@else_block ? @else_block.render(context) : ''.freeze
|
193
|
+
end
|
194
|
+
|
195
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
196
|
+
def children
|
197
|
+
(super + [@node.limit, @node.from, @node.collection_name]).compact
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
Template.register_tag('for'.freeze, For)
|
203
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Liquid
|
2
|
+
# If is the conditional block
|
3
|
+
#
|
4
|
+
# {% if user.admin %}
|
5
|
+
# Admin user!
|
6
|
+
# {% else %}
|
7
|
+
# Not admin user
|
8
|
+
# {% endif %}
|
9
|
+
#
|
10
|
+
# There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
|
11
|
+
#
|
12
|
+
class If < Block
|
13
|
+
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
14
|
+
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
15
|
+
BOOLEAN_OPERATORS = %w(and or)
|
16
|
+
|
17
|
+
attr_reader :blocks
|
18
|
+
|
19
|
+
def initialize(tag_name, markup, options)
|
20
|
+
super
|
21
|
+
@blocks = []
|
22
|
+
push_block('if'.freeze, markup)
|
23
|
+
end
|
24
|
+
|
25
|
+
def nodelist
|
26
|
+
@blocks.map(&:attachment)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse(tokens)
|
30
|
+
while parse_body(@blocks.last.attachment, tokens)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def unknown_tag(tag, markup, tokens)
|
35
|
+
if ['elsif'.freeze, 'else'.freeze].include?(tag)
|
36
|
+
push_block(tag, markup)
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def render(context)
|
43
|
+
context.stack do
|
44
|
+
@blocks.each do |block|
|
45
|
+
if block.evaluate(context)
|
46
|
+
return block.attachment.render(context)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
''.freeze
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def push_block(tag, markup)
|
56
|
+
block = if tag == 'else'.freeze
|
57
|
+
ElseCondition.new
|
58
|
+
else
|
59
|
+
parse_with_selected_parser(markup)
|
60
|
+
end
|
61
|
+
|
62
|
+
@blocks.push(block)
|
63
|
+
block.attach(BlockBody.new)
|
64
|
+
end
|
65
|
+
|
66
|
+
def lax_parse(markup)
|
67
|
+
expressions = markup.scan(ExpressionsAndOperators)
|
68
|
+
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop =~ Syntax
|
69
|
+
|
70
|
+
condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
|
71
|
+
|
72
|
+
until expressions.empty?
|
73
|
+
operator = expressions.pop.to_s.strip
|
74
|
+
|
75
|
+
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop.to_s =~ Syntax
|
76
|
+
|
77
|
+
new_condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
|
78
|
+
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator)
|
79
|
+
new_condition.send(operator, condition)
|
80
|
+
condition = new_condition
|
81
|
+
end
|
82
|
+
|
83
|
+
condition
|
84
|
+
end
|
85
|
+
|
86
|
+
def strict_parse(markup)
|
87
|
+
p = Parser.new(markup)
|
88
|
+
condition = parse_binary_comparisons(p)
|
89
|
+
p.consume(:end_of_string)
|
90
|
+
condition
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_binary_comparisons(p)
|
94
|
+
condition = parse_comparison(p)
|
95
|
+
first_condition = condition
|
96
|
+
while op = (p.id?('and'.freeze) || p.id?('or'.freeze))
|
97
|
+
child_condition = parse_comparison(p)
|
98
|
+
condition.send(op, child_condition)
|
99
|
+
condition = child_condition
|
100
|
+
end
|
101
|
+
first_condition
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_comparison(p)
|
105
|
+
a = Expression.parse(p.expression)
|
106
|
+
if op = p.consume?(:comparison)
|
107
|
+
b = Expression.parse(p.expression)
|
108
|
+
Condition.new(a, op, b)
|
109
|
+
else
|
110
|
+
Condition.new(a)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
115
|
+
def children
|
116
|
+
@node.blocks
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
Template.register_tag('if'.freeze, If)
|
122
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Liquid
|
2
|
+
class Ifchanged < Block
|
3
|
+
def render(context)
|
4
|
+
context.stack do
|
5
|
+
output = super
|
6
|
+
|
7
|
+
if output != context.registers[:ifchanged]
|
8
|
+
context.registers[:ifchanged] = output
|
9
|
+
output
|
10
|
+
else
|
11
|
+
''.freeze
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Template.register_tag('ifchanged'.freeze, Ifchanged)
|
18
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Liquid
|
2
|
+
# Include allows templates to relate with other templates
|
3
|
+
#
|
4
|
+
# Simply include another template:
|
5
|
+
#
|
6
|
+
# {% include 'product' %}
|
7
|
+
#
|
8
|
+
# Include a template with a local variable:
|
9
|
+
#
|
10
|
+
# {% include 'product' with products[0] %}
|
11
|
+
#
|
12
|
+
# Include a template for a collection:
|
13
|
+
#
|
14
|
+
# {% include 'product' for products %}
|
15
|
+
#
|
16
|
+
class Include < Tag
|
17
|
+
Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
|
18
|
+
|
19
|
+
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
20
|
+
|
21
|
+
def initialize(tag_name, markup, options)
|
22
|
+
super
|
23
|
+
|
24
|
+
if markup =~ Syntax
|
25
|
+
|
26
|
+
template_name = $1
|
27
|
+
variable_name = $3
|
28
|
+
|
29
|
+
@variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
|
30
|
+
@template_name_expr = Expression.parse(template_name)
|
31
|
+
@attributes = {}
|
32
|
+
|
33
|
+
markup.scan(TagAttributes) do |key, value|
|
34
|
+
@attributes[key] = Expression.parse(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
else
|
38
|
+
raise SyntaxError.new(options[:locale].t("errors.syntax.include".freeze))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse(_tokens)
|
43
|
+
end
|
44
|
+
|
45
|
+
def render(context)
|
46
|
+
template_name = context.evaluate(@template_name_expr)
|
47
|
+
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
48
|
+
|
49
|
+
partial = load_cached_partial(template_name, context)
|
50
|
+
context_variable_name = template_name.split('/'.freeze).last
|
51
|
+
|
52
|
+
variable = if @variable_name_expr
|
53
|
+
context.evaluate(@variable_name_expr)
|
54
|
+
else
|
55
|
+
context.find_variable(template_name, raise_on_not_found: false)
|
56
|
+
end
|
57
|
+
|
58
|
+
old_template_name = context.template_name
|
59
|
+
old_partial = context.partial
|
60
|
+
begin
|
61
|
+
context.template_name = template_name
|
62
|
+
context.partial = true
|
63
|
+
context.stack do
|
64
|
+
@attributes.each do |key, value|
|
65
|
+
context[key] = context.evaluate(value)
|
66
|
+
end
|
67
|
+
|
68
|
+
if variable.is_a?(Array)
|
69
|
+
variable.collect do |var|
|
70
|
+
context[context_variable_name] = var
|
71
|
+
partial.render(context)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
context[context_variable_name] = variable
|
75
|
+
partial.render(context)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
ensure
|
79
|
+
context.template_name = old_template_name
|
80
|
+
context.partial = old_partial
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
alias_method :parse_context, :options
|
87
|
+
private :parse_context
|
88
|
+
|
89
|
+
def load_cached_partial(template_name, context)
|
90
|
+
cached_partials = context.registers[:cached_partials] || {}
|
91
|
+
|
92
|
+
if cached = cached_partials[template_name]
|
93
|
+
return cached
|
94
|
+
end
|
95
|
+
source = read_template_from_file_system(context)
|
96
|
+
begin
|
97
|
+
parse_context.partial = true
|
98
|
+
partial = Liquid::Template.parse(source, parse_context)
|
99
|
+
ensure
|
100
|
+
parse_context.partial = false
|
101
|
+
end
|
102
|
+
cached_partials[template_name] = partial
|
103
|
+
context.registers[:cached_partials] = cached_partials
|
104
|
+
partial
|
105
|
+
end
|
106
|
+
|
107
|
+
def read_template_from_file_system(context)
|
108
|
+
file_system = context.registers[:file_system] || Liquid::Template.file_system
|
109
|
+
|
110
|
+
file_system.read_template_file(context.evaluate(@template_name_expr))
|
111
|
+
end
|
112
|
+
|
113
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
114
|
+
def children
|
115
|
+
[
|
116
|
+
@node.template_name_expr,
|
117
|
+
@node.variable_name_expr
|
118
|
+
] + @node.attributes.values
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
Template.register_tag('include'.freeze, Include)
|
124
|
+
end
|