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.
- checksums.yaml +4 -4
- data/History.md +32 -4
- data/README.md +6 -0
- data/lib/liquid/block.rb +31 -14
- data/lib/liquid/block_body.rb +164 -54
- data/lib/liquid/condition.rb +39 -18
- data/lib/liquid/context.rb +106 -51
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +4 -2
- data/lib/liquid/errors.rb +20 -18
- data/lib/liquid/expression.rb +29 -34
- data/lib/liquid/extensions.rb +2 -0
- data/lib/liquid/file_system.rb +6 -4
- data/lib/liquid/forloop_drop.rb +11 -4
- data/lib/liquid/i18n.rb +5 -3
- data/lib/liquid/interrupts.rb +3 -1
- data/lib/liquid/lexer.rb +30 -23
- data/lib/liquid/locales/en.yml +3 -1
- data/lib/liquid/parse_context.rb +16 -4
- data/lib/liquid/parse_tree_visitor.rb +2 -2
- data/lib/liquid/parser.rb +30 -18
- data/lib/liquid/parser_switching.rb +17 -3
- data/lib/liquid/partial_cache.rb +24 -0
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/profiler.rb +67 -86
- data/lib/liquid/range_lookup.rb +5 -3
- data/lib/liquid/register.rb +6 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/standardfilters.rb +62 -43
- data/lib/liquid/static_registers.rb +44 -0
- data/lib/liquid/strainer_factory.rb +36 -0
- data/lib/liquid/strainer_template.rb +53 -0
- data/lib/liquid/tablerowloop_drop.rb +6 -4
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tag.rb +28 -6
- data/lib/liquid/tags/assign.rb +24 -10
- data/lib/liquid/tags/break.rb +8 -3
- data/lib/liquid/tags/capture.rb +11 -8
- data/lib/liquid/tags/case.rb +33 -27
- data/lib/liquid/tags/comment.rb +5 -3
- data/lib/liquid/tags/continue.rb +8 -3
- data/lib/liquid/tags/cycle.rb +25 -14
- data/lib/liquid/tags/decrement.rb +6 -3
- data/lib/liquid/tags/echo.rb +26 -0
- data/lib/liquid/tags/for.rb +68 -44
- data/lib/liquid/tags/if.rb +35 -23
- data/lib/liquid/tags/ifchanged.rb +11 -10
- data/lib/liquid/tags/include.rb +34 -47
- data/lib/liquid/tags/increment.rb +7 -3
- data/lib/liquid/tags/raw.rb +14 -11
- data/lib/liquid/tags/render.rb +84 -0
- data/lib/liquid/tags/table_row.rb +23 -19
- data/lib/liquid/tags/unless.rb +15 -15
- data/lib/liquid/template.rb +55 -69
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +17 -9
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +5 -3
- data/lib/liquid/variable.rb +47 -19
- data/lib/liquid/variable_lookup.rb +8 -6
- data/lib/liquid/version.rb +2 -1
- data/lib/liquid.rb +17 -5
- data/test/integration/assign_test.rb +74 -5
- data/test/integration/blank_test.rb +11 -8
- data/test/integration/block_test.rb +47 -1
- data/test/integration/capture_test.rb +18 -10
- data/test/integration/context_test.rb +608 -5
- data/test/integration/document_test.rb +4 -2
- data/test/integration/drop_test.rb +67 -57
- data/test/integration/error_handling_test.rb +73 -61
- data/test/integration/expression_test.rb +46 -0
- data/test/integration/filter_test.rb +53 -42
- data/test/integration/hash_ordering_test.rb +5 -3
- data/test/integration/output_test.rb +26 -24
- data/test/integration/parsing_quirks_test.rb +19 -7
- data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
- data/test/integration/security_test.rb +30 -21
- data/test/integration/standard_filter_test.rb +339 -281
- data/test/integration/tag/disableable_test.rb +59 -0
- data/test/integration/tag_test.rb +45 -0
- data/test/integration/tags/break_tag_test.rb +4 -2
- data/test/integration/tags/continue_tag_test.rb +4 -2
- data/test/integration/tags/echo_test.rb +13 -0
- data/test/integration/tags/for_tag_test.rb +107 -51
- data/test/integration/tags/if_else_tag_test.rb +5 -3
- data/test/integration/tags/include_tag_test.rb +70 -54
- data/test/integration/tags/increment_tag_test.rb +4 -2
- data/test/integration/tags/liquid_tag_test.rb +116 -0
- data/test/integration/tags/raw_tag_test.rb +14 -11
- data/test/integration/tags/render_tag_test.rb +213 -0
- data/test/integration/tags/standard_tag_test.rb +38 -31
- data/test/integration/tags/statements_test.rb +23 -21
- data/test/integration/tags/table_row_test.rb +2 -0
- data/test/integration/tags/unless_else_tag_test.rb +4 -2
- data/test/integration/template_test.rb +118 -124
- data/test/integration/trim_mode_test.rb +78 -44
- data/test/integration/variable_test.rb +43 -32
- data/test/test_helper.rb +75 -14
- data/test/unit/block_unit_test.rb +19 -24
- data/test/unit/condition_unit_test.rb +79 -77
- data/test/unit/file_system_unit_test.rb +6 -4
- data/test/unit/i18n_unit_test.rb +7 -5
- data/test/unit/lexer_unit_test.rb +11 -9
- data/test/{integration → unit}/parse_tree_visitor_test.rb +1 -1
- data/test/unit/parser_unit_test.rb +37 -35
- data/test/unit/partial_cache_unit_test.rb +128 -0
- data/test/unit/regexp_unit_test.rb +17 -15
- data/test/unit/static_registers_unit_test.rb +156 -0
- data/test/unit/strainer_factory_unit_test.rb +100 -0
- data/test/unit/strainer_template_unit_test.rb +82 -0
- data/test/unit/tag_unit_test.rb +5 -3
- data/test/unit/tags/case_tag_unit_test.rb +3 -1
- data/test/unit/tags/for_tag_unit_test.rb +4 -2
- data/test/unit/tags/if_tag_unit_test.rb +3 -1
- data/test/unit/template_factory_unit_test.rb +12 -0
- data/test/unit/template_unit_test.rb +19 -10
- data/test/unit/tokenizer_unit_test.rb +19 -17
- data/test/unit/variable_unit_test.rb +51 -49
- metadata +75 -47
- data/lib/liquid/strainer.rb +0 -66
- data/test/unit/context_unit_test.rb +0 -490
- data/test/unit/strainer_unit_test.rb +0 -164
data/lib/liquid/tags/include.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# Include allows templates to relate with other templates
|
3
5
|
#
|
@@ -14,40 +16,49 @@ module Liquid
|
|
14
16
|
# {% include 'product' for products %}
|
15
17
|
#
|
16
18
|
class Include < Tag
|
17
|
-
|
19
|
+
prepend Tag::Disableable
|
20
|
+
|
21
|
+
SYNTAX = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
22
|
+
Syntax = SYNTAX
|
18
23
|
|
19
24
|
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
20
25
|
|
21
26
|
def initialize(tag_name, markup, options)
|
22
27
|
super
|
23
28
|
|
24
|
-
if markup =~
|
29
|
+
if markup =~ SYNTAX
|
25
30
|
|
26
|
-
template_name =
|
27
|
-
variable_name =
|
31
|
+
template_name = Regexp.last_match(1)
|
32
|
+
variable_name = Regexp.last_match(3)
|
28
33
|
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
34
|
+
@alias_name = Regexp.last_match(5)
|
35
|
+
@variable_name_expr = variable_name ? parse_expression(variable_name) : nil
|
36
|
+
@template_name_expr = parse_expression(template_name)
|
37
|
+
@attributes = {}
|
32
38
|
|
33
39
|
markup.scan(TagAttributes) do |key, value|
|
34
|
-
@attributes[key] =
|
40
|
+
@attributes[key] = parse_expression(value)
|
35
41
|
end
|
36
42
|
|
37
43
|
else
|
38
|
-
raise SyntaxError
|
44
|
+
raise SyntaxError, options[:locale].t("errors.syntax.include")
|
39
45
|
end
|
40
46
|
end
|
41
47
|
|
42
48
|
def parse(_tokens)
|
43
49
|
end
|
44
50
|
|
45
|
-
def
|
51
|
+
def render_to_output_buffer(context, output)
|
46
52
|
template_name = context.evaluate(@template_name_expr)
|
47
|
-
raise ArgumentError
|
53
|
+
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
48
54
|
|
49
|
-
partial =
|
50
|
-
|
55
|
+
partial = PartialCache.load(
|
56
|
+
template_name,
|
57
|
+
context: context,
|
58
|
+
parse_context: parse_context
|
59
|
+
)
|
60
|
+
|
61
|
+
context_variable_name = @alias_name || template_name.split('/').last
|
51
62
|
|
52
63
|
variable = if @variable_name_expr
|
53
64
|
context.evaluate(@variable_name_expr)
|
@@ -56,69 +67,45 @@ module Liquid
|
|
56
67
|
end
|
57
68
|
|
58
69
|
old_template_name = context.template_name
|
59
|
-
old_partial
|
70
|
+
old_partial = context.partial
|
60
71
|
begin
|
61
72
|
context.template_name = template_name
|
62
|
-
context.partial
|
73
|
+
context.partial = true
|
63
74
|
context.stack do
|
64
75
|
@attributes.each do |key, value|
|
65
76
|
context[key] = context.evaluate(value)
|
66
77
|
end
|
67
78
|
|
68
79
|
if variable.is_a?(Array)
|
69
|
-
variable.
|
80
|
+
variable.each do |var|
|
70
81
|
context[context_variable_name] = var
|
71
|
-
partial.
|
82
|
+
partial.render_to_output_buffer(context, output)
|
72
83
|
end
|
73
84
|
else
|
74
85
|
context[context_variable_name] = variable
|
75
|
-
partial.
|
86
|
+
partial.render_to_output_buffer(context, output)
|
76
87
|
end
|
77
88
|
end
|
78
89
|
ensure
|
79
90
|
context.template_name = old_template_name
|
80
|
-
context.partial
|
91
|
+
context.partial = old_partial
|
81
92
|
end
|
82
|
-
end
|
83
93
|
|
84
|
-
|
94
|
+
output
|
95
|
+
end
|
85
96
|
|
86
97
|
alias_method :parse_context, :options
|
87
98
|
private :parse_context
|
88
99
|
|
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
100
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
114
101
|
def children
|
115
102
|
[
|
116
103
|
@node.template_name_expr,
|
117
|
-
@node.variable_name_expr
|
104
|
+
@node.variable_name_expr,
|
118
105
|
] + @node.attributes.values
|
119
106
|
end
|
120
107
|
end
|
121
108
|
end
|
122
109
|
|
123
|
-
Template.register_tag('include'
|
110
|
+
Template.register_tag('include', Include)
|
124
111
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# increment is used in a place where one needs to insert a counter
|
3
5
|
# into a template, and needs the counter to survive across
|
@@ -20,12 +22,14 @@ module Liquid
|
|
20
22
|
@variable = markup.strip
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
25
|
+
def render_to_output_buffer(context, output)
|
24
26
|
value = context.environments.first[@variable] ||= 0
|
25
27
|
context.environments.first[@variable] = value + 1
|
26
|
-
|
28
|
+
|
29
|
+
output << value.to_s
|
30
|
+
output
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
|
-
Template.register_tag('increment'
|
34
|
+
Template.register_tag('increment', Increment)
|
31
35
|
end
|
data/lib/liquid/tags/raw.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class Raw < Block
|
3
5
|
Syntax = /\A\s*\z/
|
@@ -10,20 +12,21 @@ module Liquid
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def parse(tokens)
|
13
|
-
@body = ''
|
14
|
-
while token = tokens.shift
|
15
|
-
if token =~ FullTokenPossiblyInvalid
|
16
|
-
@body <<
|
17
|
-
return
|
15
|
+
@body = +''
|
16
|
+
while (token = tokens.shift)
|
17
|
+
if token =~ FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
18
|
+
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
19
|
+
return
|
18
20
|
end
|
19
21
|
@body << token unless token.empty?
|
20
22
|
end
|
21
23
|
|
22
|
-
|
24
|
+
raise_tag_never_closed(block_name)
|
23
25
|
end
|
24
26
|
|
25
|
-
def
|
26
|
-
@body
|
27
|
+
def render_to_output_buffer(_context, output)
|
28
|
+
output << @body
|
29
|
+
output
|
27
30
|
end
|
28
31
|
|
29
32
|
def nodelist
|
@@ -37,11 +40,11 @@ module Liquid
|
|
37
40
|
protected
|
38
41
|
|
39
42
|
def ensure_valid_markup(tag_name, markup, parse_context)
|
40
|
-
unless markup
|
41
|
-
raise SyntaxError
|
43
|
+
unless Syntax.match?(markup)
|
44
|
+
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_unexpected_args", tag: tag_name)
|
42
45
|
end
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
46
|
-
Template.register_tag('raw'
|
49
|
+
Template.register_tag('raw', Raw)
|
47
50
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Liquid
|
4
|
+
class Render < Tag
|
5
|
+
FOR = 'for'
|
6
|
+
SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
7
|
+
|
8
|
+
disable_tags "include"
|
9
|
+
|
10
|
+
attr_reader :template_name_expr, :attributes
|
11
|
+
|
12
|
+
def initialize(tag_name, markup, options)
|
13
|
+
super
|
14
|
+
|
15
|
+
raise SyntaxError, options[:locale].t("errors.syntax.render") unless markup =~ SYNTAX
|
16
|
+
|
17
|
+
template_name = Regexp.last_match(1)
|
18
|
+
with_or_for = Regexp.last_match(3)
|
19
|
+
variable_name = Regexp.last_match(4)
|
20
|
+
|
21
|
+
@alias_name = Regexp.last_match(6)
|
22
|
+
@variable_name_expr = variable_name ? parse_expression(variable_name) : nil
|
23
|
+
@template_name_expr = parse_expression(template_name)
|
24
|
+
@for = (with_or_for == FOR)
|
25
|
+
|
26
|
+
@attributes = {}
|
27
|
+
markup.scan(TagAttributes) do |key, value|
|
28
|
+
@attributes[key] = parse_expression(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def render_to_output_buffer(context, output)
|
33
|
+
render_tag(context, output)
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_tag(context, output)
|
37
|
+
# Though we evaluate this here we will only ever parse it as a string literal.
|
38
|
+
template_name = context.evaluate(@template_name_expr)
|
39
|
+
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
40
|
+
|
41
|
+
partial = PartialCache.load(
|
42
|
+
template_name,
|
43
|
+
context: context,
|
44
|
+
parse_context: parse_context
|
45
|
+
)
|
46
|
+
|
47
|
+
context_variable_name = @alias_name || template_name.split('/').last
|
48
|
+
|
49
|
+
render_partial_func = ->(var, forloop) {
|
50
|
+
inner_context = context.new_isolated_subcontext
|
51
|
+
inner_context.template_name = template_name
|
52
|
+
inner_context.partial = true
|
53
|
+
inner_context['forloop'] = forloop if forloop
|
54
|
+
|
55
|
+
@attributes.each do |key, value|
|
56
|
+
inner_context[key] = context.evaluate(value)
|
57
|
+
end
|
58
|
+
inner_context[context_variable_name] = var unless var.nil?
|
59
|
+
partial.render_to_output_buffer(inner_context, output)
|
60
|
+
forloop&.send(:increment!)
|
61
|
+
}
|
62
|
+
|
63
|
+
variable = @variable_name_expr ? context.evaluate(@variable_name_expr) : nil
|
64
|
+
if @for && variable.respond_to?(:each) && variable.respond_to?(:count)
|
65
|
+
forloop = Liquid::ForloopDrop.new(template_name, variable.count, nil)
|
66
|
+
variable.each { |var| render_partial_func.call(var, forloop) }
|
67
|
+
else
|
68
|
+
render_partial_func.call(variable, nil)
|
69
|
+
end
|
70
|
+
|
71
|
+
output
|
72
|
+
end
|
73
|
+
|
74
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
75
|
+
def children
|
76
|
+
[
|
77
|
+
@node.template_name_expr,
|
78
|
+
] + @node.attributes.values
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Template.register_tag('render', Render)
|
84
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class TableRow < Block
|
3
5
|
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
@@ -7,48 +9,50 @@ module Liquid
|
|
7
9
|
def initialize(tag_name, markup, options)
|
8
10
|
super
|
9
11
|
if markup =~ Syntax
|
10
|
-
@variable_name
|
11
|
-
@collection_name =
|
12
|
-
@attributes
|
12
|
+
@variable_name = Regexp.last_match(1)
|
13
|
+
@collection_name = parse_expression(Regexp.last_match(2))
|
14
|
+
@attributes = {}
|
13
15
|
markup.scan(TagAttributes) do |key, value|
|
14
|
-
@attributes[key] =
|
16
|
+
@attributes[key] = parse_expression(value)
|
15
17
|
end
|
16
18
|
else
|
17
|
-
raise SyntaxError
|
19
|
+
raise SyntaxError, options[:locale].t("errors.syntax.table_row")
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
22
|
-
collection = context.evaluate(@collection_name)
|
23
|
+
def render_to_output_buffer(context, output)
|
24
|
+
(collection = context.evaluate(@collection_name)) || (return '')
|
23
25
|
|
24
|
-
from = @attributes.key?('offset'
|
25
|
-
to
|
26
|
+
from = @attributes.key?('offset') ? context.evaluate(@attributes['offset']).to_i : 0
|
27
|
+
to = @attributes.key?('limit') ? from + context.evaluate(@attributes['limit']).to_i : nil
|
26
28
|
|
27
29
|
collection = Utils.slice_collection(collection, from, to)
|
30
|
+
length = collection.length
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
32
|
+
cols = context.evaluate(@attributes['cols']).to_i
|
32
33
|
|
33
|
-
|
34
|
+
output << "<tr class=\"row1\">\n"
|
34
35
|
context.stack do
|
35
36
|
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
36
|
-
context['tablerowloop'
|
37
|
+
context['tablerowloop'] = tablerowloop
|
37
38
|
|
38
39
|
collection.each do |item|
|
39
40
|
context[@variable_name] = item
|
40
41
|
|
41
|
-
|
42
|
+
output << "<td class=\"col#{tablerowloop.col}\">"
|
43
|
+
super
|
44
|
+
output << '</td>'
|
42
45
|
|
43
46
|
if tablerowloop.col_last && !tablerowloop.last
|
44
|
-
|
47
|
+
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
45
48
|
end
|
46
49
|
|
47
50
|
tablerowloop.send(:increment!)
|
48
51
|
end
|
49
52
|
end
|
50
|
-
|
51
|
-
|
53
|
+
|
54
|
+
output << "</tr>\n"
|
55
|
+
output
|
52
56
|
end
|
53
57
|
|
54
58
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
@@ -58,5 +62,5 @@ module Liquid
|
|
58
62
|
end
|
59
63
|
end
|
60
64
|
|
61
|
-
Template.register_tag('tablerow'
|
65
|
+
Template.register_tag('tablerow', TableRow)
|
62
66
|
end
|
data/lib/liquid/tags/unless.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'if'
|
2
4
|
|
3
5
|
module Liquid
|
@@ -6,25 +8,23 @@ module Liquid
|
|
6
8
|
# {% unless x < 0 %} x is greater than zero {% endunless %}
|
7
9
|
#
|
8
10
|
class Unless < If
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
11
|
+
def render_to_output_buffer(context, output)
|
12
|
+
# First condition is interpreted backwards ( if not )
|
13
|
+
first_block = @blocks.first
|
14
|
+
unless first_block.evaluate(context)
|
15
|
+
return first_block.attachment.render_to_output_buffer(context, output)
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
18
|
+
# After the first condition unless works just like if
|
19
|
+
@blocks[1..-1].each do |block|
|
20
|
+
if block.evaluate(context)
|
21
|
+
return block.attachment.render_to_output_buffer(context, output)
|
22
22
|
end
|
23
|
-
|
24
|
-
''.freeze
|
25
23
|
end
|
24
|
+
|
25
|
+
output
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
Template.register_tag('unless'
|
29
|
+
Template.register_tag('unless', Unless)
|
30
30
|
end
|
data/lib/liquid/template.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# Templates are central to liquid.
|
3
5
|
# Interpretating templates is a two step process. First you compile the
|
@@ -16,13 +18,11 @@ module Liquid
|
|
16
18
|
attr_accessor :root
|
17
19
|
attr_reader :resource_limits, :warnings
|
18
20
|
|
19
|
-
@@file_system = BlankFileSystem.new
|
20
|
-
|
21
21
|
class TagRegistry
|
22
22
|
include Enumerable
|
23
23
|
|
24
24
|
def initialize
|
25
|
-
@tags
|
25
|
+
@tags = {}
|
26
26
|
@cache = {}
|
27
27
|
end
|
28
28
|
|
@@ -50,7 +50,7 @@ module Liquid
|
|
50
50
|
private
|
51
51
|
|
52
52
|
def lookup_class(name)
|
53
|
-
|
53
|
+
Object.const_get(name)
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -61,74 +61,53 @@ module Liquid
|
|
61
61
|
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
|
62
62
|
# :warn is the default and will give deprecation warnings when invalid syntax is used.
|
63
63
|
# :strict will enforce correct syntax.
|
64
|
-
|
65
|
-
|
66
|
-
# Deprecated. No longer used. Removed in version 5
|
67
|
-
attr_writer :taint_mode
|
64
|
+
attr_accessor :error_mode
|
65
|
+
Template.error_mode = :lax
|
68
66
|
|
69
67
|
attr_accessor :default_exception_renderer
|
70
68
|
Template.default_exception_renderer = lambda do |exception|
|
71
69
|
exception
|
72
70
|
end
|
73
71
|
|
74
|
-
|
75
|
-
|
76
|
-
end
|
72
|
+
attr_accessor :file_system
|
73
|
+
Template.file_system = BlankFileSystem.new
|
77
74
|
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
attr_accessor :tags
|
76
|
+
Template.tags = TagRegistry.new
|
77
|
+
private :tags=
|
81
78
|
|
82
79
|
def register_tag(name, klass)
|
83
80
|
tags[name.to_s] = klass
|
84
81
|
end
|
85
82
|
|
86
|
-
def tags
|
87
|
-
@tags ||= TagRegistry.new
|
88
|
-
end
|
89
|
-
|
90
|
-
def error_mode
|
91
|
-
@error_mode ||= :lax
|
92
|
-
end
|
93
|
-
|
94
|
-
# Deprecated. Removed in version 5
|
95
|
-
def taint_mode
|
96
|
-
@taint_mode ||= :lax
|
97
|
-
end
|
98
|
-
|
99
83
|
# Pass a module with filter methods which should be available
|
100
84
|
# to all liquid views. Good for registering the standard library
|
101
85
|
def register_filter(mod)
|
102
|
-
|
86
|
+
StrainerFactory.add_global_filter(mod)
|
103
87
|
end
|
104
88
|
|
105
|
-
|
106
|
-
|
107
|
-
|
89
|
+
attr_accessor :default_resource_limits
|
90
|
+
Template.default_resource_limits = {}
|
91
|
+
private :default_resource_limits=
|
108
92
|
|
109
93
|
# creates a new <tt>Template</tt> object from liquid source code
|
110
94
|
# To enable profiling, pass in <tt>profile: true</tt> as an option.
|
111
95
|
# See Liquid::Profiler for more information
|
112
96
|
def parse(source, options = {})
|
113
|
-
|
114
|
-
template.parse(source, options)
|
97
|
+
new.parse(source, options)
|
115
98
|
end
|
116
99
|
end
|
117
100
|
|
118
101
|
def initialize
|
119
|
-
@rethrow_errors
|
120
|
-
@resource_limits = ResourceLimits.new(
|
102
|
+
@rethrow_errors = false
|
103
|
+
@resource_limits = ResourceLimits.new(Template.default_resource_limits)
|
121
104
|
end
|
122
105
|
|
123
106
|
# Parse source code.
|
124
107
|
# Returns self for easy chaining
|
125
108
|
def parse(source, options = {})
|
126
|
-
|
127
|
-
@
|
128
|
-
@line_numbers = options[:line_numbers] || @profiling
|
129
|
-
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
130
|
-
@root = Document.parse(tokenize(source), parse_context)
|
131
|
-
@warnings = parse_context.warnings
|
109
|
+
parse_context = configure_options(options)
|
110
|
+
@root = Document.parse(tokenize(source), parse_context)
|
132
111
|
self
|
133
112
|
end
|
134
113
|
|
@@ -163,19 +142,19 @@ module Liquid
|
|
163
142
|
# filters and tags and might be useful to integrate liquid more with its host application
|
164
143
|
#
|
165
144
|
def render(*args)
|
166
|
-
return ''
|
145
|
+
return '' if @root.nil?
|
167
146
|
|
168
147
|
context = case args.first
|
169
148
|
when Liquid::Context
|
170
149
|
c = args.shift
|
171
150
|
|
172
151
|
if @rethrow_errors
|
173
|
-
c.exception_renderer =
|
152
|
+
c.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
174
153
|
end
|
175
154
|
|
176
155
|
c
|
177
156
|
when Liquid::Drop
|
178
|
-
drop
|
157
|
+
drop = args.shift
|
179
158
|
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
180
159
|
when Hash
|
181
160
|
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
@@ -185,11 +164,18 @@ module Liquid
|
|
185
164
|
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
186
165
|
end
|
187
166
|
|
167
|
+
output = nil
|
168
|
+
|
169
|
+
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
|
170
|
+
|
188
171
|
case args.last
|
189
172
|
when Hash
|
190
173
|
options = args.pop
|
174
|
+
output = options[:output] if options[:output]
|
191
175
|
|
192
|
-
|
176
|
+
options[:registers]&.each do |key, register|
|
177
|
+
context_register[key] = register
|
178
|
+
end
|
193
179
|
|
194
180
|
apply_options_to_context(context, options)
|
195
181
|
when Module, Array
|
@@ -199,13 +185,13 @@ module Liquid
|
|
199
185
|
# Retrying a render resets resource usage
|
200
186
|
context.resource_limits.reset
|
201
187
|
|
188
|
+
if @profiling && context.profiler.nil?
|
189
|
+
@profiler = context.profiler = Liquid::Profiler.new
|
190
|
+
end
|
191
|
+
|
202
192
|
begin
|
203
193
|
# render the nodelist.
|
204
|
-
|
205
|
-
result = with_profiling(context) do
|
206
|
-
@root.render(context)
|
207
|
-
end
|
208
|
-
result.respond_to?(:join) ? result.join : result
|
194
|
+
@root.render_to_output_buffer(context, output || +'')
|
209
195
|
rescue Liquid::MemoryError => e
|
210
196
|
context.handle_error(e)
|
211
197
|
ensure
|
@@ -218,35 +204,35 @@ module Liquid
|
|
218
204
|
render(*args)
|
219
205
|
end
|
220
206
|
|
221
|
-
|
222
|
-
|
223
|
-
def tokenize(source)
|
224
|
-
Tokenizer.new(source, @line_numbers)
|
207
|
+
def render_to_output_buffer(context, output)
|
208
|
+
render(context, output: output)
|
225
209
|
end
|
226
210
|
|
227
|
-
|
228
|
-
|
211
|
+
private
|
212
|
+
|
213
|
+
def configure_options(options)
|
214
|
+
if (profiling = options[:profile])
|
229
215
|
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
|
216
|
+
end
|
230
217
|
|
231
|
-
|
232
|
-
|
218
|
+
@options = options
|
219
|
+
@profiling = profiling
|
220
|
+
@line_numbers = options[:line_numbers] || @profiling
|
221
|
+
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
222
|
+
@warnings = parse_context.warnings
|
223
|
+
parse_context
|
224
|
+
end
|
233
225
|
|
234
|
-
|
235
|
-
|
236
|
-
ensure
|
237
|
-
@profiler.stop
|
238
|
-
end
|
239
|
-
else
|
240
|
-
yield
|
241
|
-
end
|
226
|
+
def tokenize(source)
|
227
|
+
Tokenizer.new(source, @line_numbers)
|
242
228
|
end
|
243
229
|
|
244
230
|
def apply_options_to_context(context, options)
|
245
231
|
context.add_filters(options[:filters]) if options[:filters]
|
246
|
-
context.global_filter
|
232
|
+
context.global_filter = options[:global_filter] if options[:global_filter]
|
247
233
|
context.exception_renderer = options[:exception_renderer] if options[:exception_renderer]
|
248
|
-
context.strict_variables
|
249
|
-
context.strict_filters
|
234
|
+
context.strict_variables = options[:strict_variables] if options[:strict_variables]
|
235
|
+
context.strict_filters = options[:strict_filters] if options[:strict_filters]
|
250
236
|
end
|
251
237
|
end
|
252
238
|
end
|