liquid 3.0.6 → 4.0.3
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 +5 -5
- data/History.md +154 -58
- data/{MIT-LICENSE → LICENSE} +0 -0
- data/README.md +33 -0
- data/lib/liquid/block.rb +42 -125
- data/lib/liquid/block_body.rb +99 -79
- data/lib/liquid/condition.rb +52 -32
- data/lib/liquid/context.rb +57 -51
- data/lib/liquid/document.rb +19 -9
- data/lib/liquid/drop.rb +17 -16
- data/lib/liquid/errors.rb +20 -24
- data/lib/liquid/expression.rb +26 -10
- data/lib/liquid/extensions.rb +19 -7
- data/lib/liquid/file_system.rb +11 -11
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +6 -6
- data/lib/liquid/interrupts.rb +1 -2
- data/lib/liquid/lexer.rb +12 -8
- data/lib/liquid/locales/en.yml +6 -2
- data/lib/liquid/parse_context.rb +38 -0
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser_switching.rb +4 -4
- data/lib/liquid/profiler/hooks.rb +7 -7
- data/lib/liquid/profiler.rb +18 -19
- data/lib/liquid/range_lookup.rb +16 -1
- data/lib/liquid/resource_limits.rb +23 -0
- data/lib/liquid/standardfilters.rb +207 -61
- data/lib/liquid/strainer.rb +15 -8
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +9 -8
- data/lib/liquid/tags/assign.rb +25 -4
- data/lib/liquid/tags/break.rb +0 -3
- data/lib/liquid/tags/capture.rb +1 -1
- data/lib/liquid/tags/case.rb +27 -12
- data/lib/liquid/tags/comment.rb +2 -2
- data/lib/liquid/tags/cycle.rb +16 -8
- data/lib/liquid/tags/decrement.rb +1 -4
- data/lib/liquid/tags/for.rb +103 -75
- data/lib/liquid/tags/if.rb +60 -44
- data/lib/liquid/tags/ifchanged.rb +0 -2
- data/lib/liquid/tags/include.rb +71 -51
- data/lib/liquid/tags/raw.rb +32 -4
- data/lib/liquid/tags/table_row.rb +21 -31
- data/lib/liquid/tags/unless.rb +3 -4
- data/lib/liquid/template.rb +42 -54
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/truffle.rb +5 -0
- data/lib/liquid/utils.rb +52 -8
- data/lib/liquid/variable.rb +59 -46
- data/lib/liquid/variable_lookup.rb +14 -6
- data/lib/liquid/version.rb +2 -1
- data/lib/liquid.rb +10 -7
- data/test/integration/assign_test.rb +8 -8
- data/test/integration/blank_test.rb +14 -14
- data/test/integration/block_test.rb +12 -0
- data/test/integration/context_test.rb +2 -2
- data/test/integration/document_test.rb +19 -0
- data/test/integration/drop_test.rb +42 -40
- data/test/integration/error_handling_test.rb +96 -43
- data/test/integration/filter_test.rb +60 -20
- data/test/integration/hash_ordering_test.rb +9 -9
- data/test/integration/output_test.rb +26 -27
- data/test/integration/parse_tree_visitor_test.rb +247 -0
- data/test/integration/parsing_quirks_test.rb +19 -13
- data/test/integration/render_profiling_test.rb +20 -20
- data/test/integration/security_test.rb +23 -7
- data/test/integration/standard_filter_test.rb +426 -46
- data/test/integration/tags/break_tag_test.rb +1 -2
- data/test/integration/tags/continue_tag_test.rb +0 -1
- data/test/integration/tags/for_tag_test.rb +135 -100
- data/test/integration/tags/if_else_tag_test.rb +75 -77
- data/test/integration/tags/include_tag_test.rb +50 -31
- data/test/integration/tags/increment_tag_test.rb +10 -11
- data/test/integration/tags/raw_tag_test.rb +7 -1
- data/test/integration/tags/standard_tag_test.rb +121 -122
- data/test/integration/tags/statements_test.rb +3 -5
- data/test/integration/tags/table_row_test.rb +20 -19
- data/test/integration/tags/unless_else_tag_test.rb +6 -6
- data/test/integration/template_test.rb +199 -49
- data/test/integration/trim_mode_test.rb +529 -0
- data/test/integration/variable_test.rb +27 -13
- data/test/test_helper.rb +33 -6
- data/test/truffle/truffle_test.rb +9 -0
- data/test/unit/block_unit_test.rb +8 -5
- data/test/unit/condition_unit_test.rb +94 -77
- data/test/unit/context_unit_test.rb +69 -72
- data/test/unit/file_system_unit_test.rb +3 -3
- data/test/unit/i18n_unit_test.rb +2 -2
- data/test/unit/lexer_unit_test.rb +12 -9
- data/test/unit/parser_unit_test.rb +2 -2
- data/test/unit/regexp_unit_test.rb +1 -1
- data/test/unit/strainer_unit_test.rb +96 -1
- data/test/unit/tag_unit_test.rb +7 -2
- data/test/unit/tags/case_tag_unit_test.rb +1 -1
- data/test/unit/tags/for_tag_unit_test.rb +2 -2
- data/test/unit/tags/if_tag_unit_test.rb +1 -1
- data/test/unit/template_unit_test.rb +14 -5
- data/test/unit/tokenizer_unit_test.rb +24 -7
- data/test/unit/variable_unit_test.rb +60 -43
- metadata +62 -50
- data/lib/liquid/module_ex.rb +0 -62
- data/lib/liquid/token.rb +0 -18
- data/test/unit/module_ex_unit_test.rb +0 -87
data/lib/liquid/tags/if.rb
CHANGED
@@ -12,7 +12,9 @@ module Liquid
|
|
12
12
|
class If < Block
|
13
13
|
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
14
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)
|
15
|
+
BOOLEAN_OPERATORS = %w(and or).freeze
|
16
|
+
|
17
|
+
attr_reader :blocks
|
16
18
|
|
17
19
|
def initialize(tag_name, markup, options)
|
18
20
|
super
|
@@ -21,7 +23,12 @@ module Liquid
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def nodelist
|
24
|
-
@blocks.
|
26
|
+
@blocks.map(&:attachment)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse(tokens)
|
30
|
+
while parse_body(@blocks.last.attachment, tokens)
|
31
|
+
end
|
25
32
|
end
|
26
33
|
|
27
34
|
def unknown_tag(tag, markup, tokens)
|
@@ -36,7 +43,7 @@ module Liquid
|
|
36
43
|
context.stack do
|
37
44
|
@blocks.each do |block|
|
38
45
|
if block.evaluate(context)
|
39
|
-
return
|
46
|
+
return block.attachment.render(context)
|
40
47
|
end
|
41
48
|
end
|
42
49
|
''.freeze
|
@@ -45,61 +52,70 @@ module Liquid
|
|
45
52
|
|
46
53
|
private
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
@blocks.push(block)
|
56
|
-
@nodelist = block.attach(Array.new)
|
55
|
+
def push_block(tag, markup)
|
56
|
+
block = if tag == 'else'.freeze
|
57
|
+
ElseCondition.new
|
58
|
+
else
|
59
|
+
parse_with_selected_parser(markup)
|
57
60
|
end
|
58
61
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
+
@blocks.push(block)
|
63
|
+
block.attach(BlockBody.new)
|
64
|
+
end
|
62
65
|
|
63
|
-
|
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
|
64
69
|
|
65
|
-
|
66
|
-
operator = expressions.pop.to_s.strip
|
70
|
+
condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
|
67
71
|
|
68
|
-
|
72
|
+
until expressions.empty?
|
73
|
+
operator = expressions.pop.to_s.strip
|
69
74
|
|
70
|
-
|
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
|
75
|
+
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop.to_s =~ Syntax
|
75
76
|
|
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
|
77
81
|
end
|
78
82
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
84
100
|
end
|
101
|
+
first_condition
|
102
|
+
end
|
85
103
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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)
|
92
111
|
end
|
112
|
+
end
|
93
113
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
b = p.expression
|
98
|
-
Condition.new(a, op, b)
|
99
|
-
else
|
100
|
-
Condition.new(a)
|
101
|
-
end
|
114
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
115
|
+
def children
|
116
|
+
@node.blocks
|
102
117
|
end
|
118
|
+
end
|
103
119
|
end
|
104
120
|
|
105
121
|
Template.register_tag('if'.freeze, If)
|
data/lib/liquid/tags/include.rb
CHANGED
@@ -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:
|
@@ -17,17 +16,22 @@ module Liquid
|
|
17
16
|
class Include < Tag
|
18
17
|
Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
|
19
18
|
|
19
|
+
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
20
|
+
|
20
21
|
def initialize(tag_name, markup, options)
|
21
22
|
super
|
22
23
|
|
23
24
|
if markup =~ Syntax
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
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 = {}
|
28
32
|
|
29
33
|
markup.scan(TagAttributes) do |key, value|
|
30
|
-
@attributes[key] = value
|
34
|
+
@attributes[key] = Expression.parse(value)
|
31
35
|
end
|
32
36
|
|
33
37
|
else
|
@@ -35,69 +39,85 @@ module Liquid
|
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
38
|
-
def parse(
|
42
|
+
def parse(_tokens)
|
39
43
|
end
|
40
44
|
|
41
45
|
def render(context)
|
42
|
-
|
43
|
-
|
46
|
+
template_name = context.evaluate(@template_name_expr)
|
47
|
+
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
44
48
|
|
45
|
-
context
|
46
|
-
|
47
|
-
|
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
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
54
75
|
partial.render(context)
|
55
76
|
end
|
56
|
-
else
|
57
|
-
context[context_variable_name] = variable
|
58
|
-
partial.render(context)
|
59
77
|
end
|
78
|
+
ensure
|
79
|
+
context.template_name = old_template_name
|
80
|
+
context.partial = old_partial
|
60
81
|
end
|
61
82
|
end
|
62
83
|
|
63
84
|
private
|
64
|
-
def load_cached_partial(context)
|
65
|
-
cached_partials = context.registers[:cached_partials] || {}
|
66
|
-
template_name = context[@template_name]
|
67
85
|
|
68
|
-
|
69
|
-
|
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
|
86
|
+
alias_method :parse_context, :options
|
87
|
+
private :parse_context
|
77
88
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
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
|
90
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
|
91
106
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
100
119
|
end
|
120
|
+
end
|
101
121
|
end
|
102
122
|
|
103
123
|
Template.register_tag('include'.freeze, Include)
|
data/lib/liquid/tags/raw.rb
CHANGED
@@ -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
|
-
@
|
7
|
-
@nodelist.clear
|
13
|
+
@body = ''
|
8
14
|
while token = tokens.shift
|
9
15
|
if token =~ FullTokenPossiblyInvalid
|
10
|
-
@
|
16
|
+
@body << $1 if $1 != "".freeze
|
11
17
|
return if block_delimiter == $2
|
12
18
|
end
|
13
|
-
@
|
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
|
@@ -2,14 +2,16 @@ module Liquid
|
|
2
2
|
class TableRow < Block
|
3
3
|
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
4
4
|
|
5
|
+
attr_reader :variable_name, :collection_name, :attributes
|
6
|
+
|
5
7
|
def initialize(tag_name, markup, options)
|
6
8
|
super
|
7
9
|
if markup =~ Syntax
|
8
10
|
@variable_name = $1
|
9
|
-
@collection_name = $2
|
11
|
+
@collection_name = Expression.parse($2)
|
10
12
|
@attributes = {}
|
11
13
|
markup.scan(TagAttributes) do |key, value|
|
12
|
-
@attributes[key] = value
|
14
|
+
@attributes[key] = Expression.parse(value)
|
13
15
|
end
|
14
16
|
else
|
15
17
|
raise SyntaxError.new(options[:locale].t("errors.syntax.table_row".freeze))
|
@@ -17,55 +19,43 @@ module Liquid
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def render(context)
|
20
|
-
collection = context
|
22
|
+
collection = context.evaluate(@collection_name) or return ''.freeze
|
21
23
|
|
22
|
-
from = @attributes
|
23
|
-
to = @attributes
|
24
|
+
from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
|
25
|
+
to = @attributes.key?('limit'.freeze) ? from + context.evaluate(@attributes['limit'.freeze]).to_i : nil
|
24
26
|
|
25
27
|
collection = Utils.slice_collection(collection, from, to)
|
26
28
|
|
27
29
|
length = collection.length
|
28
30
|
|
29
|
-
cols = context
|
30
|
-
|
31
|
-
row = 1
|
32
|
-
col = 0
|
31
|
+
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
33
32
|
|
34
33
|
result = "<tr class=\"row1\">\n"
|
35
34
|
context.stack do
|
35
|
+
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
36
|
+
context['tablerowloop'.freeze] = tablerowloop
|
36
37
|
|
37
|
-
collection.
|
38
|
+
collection.each do |item|
|
38
39
|
context[@variable_name] = item
|
39
|
-
context['tablerowloop'.freeze] = {
|
40
|
-
'length'.freeze => length,
|
41
|
-
'index'.freeze => index + 1,
|
42
|
-
'index0'.freeze => index,
|
43
|
-
'col'.freeze => col + 1,
|
44
|
-
'col0'.freeze => col,
|
45
|
-
'rindex'.freeze => length - index,
|
46
|
-
'rindex0'.freeze => length - index - 1,
|
47
|
-
'first'.freeze => (index == 0),
|
48
|
-
'last'.freeze => (index == length - 1),
|
49
|
-
'col_first'.freeze => (col == 0),
|
50
|
-
'col_last'.freeze => (col == cols - 1)
|
51
|
-
}
|
52
|
-
|
53
|
-
|
54
|
-
col += 1
|
55
40
|
|
56
|
-
result << "<td class=\"col#{col}\">" << super << '</td>'
|
41
|
+
result << "<td class=\"col#{tablerowloop.col}\">" << super << '</td>'
|
57
42
|
|
58
|
-
if
|
59
|
-
|
60
|
-
row += 1
|
61
|
-
result << "</tr>\n<tr class=\"row#{row}\">"
|
43
|
+
if tablerowloop.col_last && !tablerowloop.last
|
44
|
+
result << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
62
45
|
end
|
63
46
|
|
47
|
+
tablerowloop.send(:increment!)
|
64
48
|
end
|
65
49
|
end
|
66
50
|
result << "</tr>\n"
|
67
51
|
result
|
68
52
|
end
|
53
|
+
|
54
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
55
|
+
def children
|
56
|
+
super + @node.attributes.values + [@node.collection_name]
|
57
|
+
end
|
58
|
+
end
|
69
59
|
end
|
70
60
|
|
71
61
|
Template.register_tag('tablerow'.freeze, TableRow)
|
data/lib/liquid/tags/unless.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'if'
|
2
2
|
|
3
3
|
module Liquid
|
4
4
|
# Unless is a conditional just like 'if' but works on the inverse logic.
|
@@ -8,17 +8,16 @@ module Liquid
|
|
8
8
|
class Unless < If
|
9
9
|
def render(context)
|
10
10
|
context.stack do
|
11
|
-
|
12
11
|
# First condition is interpreted backwards ( if not )
|
13
12
|
first_block = @blocks.first
|
14
13
|
unless first_block.evaluate(context)
|
15
|
-
return
|
14
|
+
return first_block.attachment.render(context)
|
16
15
|
end
|
17
16
|
|
18
17
|
# After the first condition unless works just like if
|
19
18
|
@blocks[1..-1].each do |block|
|
20
19
|
if block.evaluate(context)
|
21
|
-
return
|
20
|
+
return block.attachment.render(context)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
data/lib/liquid/template.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Liquid
|
2
|
-
|
3
2
|
# Templates are central to liquid.
|
4
3
|
# Interpretating templates is a two step process. First you compile the
|
5
4
|
# source code you got. During compile time some extensive error checking is performed.
|
@@ -14,21 +13,21 @@ module Liquid
|
|
14
13
|
# template.render('user_name' => 'bob')
|
15
14
|
#
|
16
15
|
class Template
|
17
|
-
|
18
|
-
|
19
|
-
}
|
16
|
+
attr_accessor :root
|
17
|
+
attr_reader :resource_limits, :warnings
|
20
18
|
|
21
|
-
attr_accessor :root, :resource_limits
|
22
19
|
@@file_system = BlankFileSystem.new
|
23
20
|
|
24
21
|
class TagRegistry
|
22
|
+
include Enumerable
|
23
|
+
|
25
24
|
def initialize
|
26
|
-
@tags
|
25
|
+
@tags = {}
|
27
26
|
@cache = {}
|
28
27
|
end
|
29
28
|
|
30
29
|
def [](tag_name)
|
31
|
-
return nil unless @tags.
|
30
|
+
return nil unless @tags.key?(tag_name)
|
32
31
|
return @cache[tag_name] if Liquid.cache_classes
|
33
32
|
|
34
33
|
lookup_class(@tags[tag_name]).tap { |o| @cache[tag_name] = o }
|
@@ -44,6 +43,10 @@ module Liquid
|
|
44
43
|
@cache.delete(tag_name)
|
45
44
|
end
|
46
45
|
|
46
|
+
def each(&block)
|
47
|
+
@tags.each(&block)
|
48
|
+
end
|
49
|
+
|
47
50
|
private
|
48
51
|
|
49
52
|
def lookup_class(name)
|
@@ -66,6 +69,11 @@ module Liquid
|
|
66
69
|
# :error raises an error when tainted output is used
|
67
70
|
attr_writer :taint_mode
|
68
71
|
|
72
|
+
attr_accessor :default_exception_renderer
|
73
|
+
Template.default_exception_renderer = lambda do |exception|
|
74
|
+
exception
|
75
|
+
end
|
76
|
+
|
69
77
|
def file_system
|
70
78
|
@@file_system
|
71
79
|
end
|
@@ -83,11 +91,11 @@ module Liquid
|
|
83
91
|
end
|
84
92
|
|
85
93
|
def error_mode
|
86
|
-
@error_mode
|
94
|
+
@error_mode ||= :lax
|
87
95
|
end
|
88
96
|
|
89
97
|
def taint_mode
|
90
|
-
@taint_mode
|
98
|
+
@taint_mode ||= :lax
|
91
99
|
end
|
92
100
|
|
93
101
|
# Pass a module with filter methods which should be available
|
@@ -110,7 +118,8 @@ module Liquid
|
|
110
118
|
end
|
111
119
|
|
112
120
|
def initialize
|
113
|
-
@
|
121
|
+
@rethrow_errors = false
|
122
|
+
@resource_limits = ResourceLimits.new(self.class.default_resource_limits)
|
114
123
|
end
|
115
124
|
|
116
125
|
# Parse source code.
|
@@ -119,16 +128,12 @@ module Liquid
|
|
119
128
|
@options = options
|
120
129
|
@profiling = options[:profile]
|
121
130
|
@line_numbers = options[:line_numbers] || @profiling
|
122
|
-
|
123
|
-
@
|
131
|
+
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
132
|
+
@root = Document.parse(tokenize(source), parse_context)
|
133
|
+
@warnings = parse_context.warnings
|
124
134
|
self
|
125
135
|
end
|
126
136
|
|
127
|
-
def warnings
|
128
|
-
return [] unless @root
|
129
|
-
@warnings ||= @root.warnings
|
130
|
-
end
|
131
|
-
|
132
137
|
def registers
|
133
138
|
@registers ||= {}
|
134
139
|
end
|
@@ -167,7 +172,7 @@ module Liquid
|
|
167
172
|
c = args.shift
|
168
173
|
|
169
174
|
if @rethrow_errors
|
170
|
-
c.
|
175
|
+
c.exception_renderer = ->(e) { raise }
|
171
176
|
end
|
172
177
|
|
173
178
|
c
|
@@ -186,27 +191,20 @@ module Liquid
|
|
186
191
|
when Hash
|
187
192
|
options = args.pop
|
188
193
|
|
189
|
-
if options[:registers].is_a?(Hash)
|
190
|
-
self.registers.merge!(options[:registers])
|
191
|
-
end
|
192
|
-
|
193
|
-
if options[:filters]
|
194
|
-
context.add_filters(options[:filters])
|
195
|
-
end
|
194
|
+
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
196
195
|
|
197
|
-
|
198
|
-
|
199
|
-
end
|
200
|
-
when Module
|
201
|
-
context.add_filters(args.pop)
|
202
|
-
when Array
|
196
|
+
apply_options_to_context(context, options)
|
197
|
+
when Module, Array
|
203
198
|
context.add_filters(args.pop)
|
204
199
|
end
|
205
200
|
|
201
|
+
# Retrying a render resets resource usage
|
202
|
+
context.resource_limits.reset
|
203
|
+
|
206
204
|
begin
|
207
205
|
# render the nodelist.
|
208
206
|
# for performance reasons we get an array back here. join will make a string out of it.
|
209
|
-
result = with_profiling do
|
207
|
+
result = with_profiling(context) do
|
210
208
|
@root.render(context)
|
211
209
|
end
|
212
210
|
result.respond_to?(:join) ? result.join : result
|
@@ -224,32 +222,14 @@ module Liquid
|
|
224
222
|
|
225
223
|
private
|
226
224
|
|
227
|
-
# Uses the <tt>Liquid::TemplateParser</tt> regexp to tokenize the passed source
|
228
225
|
def tokenize(source)
|
229
|
-
|
230
|
-
return [] if source.to_s.empty?
|
231
|
-
|
232
|
-
tokens = calculate_line_numbers(source.split(TemplateParser))
|
233
|
-
|
234
|
-
# removes the rogue empty element at the beginning of the array
|
235
|
-
tokens.shift if tokens[0] and tokens[0].empty?
|
236
|
-
|
237
|
-
tokens
|
226
|
+
Tokenizer.new(source, @line_numbers)
|
238
227
|
end
|
239
228
|
|
240
|
-
def
|
241
|
-
|
229
|
+
def with_profiling(context)
|
230
|
+
if @profiling && !context.partial
|
231
|
+
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
|
242
232
|
|
243
|
-
current_line = 1
|
244
|
-
raw_tokens.map do |token|
|
245
|
-
Token.new(token, current_line).tap do
|
246
|
-
current_line += token.count("\n")
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
def with_profiling
|
252
|
-
if @profiling && !@options[:included]
|
253
233
|
@profiler = Profiler.new
|
254
234
|
@profiler.start
|
255
235
|
|
@@ -262,5 +242,13 @@ module Liquid
|
|
262
242
|
yield
|
263
243
|
end
|
264
244
|
end
|
245
|
+
|
246
|
+
def apply_options_to_context(context, options)
|
247
|
+
context.add_filters(options[:filters]) if options[:filters]
|
248
|
+
context.global_filter = options[:global_filter] if options[:global_filter]
|
249
|
+
context.exception_renderer = options[:exception_renderer] if options[:exception_renderer]
|
250
|
+
context.strict_variables = options[:strict_variables] if options[:strict_variables]
|
251
|
+
context.strict_filters = options[:strict_filters] if options[:strict_filters]
|
252
|
+
end
|
265
253
|
end
|
266
254
|
end
|