liquid 4.0.0 → 5.10.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 +5 -5
- data/History.md +235 -2
- data/README.md +58 -8
- data/lib/liquid/block.rb +51 -20
- data/lib/liquid/block_body.rb +216 -82
- data/lib/liquid/condition.rb +83 -32
- data/lib/liquid/const.rb +8 -0
- data/lib/liquid/context.rb +130 -59
- data/lib/liquid/deprecations.rb +22 -0
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +8 -2
- data/lib/liquid/environment.rb +159 -0
- data/lib/liquid/errors.rb +23 -20
- data/lib/liquid/expression.rb +114 -31
- data/lib/liquid/extensions.rb +8 -0
- data/lib/liquid/file_system.rb +6 -4
- data/lib/liquid/forloop_drop.rb +51 -4
- data/lib/liquid/i18n.rb +5 -3
- data/lib/liquid/interrupts.rb +3 -1
- data/lib/liquid/lexer.rb +165 -39
- data/lib/liquid/locales/en.yml +16 -6
- data/lib/liquid/parse_context.rb +62 -7
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +31 -19
- data/lib/liquid/parser_switching.rb +42 -3
- data/lib/liquid/partial_cache.rb +33 -0
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/profiler.rb +67 -86
- data/lib/liquid/range_lookup.rb +26 -6
- data/lib/liquid/registers.rb +51 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/snippet_drop.rb +22 -0
- data/lib/liquid/standardfilters.rb +813 -137
- data/lib/liquid/strainer_template.rb +62 -0
- data/lib/liquid/tablerowloop_drop.rb +64 -5
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +13 -0
- data/lib/liquid/tag.rb +42 -6
- data/lib/liquid/tags/assign.rb +46 -18
- data/lib/liquid/tags/break.rb +15 -4
- data/lib/liquid/tags/capture.rb +26 -18
- data/lib/liquid/tags/case.rb +108 -32
- data/lib/liquid/tags/comment.rb +76 -4
- data/lib/liquid/tags/continue.rb +15 -13
- data/lib/liquid/tags/cycle.rb +117 -34
- data/lib/liquid/tags/decrement.rb +30 -23
- data/lib/liquid/tags/doc.rb +81 -0
- data/lib/liquid/tags/echo.rb +39 -0
- data/lib/liquid/tags/for.rb +109 -96
- data/lib/liquid/tags/if.rb +72 -41
- data/lib/liquid/tags/ifchanged.rb +10 -11
- data/lib/liquid/tags/include.rb +89 -63
- data/lib/liquid/tags/increment.rb +31 -20
- data/lib/liquid/tags/inline_comment.rb +28 -0
- data/lib/liquid/tags/raw.rb +25 -13
- data/lib/liquid/tags/render.rb +151 -0
- data/lib/liquid/tags/snippet.rb +45 -0
- data/lib/liquid/tags/table_row.rb +104 -21
- data/lib/liquid/tags/unless.rb +37 -20
- data/lib/liquid/tags.rb +51 -0
- data/lib/liquid/template.rb +90 -106
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +143 -13
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +114 -5
- data/lib/liquid/variable.rb +119 -45
- data/lib/liquid/variable_lookup.rb +35 -13
- data/lib/liquid/version.rb +3 -1
- data/lib/liquid.rb +31 -18
- metadata +56 -107
- data/lib/liquid/strainer.rb +0 -66
- data/test/fixtures/en_locale.yml +0 -9
- data/test/integration/assign_test.rb +0 -48
- data/test/integration/blank_test.rb +0 -106
- data/test/integration/capture_test.rb +0 -50
- data/test/integration/context_test.rb +0 -32
- data/test/integration/document_test.rb +0 -19
- data/test/integration/drop_test.rb +0 -273
- data/test/integration/error_handling_test.rb +0 -260
- data/test/integration/filter_test.rb +0 -178
- data/test/integration/hash_ordering_test.rb +0 -23
- data/test/integration/output_test.rb +0 -123
- data/test/integration/parsing_quirks_test.rb +0 -118
- data/test/integration/render_profiling_test.rb +0 -154
- data/test/integration/security_test.rb +0 -66
- data/test/integration/standard_filter_test.rb +0 -535
- data/test/integration/tags/break_tag_test.rb +0 -15
- data/test/integration/tags/continue_tag_test.rb +0 -15
- data/test/integration/tags/for_tag_test.rb +0 -410
- data/test/integration/tags/if_else_tag_test.rb +0 -188
- data/test/integration/tags/include_tag_test.rb +0 -238
- data/test/integration/tags/increment_tag_test.rb +0 -23
- data/test/integration/tags/raw_tag_test.rb +0 -31
- data/test/integration/tags/standard_tag_test.rb +0 -296
- data/test/integration/tags/statements_test.rb +0 -111
- data/test/integration/tags/table_row_test.rb +0 -64
- data/test/integration/tags/unless_else_tag_test.rb +0 -26
- data/test/integration/template_test.rb +0 -323
- data/test/integration/trim_mode_test.rb +0 -525
- data/test/integration/variable_test.rb +0 -92
- data/test/test_helper.rb +0 -117
- data/test/unit/block_unit_test.rb +0 -58
- data/test/unit/condition_unit_test.rb +0 -158
- data/test/unit/context_unit_test.rb +0 -483
- data/test/unit/file_system_unit_test.rb +0 -35
- data/test/unit/i18n_unit_test.rb +0 -37
- data/test/unit/lexer_unit_test.rb +0 -51
- data/test/unit/parser_unit_test.rb +0 -82
- data/test/unit/regexp_unit_test.rb +0 -44
- data/test/unit/strainer_unit_test.rb +0 -148
- data/test/unit/tag_unit_test.rb +0 -21
- data/test/unit/tags/case_tag_unit_test.rb +0 -10
- data/test/unit/tags/for_tag_unit_test.rb +0 -13
- data/test/unit/tags/if_tag_unit_test.rb +0 -8
- data/test/unit/template_unit_test.rb +0 -78
- data/test/unit/tokenizer_unit_test.rb +0 -55
- data/test/unit/variable_unit_test.rb +0 -162
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Liquid
|
|
6
|
+
# StrainerTemplate is the computed class for the filters system.
|
|
7
|
+
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
|
|
8
|
+
#
|
|
9
|
+
# The Strainer only allows method calls defined in filters given to it via StrainerFactory.add_global_filter,
|
|
10
|
+
# Context#add_filters or Template.register_filter
|
|
11
|
+
class StrainerTemplate
|
|
12
|
+
def initialize(context)
|
|
13
|
+
@context = context
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def add_filter(filter)
|
|
18
|
+
return if include?(filter)
|
|
19
|
+
|
|
20
|
+
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
|
|
21
|
+
if invokable_non_public_methods.any?
|
|
22
|
+
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
include(filter)
|
|
26
|
+
|
|
27
|
+
filter_methods.merge(filter.public_instance_methods.map(&:to_s))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def invokable?(method)
|
|
31
|
+
filter_methods.include?(method.to_s)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def inherited(subclass)
|
|
35
|
+
super
|
|
36
|
+
subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def filter_method_names
|
|
40
|
+
filter_methods.map(&:to_s).to_a
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def filter_methods
|
|
46
|
+
@filter_methods ||= Set.new
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def invoke(method, *args)
|
|
51
|
+
if self.class.invokable?(method)
|
|
52
|
+
send(method, *args)
|
|
53
|
+
elsif @context.strict_filters
|
|
54
|
+
raise Liquid::UndefinedFilter, "undefined filter #{method}"
|
|
55
|
+
else
|
|
56
|
+
args.first
|
|
57
|
+
end
|
|
58
|
+
rescue ::ArgumentError => e
|
|
59
|
+
raise Liquid::ArgumentError, e.message, e.backtrace
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -1,47 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
4
|
+
# @liquid_public_docs
|
|
5
|
+
# @liquid_type object
|
|
6
|
+
# @liquid_name tablerowloop
|
|
7
|
+
# @liquid_summary
|
|
8
|
+
# Information about a parent [`tablerow` loop](/docs/api/liquid/tags/tablerow).
|
|
2
9
|
class TablerowloopDrop < Drop
|
|
3
10
|
def initialize(length, cols)
|
|
4
11
|
@length = length
|
|
5
|
-
@row
|
|
6
|
-
@col
|
|
7
|
-
@cols
|
|
8
|
-
@index
|
|
12
|
+
@row = 1
|
|
13
|
+
@col = 1
|
|
14
|
+
@cols = cols
|
|
15
|
+
@index = 0
|
|
9
16
|
end
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
# @liquid_public_docs
|
|
19
|
+
# @liquid_summary
|
|
20
|
+
# The total number of iterations in the loop.
|
|
21
|
+
# @liquid_return [number]
|
|
22
|
+
attr_reader :length
|
|
23
|
+
|
|
24
|
+
# @liquid_public_docs
|
|
25
|
+
# @liquid_summary
|
|
26
|
+
# The 1-based index of the current column.
|
|
27
|
+
# @liquid_return [number]
|
|
28
|
+
attr_reader :col
|
|
29
|
+
|
|
30
|
+
# @liquid_public_docs
|
|
31
|
+
# @liquid_summary
|
|
32
|
+
# The 1-based index of current row.
|
|
33
|
+
# @liquid_return [number]
|
|
34
|
+
attr_reader :row
|
|
12
35
|
|
|
36
|
+
# @liquid_public_docs
|
|
37
|
+
# @liquid_summary
|
|
38
|
+
# The 1-based index of the current iteration.
|
|
39
|
+
# @liquid_return [number]
|
|
13
40
|
def index
|
|
14
41
|
@index + 1
|
|
15
42
|
end
|
|
16
43
|
|
|
44
|
+
# @liquid_public_docs
|
|
45
|
+
# @liquid_summary
|
|
46
|
+
# The 0-based index of the current iteration.
|
|
47
|
+
# @liquid_return [number]
|
|
17
48
|
def index0
|
|
18
49
|
@index
|
|
19
50
|
end
|
|
20
51
|
|
|
52
|
+
# @liquid_public_docs
|
|
53
|
+
# @liquid_summary
|
|
54
|
+
# The 0-based index of the current column.
|
|
55
|
+
# @liquid_return [number]
|
|
21
56
|
def col0
|
|
22
57
|
@col - 1
|
|
23
58
|
end
|
|
24
59
|
|
|
60
|
+
# @liquid_public_docs
|
|
61
|
+
# @liquid_summary
|
|
62
|
+
# The 1-based index of the current iteration, in reverse order.
|
|
63
|
+
# @liquid_return [number]
|
|
25
64
|
def rindex
|
|
26
65
|
@length - @index
|
|
27
66
|
end
|
|
28
67
|
|
|
68
|
+
# @liquid_public_docs
|
|
69
|
+
# @liquid_summary
|
|
70
|
+
# The 0-based index of the current iteration, in reverse order.
|
|
71
|
+
# @liquid_return [number]
|
|
29
72
|
def rindex0
|
|
30
73
|
@length - @index - 1
|
|
31
74
|
end
|
|
32
75
|
|
|
76
|
+
# @liquid_public_docs
|
|
77
|
+
# @liquid_summary
|
|
78
|
+
# Returns `true` if the current iteration is the first. Returns `false` if not.
|
|
79
|
+
# @liquid_return [boolean]
|
|
33
80
|
def first
|
|
34
81
|
@index == 0
|
|
35
82
|
end
|
|
36
83
|
|
|
84
|
+
# @liquid_public_docs
|
|
85
|
+
# @liquid_summary
|
|
86
|
+
# Returns `true` if the current iteration is the last. Returns `false` if not.
|
|
87
|
+
# @liquid_return [boolean]
|
|
37
88
|
def last
|
|
38
89
|
@index == @length - 1
|
|
39
90
|
end
|
|
40
91
|
|
|
92
|
+
# @liquid_public_docs
|
|
93
|
+
# @liquid_summary
|
|
94
|
+
# Returns `true` if the current column is the first in the row. Returns `false` if not.
|
|
95
|
+
# @liquid_return [boolean]
|
|
41
96
|
def col_first
|
|
42
97
|
@col == 1
|
|
43
98
|
end
|
|
44
99
|
|
|
100
|
+
# @liquid_public_docs
|
|
101
|
+
# @liquid_summary
|
|
102
|
+
# Returns `true` if the current column is the last in the row. Returns `false` if not.
|
|
103
|
+
# @liquid_return [boolean]
|
|
45
104
|
def col_last
|
|
46
105
|
@col == @cols
|
|
47
106
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Liquid
|
|
4
|
+
class Tag
|
|
5
|
+
module Disableable
|
|
6
|
+
def render_to_output_buffer(context, output)
|
|
7
|
+
if context.tag_disabled?(tag_name)
|
|
8
|
+
output << disabled_error(context)
|
|
9
|
+
return
|
|
10
|
+
end
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def disabled_error(context)
|
|
15
|
+
# raise then rescue the exception so that the Context#exception_renderer can re-raise it
|
|
16
|
+
raise DisabledError, "#{tag_name} #{parse_context[:locale].t('errors.disabled.tag')}"
|
|
17
|
+
rescue DisabledError => exc
|
|
18
|
+
context.handle_error(exc, line_number)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/liquid/tag.rb
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'liquid/tag/disabler'
|
|
4
|
+
require 'liquid/tag/disableable'
|
|
5
|
+
|
|
1
6
|
module Liquid
|
|
2
7
|
class Tag
|
|
3
8
|
attr_reader :nodelist, :tag_name, :line_number, :parse_context
|
|
@@ -5,20 +10,32 @@ module Liquid
|
|
|
5
10
|
include ParserSwitching
|
|
6
11
|
|
|
7
12
|
class << self
|
|
8
|
-
def parse(tag_name, markup, tokenizer,
|
|
9
|
-
tag = new(tag_name, markup,
|
|
13
|
+
def parse(tag_name, markup, tokenizer, parse_context)
|
|
14
|
+
tag = new(tag_name, markup, parse_context)
|
|
10
15
|
tag.parse(tokenizer)
|
|
11
16
|
tag
|
|
12
17
|
end
|
|
13
18
|
|
|
19
|
+
def disable_tags(*tag_names)
|
|
20
|
+
tag_names += disabled_tags
|
|
21
|
+
define_singleton_method(:disabled_tags) { tag_names }
|
|
22
|
+
prepend(Disabler)
|
|
23
|
+
end
|
|
24
|
+
|
|
14
25
|
private :new
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
def disabled_tags
|
|
30
|
+
[]
|
|
31
|
+
end
|
|
15
32
|
end
|
|
16
33
|
|
|
17
34
|
def initialize(tag_name, markup, parse_context)
|
|
18
|
-
@tag_name
|
|
19
|
-
@markup
|
|
35
|
+
@tag_name = tag_name
|
|
36
|
+
@markup = markup
|
|
20
37
|
@parse_context = parse_context
|
|
21
|
-
@line_number
|
|
38
|
+
@line_number = parse_context.line_number
|
|
22
39
|
end
|
|
23
40
|
|
|
24
41
|
def parse(_tokens)
|
|
@@ -33,11 +50,30 @@ module Liquid
|
|
|
33
50
|
end
|
|
34
51
|
|
|
35
52
|
def render(_context)
|
|
36
|
-
''
|
|
53
|
+
''
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# For backwards compatibility with custom tags. In a future release, the semantics
|
|
57
|
+
# of the `render_to_output_buffer` method will become the default and the `render`
|
|
58
|
+
# method will be removed.
|
|
59
|
+
def render_to_output_buffer(context, output)
|
|
60
|
+
render_result = render(context)
|
|
61
|
+
output << render_result if render_result
|
|
62
|
+
output
|
|
37
63
|
end
|
|
38
64
|
|
|
39
65
|
def blank?
|
|
40
66
|
false
|
|
41
67
|
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def safe_parse_expression(parser)
|
|
72
|
+
parse_context.safe_parse_expression(parser)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parse_expression(markup, safe: false)
|
|
76
|
+
parse_context.parse_expression(markup, safe: safe)
|
|
77
|
+
end
|
|
42
78
|
end
|
|
43
79
|
end
|
data/lib/liquid/tags/assign.rb
CHANGED
|
@@ -1,30 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
4
|
+
# @liquid_public_docs
|
|
5
|
+
# @liquid_type tag
|
|
6
|
+
# @liquid_category variable
|
|
7
|
+
# @liquid_name assign
|
|
8
|
+
# @liquid_summary
|
|
9
|
+
# Creates a new variable.
|
|
10
|
+
# @liquid_description
|
|
11
|
+
# You can create variables of any [basic type](/docs/api/liquid/basics#types), [object](/docs/api/liquid/objects), or object property.
|
|
9
12
|
#
|
|
13
|
+
# > Caution:
|
|
14
|
+
# > Predefined Liquid objects can be overridden by variables with the same name.
|
|
15
|
+
# > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
|
|
16
|
+
# @liquid_syntax
|
|
17
|
+
# {% assign variable_name = value %}
|
|
18
|
+
# @liquid_syntax_keyword variable_name The name of the variable being created.
|
|
19
|
+
# @liquid_syntax_keyword value The value you want to assign to the variable.
|
|
10
20
|
class Assign < Tag
|
|
11
21
|
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
|
12
22
|
|
|
13
|
-
|
|
23
|
+
# @api private
|
|
24
|
+
def self.raise_syntax_error(parse_context)
|
|
25
|
+
raise Liquid::SyntaxError, parse_context.locale.t('errors.syntax.assign')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_reader :to, :from
|
|
29
|
+
|
|
30
|
+
def initialize(tag_name, markup, parse_context)
|
|
14
31
|
super
|
|
15
32
|
if markup =~ Syntax
|
|
16
|
-
@to
|
|
17
|
-
@from = Variable.new(
|
|
33
|
+
@to = Regexp.last_match(1)
|
|
34
|
+
@from = Variable.new(Regexp.last_match(2), parse_context)
|
|
18
35
|
else
|
|
19
|
-
|
|
36
|
+
self.class.raise_syntax_error(parse_context)
|
|
20
37
|
end
|
|
21
38
|
end
|
|
22
39
|
|
|
23
|
-
def
|
|
40
|
+
def render_to_output_buffer(context, output)
|
|
24
41
|
val = @from.render(context)
|
|
25
42
|
context.scopes.last[@to] = val
|
|
26
|
-
context.resource_limits.
|
|
27
|
-
|
|
43
|
+
context.resource_limits.increment_assign_score(assign_score_of(val))
|
|
44
|
+
output
|
|
28
45
|
end
|
|
29
46
|
|
|
30
47
|
def blank?
|
|
@@ -35,17 +52,28 @@ module Liquid
|
|
|
35
52
|
|
|
36
53
|
def assign_score_of(val)
|
|
37
54
|
if val.instance_of?(String)
|
|
38
|
-
val.
|
|
39
|
-
elsif val.instance_of?(Array)
|
|
55
|
+
val.bytesize
|
|
56
|
+
elsif val.instance_of?(Array)
|
|
40
57
|
sum = 1
|
|
41
58
|
# Uses #each to avoid extra allocations.
|
|
42
59
|
val.each { |child| sum += assign_score_of(child) }
|
|
43
60
|
sum
|
|
61
|
+
elsif val.instance_of?(Hash)
|
|
62
|
+
sum = 1
|
|
63
|
+
val.each do |key, entry_value|
|
|
64
|
+
sum += assign_score_of(key)
|
|
65
|
+
sum += assign_score_of(entry_value)
|
|
66
|
+
end
|
|
67
|
+
sum
|
|
44
68
|
else
|
|
45
69
|
1
|
|
46
70
|
end
|
|
47
71
|
end
|
|
48
|
-
end
|
|
49
72
|
|
|
50
|
-
|
|
73
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
74
|
+
def children
|
|
75
|
+
[@node.from]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
51
79
|
end
|
data/lib/liquid/tags/break.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
2
4
|
# Break tag to be used to break out of a for loop.
|
|
3
5
|
#
|
|
@@ -8,11 +10,20 @@ module Liquid
|
|
|
8
10
|
# {% endif %}
|
|
9
11
|
# {% endfor %}
|
|
10
12
|
#
|
|
13
|
+
# @liquid_public_docs
|
|
14
|
+
# @liquid_type tag
|
|
15
|
+
# @liquid_category iteration
|
|
16
|
+
# @liquid_name break
|
|
17
|
+
# @liquid_summary
|
|
18
|
+
# Stops a [`for` loop](/docs/api/liquid/tags/for) from iterating.
|
|
19
|
+
# @liquid_syntax
|
|
20
|
+
# {% break %}
|
|
11
21
|
class Break < Tag
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
INTERRUPT = BreakInterrupt.new.freeze
|
|
23
|
+
|
|
24
|
+
def render_to_output_buffer(context, output)
|
|
25
|
+
context.push_interrupt(INTERRUPT)
|
|
26
|
+
output
|
|
14
27
|
end
|
|
15
28
|
end
|
|
16
|
-
|
|
17
|
-
Template.register_tag('break'.freeze, Break)
|
|
18
29
|
end
|
data/lib/liquid/tags/capture.rb
CHANGED
|
@@ -1,38 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
2
|
-
#
|
|
4
|
+
# @liquid_public_docs
|
|
5
|
+
# @liquid_type tag
|
|
6
|
+
# @liquid_category variable
|
|
7
|
+
# @liquid_name capture
|
|
8
|
+
# @liquid_summary
|
|
9
|
+
# Creates a new variable with a string value.
|
|
10
|
+
# @liquid_description
|
|
11
|
+
# You can create complex strings with Liquid logic and variables.
|
|
3
12
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
13
|
+
# > Caution:
|
|
14
|
+
# > Predefined Liquid objects can be overridden by variables with the same name.
|
|
15
|
+
# > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
|
|
16
|
+
# @liquid_syntax
|
|
17
|
+
# {% capture variable %}
|
|
18
|
+
# value
|
|
6
19
|
# {% endcapture %}
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# Capture is useful for saving content for use later in your template, such as
|
|
11
|
-
# in a sidebar or footer.
|
|
12
|
-
#
|
|
20
|
+
# @liquid_syntax_keyword variable The name of the variable being created.
|
|
21
|
+
# @liquid_syntax_keyword value The value you want to assign to the variable.
|
|
13
22
|
class Capture < Block
|
|
14
23
|
Syntax = /(#{VariableSignature}+)/o
|
|
15
24
|
|
|
16
25
|
def initialize(tag_name, markup, options)
|
|
17
26
|
super
|
|
18
27
|
if markup =~ Syntax
|
|
19
|
-
@to =
|
|
28
|
+
@to = Regexp.last_match(1)
|
|
20
29
|
else
|
|
21
|
-
raise SyntaxError
|
|
30
|
+
raise SyntaxError, options[:locale].t("errors.syntax.capture")
|
|
22
31
|
end
|
|
23
32
|
end
|
|
24
33
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
def render_to_output_buffer(context, output)
|
|
35
|
+
context.resource_limits.with_capture do
|
|
36
|
+
capture_output = render(context)
|
|
37
|
+
context.scopes.last[@to] = capture_output
|
|
38
|
+
end
|
|
39
|
+
output
|
|
30
40
|
end
|
|
31
41
|
|
|
32
42
|
def blank?
|
|
33
43
|
true
|
|
34
44
|
end
|
|
35
45
|
end
|
|
36
|
-
|
|
37
|
-
Template.register_tag('capture'.freeze, Capture)
|
|
38
46
|
end
|
data/lib/liquid/tags/case.rb
CHANGED
|
@@ -1,24 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
4
|
+
# @liquid_public_docs
|
|
5
|
+
# @liquid_type tag
|
|
6
|
+
# @liquid_category conditional
|
|
7
|
+
# @liquid_name case
|
|
8
|
+
# @liquid_summary
|
|
9
|
+
# Renders a specific expression depending on the value of a specific variable.
|
|
10
|
+
# @liquid_syntax
|
|
11
|
+
# {% case variable %}
|
|
12
|
+
# {% when first_value %}
|
|
13
|
+
# first_expression
|
|
14
|
+
# {% when second_value %}
|
|
15
|
+
# second_expression
|
|
16
|
+
# {% else %}
|
|
17
|
+
# third_expression
|
|
18
|
+
# {% endcase %}
|
|
19
|
+
# @liquid_syntax_keyword variable The name of the variable you want to base your case statement on.
|
|
20
|
+
# @liquid_syntax_keyword first_value A specific value to check for.
|
|
21
|
+
# @liquid_syntax_keyword second_value A specific value to check for.
|
|
22
|
+
# @liquid_syntax_keyword first_expression An expression to be rendered when the variable's value matches `first_value`.
|
|
23
|
+
# @liquid_syntax_keyword second_expression An expression to be rendered when the variable's value matches `second_value`.
|
|
24
|
+
# @liquid_syntax_keyword third_expression An expression to be rendered when the variable's value has no match.
|
|
2
25
|
class Case < Block
|
|
3
26
|
Syntax = /(#{QuotedFragment})/o
|
|
4
27
|
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
|
|
5
28
|
|
|
29
|
+
attr_reader :blocks, :left
|
|
30
|
+
|
|
6
31
|
def initialize(tag_name, markup, options)
|
|
7
32
|
super
|
|
8
33
|
@blocks = []
|
|
9
|
-
|
|
10
|
-
if markup =~ Syntax
|
|
11
|
-
@left = Expression.parse($1)
|
|
12
|
-
else
|
|
13
|
-
raise SyntaxError.new(options[:locale].t("errors.syntax.case".freeze))
|
|
14
|
-
end
|
|
34
|
+
parse_with_selected_parser(markup)
|
|
15
35
|
end
|
|
16
36
|
|
|
17
37
|
def parse(tokens)
|
|
18
|
-
body =
|
|
19
|
-
while parse_body(body, tokens)
|
|
20
|
-
|
|
38
|
+
body = case_body = new_body
|
|
39
|
+
body = @blocks.last.attachment while parse_body(body, tokens)
|
|
40
|
+
@blocks.reverse_each do |condition|
|
|
41
|
+
body = condition.attachment
|
|
42
|
+
unless body.frozen?
|
|
43
|
+
body.remove_blank_strings if blank?
|
|
44
|
+
body.freeze
|
|
45
|
+
end
|
|
21
46
|
end
|
|
47
|
+
case_body.freeze
|
|
22
48
|
end
|
|
23
49
|
|
|
24
50
|
def nodelist
|
|
@@ -27,45 +53,91 @@ module Liquid
|
|
|
27
53
|
|
|
28
54
|
def unknown_tag(tag, markup, tokens)
|
|
29
55
|
case tag
|
|
30
|
-
when 'when'
|
|
56
|
+
when 'when'
|
|
31
57
|
record_when_condition(markup)
|
|
32
|
-
when 'else'
|
|
58
|
+
when 'else'
|
|
33
59
|
record_else_condition(markup)
|
|
34
60
|
else
|
|
35
61
|
super
|
|
36
62
|
end
|
|
37
63
|
end
|
|
38
64
|
|
|
39
|
-
def
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
def render_to_output_buffer(context, output)
|
|
66
|
+
execute_else_block = true
|
|
67
|
+
|
|
68
|
+
@blocks.each do |block|
|
|
69
|
+
if block.else?
|
|
70
|
+
block.attachment.render_to_output_buffer(context, output) if execute_else_block
|
|
71
|
+
next
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
result = Liquid::Utils.to_liquid_value(
|
|
75
|
+
block.evaluate(context),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if result
|
|
79
|
+
execute_else_block = false
|
|
80
|
+
block.attachment.render_to_output_buffer(context, output)
|
|
51
81
|
end
|
|
52
|
-
output
|
|
53
82
|
end
|
|
83
|
+
|
|
84
|
+
output
|
|
54
85
|
end
|
|
55
86
|
|
|
56
87
|
private
|
|
57
88
|
|
|
89
|
+
def rigid_parse(markup)
|
|
90
|
+
parser = @parse_context.new_parser(markup)
|
|
91
|
+
@left = safe_parse_expression(parser)
|
|
92
|
+
parser.consume(:end_of_string)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def strict_parse(markup)
|
|
96
|
+
lax_parse(markup)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def lax_parse(markup)
|
|
100
|
+
if markup =~ Syntax
|
|
101
|
+
@left = parse_expression(Regexp.last_match(1))
|
|
102
|
+
else
|
|
103
|
+
raise SyntaxError, options[:locale].t("errors.syntax.case")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
58
107
|
def record_when_condition(markup)
|
|
59
|
-
body =
|
|
108
|
+
body = new_body
|
|
109
|
+
|
|
110
|
+
if rigid_mode?
|
|
111
|
+
parse_rigid_when(markup, body)
|
|
112
|
+
else
|
|
113
|
+
parse_lax_when(markup, body)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def parse_rigid_when(markup, body)
|
|
118
|
+
parser = @parse_context.new_parser(markup)
|
|
119
|
+
|
|
120
|
+
loop do
|
|
121
|
+
expr = safe_parse_expression(parser)
|
|
122
|
+
block = Condition.new(@left, '==', expr)
|
|
123
|
+
block.attach(body)
|
|
124
|
+
@blocks << block
|
|
125
|
+
|
|
126
|
+
break unless parser.id?('or') || parser.consume?(:comma)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
parser.consume(:end_of_string)
|
|
130
|
+
end
|
|
60
131
|
|
|
132
|
+
def parse_lax_when(markup, body)
|
|
61
133
|
while markup
|
|
62
134
|
unless markup =~ WhenSyntax
|
|
63
|
-
raise SyntaxError
|
|
135
|
+
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_when")
|
|
64
136
|
end
|
|
65
137
|
|
|
66
|
-
markup =
|
|
138
|
+
markup = Regexp.last_match(2)
|
|
67
139
|
|
|
68
|
-
block = Condition.new(@left, '=='.
|
|
140
|
+
block = Condition.new(@left, '==', Condition.parse_expression(parse_context, Regexp.last_match(1)))
|
|
69
141
|
block.attach(body)
|
|
70
142
|
@blocks << block
|
|
71
143
|
end
|
|
@@ -73,14 +145,18 @@ module Liquid
|
|
|
73
145
|
|
|
74
146
|
def record_else_condition(markup)
|
|
75
147
|
unless markup.strip.empty?
|
|
76
|
-
raise SyntaxError
|
|
148
|
+
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_else")
|
|
77
149
|
end
|
|
78
150
|
|
|
79
151
|
block = ElseCondition.new
|
|
80
|
-
block.attach(
|
|
152
|
+
block.attach(new_body)
|
|
81
153
|
@blocks << block
|
|
82
154
|
end
|
|
83
|
-
end
|
|
84
155
|
|
|
85
|
-
|
|
156
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
157
|
+
def children
|
|
158
|
+
[@node.left] + @node.blocks
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
86
162
|
end
|