liquid 4.0.3 → 5.1.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 +54 -0
- data/README.md +6 -0
- data/lib/liquid/block.rb +31 -14
- data/lib/liquid/block_body.rb +166 -54
- data/lib/liquid/condition.rb +41 -20
- data/lib/liquid/context.rb +107 -52
- 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 +20 -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 +13 -3
- data/lib/liquid/register.rb +6 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/standardfilters.rb +95 -46
- 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 +40 -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 +34 -0
- data/lib/liquid/tags/for.rb +68 -44
- data/lib/liquid/tags/if.rb +39 -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 +23 -15
- data/lib/liquid/template.rb +53 -72
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +18 -10
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +13 -3
- data/lib/liquid/variable.rb +46 -41
- data/lib/liquid/variable_lookup.rb +11 -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 +609 -5
- data/test/integration/document_test.rb +4 -2
- data/test/integration/drop_test.rb +67 -83
- 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 +385 -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 +132 -124
- data/test/integration/trim_mode_test.rb +78 -44
- data/test/integration/variable_test.rb +74 -32
- data/test/test_helper.rb +113 -22
- 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 +16 -2
- 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 +26 -19
- data/test/unit/variable_unit_test.rb +51 -49
- metadata +76 -50
- data/lib/liquid/strainer.rb +0 -66
- data/lib/liquid/truffle.rb +0 -5
- data/test/truffle/truffle_test.rb +0 -9
- data/test/unit/context_unit_test.rb +0 -489
- data/test/unit/strainer_unit_test.rb +0 -164
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,76 +61,54 @@ 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
|
-
# Sets how strict the taint checker should be.
|
67
|
-
# :lax is the default, and ignores the taint flag completely
|
68
|
-
# :warn adds a warning, but does not interrupt the rendering
|
69
|
-
# :error raises an error when tainted output is used
|
70
|
-
attr_writer :taint_mode
|
64
|
+
attr_accessor :error_mode
|
65
|
+
Template.error_mode = :lax
|
71
66
|
|
72
67
|
attr_accessor :default_exception_renderer
|
73
68
|
Template.default_exception_renderer = lambda do |exception|
|
74
69
|
exception
|
75
70
|
end
|
76
71
|
|
77
|
-
|
78
|
-
|
79
|
-
end
|
72
|
+
attr_accessor :file_system
|
73
|
+
Template.file_system = BlankFileSystem.new
|
80
74
|
|
81
|
-
|
82
|
-
|
83
|
-
|
75
|
+
attr_accessor :tags
|
76
|
+
Template.tags = TagRegistry.new
|
77
|
+
private :tags=
|
84
78
|
|
85
79
|
def register_tag(name, klass)
|
86
80
|
tags[name.to_s] = klass
|
87
81
|
end
|
88
82
|
|
89
|
-
def tags
|
90
|
-
@tags ||= TagRegistry.new
|
91
|
-
end
|
92
|
-
|
93
|
-
def error_mode
|
94
|
-
@error_mode ||= :lax
|
95
|
-
end
|
96
|
-
|
97
|
-
def taint_mode
|
98
|
-
@taint_mode ||= :lax
|
99
|
-
end
|
100
|
-
|
101
83
|
# Pass a module with filter methods which should be available
|
102
84
|
# to all liquid views. Good for registering the standard library
|
103
85
|
def register_filter(mod)
|
104
|
-
|
86
|
+
StrainerFactory.add_global_filter(mod)
|
105
87
|
end
|
106
88
|
|
107
|
-
|
108
|
-
|
109
|
-
|
89
|
+
attr_accessor :default_resource_limits
|
90
|
+
Template.default_resource_limits = {}
|
91
|
+
private :default_resource_limits=
|
110
92
|
|
111
93
|
# creates a new <tt>Template</tt> object from liquid source code
|
112
94
|
# To enable profiling, pass in <tt>profile: true</tt> as an option.
|
113
95
|
# See Liquid::Profiler for more information
|
114
96
|
def parse(source, options = {})
|
115
|
-
|
116
|
-
template.parse(source, options)
|
97
|
+
new.parse(source, options)
|
117
98
|
end
|
118
99
|
end
|
119
100
|
|
120
101
|
def initialize
|
121
|
-
@rethrow_errors
|
122
|
-
@resource_limits = ResourceLimits.new(
|
102
|
+
@rethrow_errors = false
|
103
|
+
@resource_limits = ResourceLimits.new(Template.default_resource_limits)
|
123
104
|
end
|
124
105
|
|
125
106
|
# Parse source code.
|
126
107
|
# Returns self for easy chaining
|
127
108
|
def parse(source, options = {})
|
128
|
-
|
129
|
-
@
|
130
|
-
@
|
131
|
-
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
132
|
-
@root = Document.parse(tokenize(source), parse_context)
|
133
|
-
@warnings = parse_context.warnings
|
109
|
+
parse_context = configure_options(options)
|
110
|
+
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
111
|
+
@root = Document.parse(tokenizer, parse_context)
|
134
112
|
self
|
135
113
|
end
|
136
114
|
|
@@ -165,19 +143,19 @@ module Liquid
|
|
165
143
|
# filters and tags and might be useful to integrate liquid more with its host application
|
166
144
|
#
|
167
145
|
def render(*args)
|
168
|
-
return ''
|
146
|
+
return '' if @root.nil?
|
169
147
|
|
170
148
|
context = case args.first
|
171
149
|
when Liquid::Context
|
172
150
|
c = args.shift
|
173
151
|
|
174
152
|
if @rethrow_errors
|
175
|
-
c.exception_renderer =
|
153
|
+
c.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
176
154
|
end
|
177
155
|
|
178
156
|
c
|
179
157
|
when Liquid::Drop
|
180
|
-
drop
|
158
|
+
drop = args.shift
|
181
159
|
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
182
160
|
when Hash
|
183
161
|
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
@@ -187,11 +165,18 @@ module Liquid
|
|
187
165
|
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
188
166
|
end
|
189
167
|
|
168
|
+
output = nil
|
169
|
+
|
170
|
+
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
|
171
|
+
|
190
172
|
case args.last
|
191
173
|
when Hash
|
192
174
|
options = args.pop
|
175
|
+
output = options[:output] if options[:output]
|
193
176
|
|
194
|
-
|
177
|
+
options[:registers]&.each do |key, register|
|
178
|
+
context_register[key] = register
|
179
|
+
end
|
195
180
|
|
196
181
|
apply_options_to_context(context, options)
|
197
182
|
when Module, Array
|
@@ -201,13 +186,13 @@ module Liquid
|
|
201
186
|
# Retrying a render resets resource usage
|
202
187
|
context.resource_limits.reset
|
203
188
|
|
189
|
+
if @profiling && context.profiler.nil?
|
190
|
+
@profiler = context.profiler = Liquid::Profiler.new
|
191
|
+
end
|
192
|
+
|
204
193
|
begin
|
205
194
|
# render the nodelist.
|
206
|
-
|
207
|
-
result = with_profiling(context) do
|
208
|
-
@root.render(context)
|
209
|
-
end
|
210
|
-
result.respond_to?(:join) ? result.join : result
|
195
|
+
@root.render_to_output_buffer(context, output || +'')
|
211
196
|
rescue Liquid::MemoryError => e
|
212
197
|
context.handle_error(e)
|
213
198
|
ensure
|
@@ -220,35 +205,31 @@ module Liquid
|
|
220
205
|
render(*args)
|
221
206
|
end
|
222
207
|
|
223
|
-
|
224
|
-
|
225
|
-
def tokenize(source)
|
226
|
-
Tokenizer.new(source, @line_numbers)
|
208
|
+
def render_to_output_buffer(context, output)
|
209
|
+
render(context, output: output)
|
227
210
|
end
|
228
211
|
|
229
|
-
|
230
|
-
if @profiling && !context.partial
|
231
|
-
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
|
232
|
-
|
233
|
-
@profiler = Profiler.new
|
234
|
-
@profiler.start
|
212
|
+
private
|
235
213
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
@profiler.stop
|
240
|
-
end
|
241
|
-
else
|
242
|
-
yield
|
214
|
+
def configure_options(options)
|
215
|
+
if (profiling = options[:profile])
|
216
|
+
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
|
243
217
|
end
|
218
|
+
|
219
|
+
@options = options
|
220
|
+
@profiling = profiling
|
221
|
+
@line_numbers = options[:line_numbers] || @profiling
|
222
|
+
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
223
|
+
@warnings = parse_context.warnings
|
224
|
+
parse_context
|
244
225
|
end
|
245
226
|
|
246
227
|
def apply_options_to_context(context, options)
|
247
228
|
context.add_filters(options[:filters]) if options[:filters]
|
248
|
-
context.global_filter
|
229
|
+
context.global_filter = options[:global_filter] if options[:global_filter]
|
249
230
|
context.exception_renderer = options[:exception_renderer] if options[:exception_renderer]
|
250
|
-
context.strict_variables
|
251
|
-
context.strict_filters
|
231
|
+
context.strict_variables = options[:strict_variables] if options[:strict_variables]
|
232
|
+
context.strict_filters = options[:strict_filters] if options[:strict_filters]
|
252
233
|
end
|
253
234
|
end
|
254
235
|
end
|
data/lib/liquid/tokenizer.rb
CHANGED
@@ -1,29 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class Tokenizer
|
3
|
-
attr_reader :line_number
|
5
|
+
attr_reader :line_number, :for_liquid_tag
|
4
6
|
|
5
|
-
def initialize(source, line_numbers = false)
|
6
|
-
@source
|
7
|
-
@line_number
|
8
|
-
@
|
7
|
+
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
8
|
+
@source = source.to_s.to_str
|
9
|
+
@line_number = line_number || (line_numbers ? 1 : nil)
|
10
|
+
@for_liquid_tag = for_liquid_tag
|
11
|
+
@tokens = tokenize
|
9
12
|
end
|
10
13
|
|
11
14
|
def shift
|
12
|
-
token = @tokens.shift
|
13
|
-
|
15
|
+
(token = @tokens.shift) || return
|
16
|
+
|
17
|
+
if @line_number
|
18
|
+
@line_number += @for_liquid_tag ? 1 : token.count("\n")
|
19
|
+
end
|
20
|
+
|
14
21
|
token
|
15
22
|
end
|
16
23
|
|
17
24
|
private
|
18
25
|
|
19
26
|
def tokenize
|
20
|
-
|
21
|
-
|
27
|
+
return [] if @source.empty?
|
28
|
+
|
29
|
+
return @source.split("\n") if @for_liquid_tag
|
22
30
|
|
23
31
|
tokens = @source.split(TemplateParser)
|
24
32
|
|
25
33
|
# removes the rogue empty element at the beginning of the array
|
26
|
-
tokens.shift if tokens[0]
|
34
|
+
tokens.shift if tokens[0]&.empty?
|
27
35
|
|
28
36
|
tokens
|
29
37
|
end
|
data/lib/liquid/usage.rb
ADDED
data/lib/liquid/utils.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
module Utils
|
3
5
|
def self.slice_collection(collection, from, to)
|
@@ -10,7 +12,7 @@ module Liquid
|
|
10
12
|
|
11
13
|
def self.slice_collection_using_each(collection, from, to)
|
12
14
|
segments = []
|
13
|
-
index
|
15
|
+
index = 0
|
14
16
|
|
15
17
|
# Maintains Ruby 1.8.7 String#each behaviour on 1.9
|
16
18
|
if collection.is_a?(String)
|
@@ -50,7 +52,7 @@ module Liquid
|
|
50
52
|
when Numeric
|
51
53
|
obj
|
52
54
|
when String
|
53
|
-
|
55
|
+
/\A-?\d+\.\d+\z/.match?(obj.strip) ? BigDecimal(obj) : obj.to_i
|
54
56
|
else
|
55
57
|
if obj.respond_to?(:to_number)
|
56
58
|
obj.to_number
|
@@ -69,7 +71,7 @@ module Liquid
|
|
69
71
|
end
|
70
72
|
|
71
73
|
case obj
|
72
|
-
when 'now'
|
74
|
+
when 'now', 'today'
|
73
75
|
Time.now
|
74
76
|
when /\A\d+\z/, Integer
|
75
77
|
Time.at(obj.to_i)
|
@@ -79,5 +81,13 @@ module Liquid
|
|
79
81
|
rescue ::ArgumentError
|
80
82
|
nil
|
81
83
|
end
|
84
|
+
|
85
|
+
def self.to_liquid_value(obj)
|
86
|
+
# Enable "obj" to represent itself as a primitive value like integer, string, or boolean
|
87
|
+
return obj.to_liquid_value if obj.respond_to?(:to_liquid_value)
|
88
|
+
|
89
|
+
# Otherwise return the object itself
|
90
|
+
obj
|
91
|
+
end
|
82
92
|
end
|
83
93
|
end
|
data/lib/liquid/variable.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# Holds variables. Variables are only loaded "just in time"
|
3
5
|
# and are not evaluated as part of the render stage
|
@@ -10,10 +12,10 @@ module Liquid
|
|
10
12
|
# {{ user | link }}
|
11
13
|
#
|
12
14
|
class Variable
|
13
|
-
FilterMarkupRegex
|
14
|
-
FilterParser
|
15
|
-
FilterArgsRegex
|
16
|
-
JustTagAttributes
|
15
|
+
FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
|
16
|
+
FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
|
17
|
+
FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
|
18
|
+
JustTagAttributes = /\A#{TagAttributes}\z/o
|
17
19
|
MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
|
18
20
|
|
19
21
|
attr_accessor :filters, :name, :line_number
|
@@ -23,12 +25,12 @@ module Liquid
|
|
23
25
|
include ParserSwitching
|
24
26
|
|
25
27
|
def initialize(markup, parse_context)
|
26
|
-
@markup
|
27
|
-
@name
|
28
|
+
@markup = markup
|
29
|
+
@name = nil
|
28
30
|
@parse_context = parse_context
|
29
|
-
@line_number
|
31
|
+
@line_number = parse_context.line_number
|
30
32
|
|
31
|
-
|
33
|
+
strict_parse_with_error_mode_fallback(markup)
|
32
34
|
end
|
33
35
|
|
34
36
|
def raw
|
@@ -43,11 +45,11 @@ module Liquid
|
|
43
45
|
@filters = []
|
44
46
|
return unless markup =~ MarkupWithQuotedFragment
|
45
47
|
|
46
|
-
name_markup
|
47
|
-
filter_markup =
|
48
|
-
@name
|
48
|
+
name_markup = Regexp.last_match(1)
|
49
|
+
filter_markup = Regexp.last_match(2)
|
50
|
+
@name = Expression.parse(name_markup)
|
49
51
|
if filter_markup =~ FilterMarkupRegex
|
50
|
-
filters =
|
52
|
+
filters = Regexp.last_match(1).scan(FilterParser)
|
51
53
|
filters.each do |f|
|
52
54
|
next unless f =~ /\w+/
|
53
55
|
filtername = Regexp.last_match(0)
|
@@ -61,6 +63,8 @@ module Liquid
|
|
61
63
|
@filters = []
|
62
64
|
p = Parser.new(markup)
|
63
65
|
|
66
|
+
return if p.look(:end_of_string)
|
67
|
+
|
64
68
|
@name = Expression.parse(p.expression)
|
65
69
|
while p.consume?(:pipe)
|
66
70
|
filtername = p.consume(:id)
|
@@ -79,37 +83,57 @@ module Liquid
|
|
79
83
|
end
|
80
84
|
|
81
85
|
def render(context)
|
82
|
-
obj =
|
86
|
+
obj = context.evaluate(@name)
|
87
|
+
|
88
|
+
@filters.each do |filter_name, filter_args, filter_kwargs|
|
83
89
|
filter_args = evaluate_filter_expressions(context, filter_args, filter_kwargs)
|
84
|
-
context.invoke(filter_name,
|
90
|
+
obj = context.invoke(filter_name, obj, *filter_args)
|
91
|
+
end
|
92
|
+
|
93
|
+
context.apply_global_filter(obj)
|
94
|
+
end
|
95
|
+
|
96
|
+
def render_to_output_buffer(context, output)
|
97
|
+
obj = render(context)
|
98
|
+
|
99
|
+
if obj.is_a?(Array)
|
100
|
+
output << obj.join
|
101
|
+
elsif obj.nil?
|
102
|
+
else
|
103
|
+
output << obj.to_s
|
85
104
|
end
|
86
105
|
|
87
|
-
|
106
|
+
output
|
107
|
+
end
|
88
108
|
|
89
|
-
|
109
|
+
def disabled?(_context)
|
110
|
+
false
|
111
|
+
end
|
90
112
|
|
91
|
-
|
113
|
+
def disabled_tags
|
114
|
+
[]
|
92
115
|
end
|
93
116
|
|
94
117
|
private
|
95
118
|
|
96
119
|
def parse_filter_expressions(filter_name, unparsed_args)
|
97
|
-
filter_args
|
98
|
-
keyword_args =
|
120
|
+
filter_args = []
|
121
|
+
keyword_args = nil
|
99
122
|
unparsed_args.each do |a|
|
100
|
-
if matches = a.match(JustTagAttributes)
|
123
|
+
if (matches = a.match(JustTagAttributes))
|
124
|
+
keyword_args ||= {}
|
101
125
|
keyword_args[matches[1]] = Expression.parse(matches[2])
|
102
126
|
else
|
103
127
|
filter_args << Expression.parse(a)
|
104
128
|
end
|
105
129
|
end
|
106
130
|
result = [filter_name, filter_args]
|
107
|
-
result << keyword_args
|
131
|
+
result << keyword_args if keyword_args
|
108
132
|
result
|
109
133
|
end
|
110
134
|
|
111
135
|
def evaluate_filter_expressions(context, filter_args, filter_kwargs)
|
112
|
-
parsed_args = filter_args.map{ |expr| context.evaluate(expr) }
|
136
|
+
parsed_args = filter_args.map { |expr| context.evaluate(expr) }
|
113
137
|
if filter_kwargs
|
114
138
|
parsed_kwargs = {}
|
115
139
|
filter_kwargs.each do |key, expr|
|
@@ -120,25 +144,6 @@ module Liquid
|
|
120
144
|
parsed_args
|
121
145
|
end
|
122
146
|
|
123
|
-
def taint_check(context, obj)
|
124
|
-
return unless obj.tainted?
|
125
|
-
return if Template.taint_mode == :lax
|
126
|
-
|
127
|
-
@markup =~ QuotedFragment
|
128
|
-
name = Regexp.last_match(0)
|
129
|
-
|
130
|
-
error = TaintedError.new("variable '#{name}' is tainted and was not escaped")
|
131
|
-
error.line_number = line_number
|
132
|
-
error.template_name = context.template_name
|
133
|
-
|
134
|
-
case Template.taint_mode
|
135
|
-
when :warn
|
136
|
-
context.warnings << error
|
137
|
-
when :error
|
138
|
-
raise error
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
147
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
143
148
|
def children
|
144
149
|
[@node.name] + @node.filters.flatten
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class VariableLookup
|
3
5
|
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
4
|
-
COMMAND_METHODS
|
6
|
+
COMMAND_METHODS = ['size', 'first', 'last'].freeze
|
5
7
|
|
6
8
|
attr_reader :name, :lookups
|
7
9
|
|
@@ -14,17 +16,17 @@ module Liquid
|
|
14
16
|
|
15
17
|
name = lookups.shift
|
16
18
|
if name =~ SQUARE_BRACKETED
|
17
|
-
name = Expression.parse(
|
19
|
+
name = Expression.parse(Regexp.last_match(1))
|
18
20
|
end
|
19
21
|
@name = name
|
20
22
|
|
21
|
-
@lookups
|
23
|
+
@lookups = lookups
|
22
24
|
@command_flags = 0
|
23
25
|
|
24
26
|
@lookups.each_index do |i|
|
25
27
|
lookup = lookups[i]
|
26
28
|
if lookup =~ SQUARE_BRACKETED
|
27
|
-
lookups[i] = Expression.parse(
|
29
|
+
lookups[i] = Expression.parse(Regexp.last_match(1))
|
28
30
|
elsif COMMAND_METHODS.include?(lookup)
|
29
31
|
@command_flags |= 1 << i
|
30
32
|
end
|
@@ -32,12 +34,15 @@ module Liquid
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def evaluate(context)
|
35
|
-
name
|
37
|
+
name = context.evaluate(@name)
|
36
38
|
object = context.find_variable(name)
|
37
39
|
|
38
40
|
@lookups.each_index do |i|
|
39
41
|
key = context.evaluate(@lookups[i])
|
40
42
|
|
43
|
+
# Cast "key" to its liquid value to enable it to act as a primitive value
|
44
|
+
key = Liquid::Utils.to_liquid_value(key)
|
45
|
+
|
41
46
|
# If object is a hash- or array-like object we look for the
|
42
47
|
# presence of the key and if its available we return it
|
43
48
|
if object.respond_to?(:[]) &&
|
@@ -45,7 +50,7 @@ module Liquid
|
|
45
50
|
(object.respond_to?(:fetch) && key.is_a?(Integer)))
|
46
51
|
|
47
52
|
# if its a proc we will replace the entry with the proc
|
48
|
-
res
|
53
|
+
res = context.lookup_and_evaluate(object, key)
|
49
54
|
object = res.to_liquid
|
50
55
|
|
51
56
|
# Some special cases. If the part wasn't in square brackets and
|
data/lib/liquid/version.rb
CHANGED
data/lib/liquid.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2005 Tobias Luetke
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining
|
@@ -21,10 +23,10 @@
|
|
21
23
|
|
22
24
|
module Liquid
|
23
25
|
FilterSeparator = /\|/
|
24
|
-
ArgumentSeparator = ','
|
25
|
-
FilterArgumentSeparator = ':'
|
26
|
-
VariableAttributeSeparator = '.'
|
27
|
-
WhitespaceControl = '-'
|
26
|
+
ArgumentSeparator = ','
|
27
|
+
FilterArgumentSeparator = ':'
|
28
|
+
VariableAttributeSeparator = '.'
|
29
|
+
WhitespaceControl = '-'
|
28
30
|
TagStart = /\{\%/
|
29
31
|
TagEnd = /\%\}/
|
30
32
|
VariableSignature = /\(?[\w\-\.\[\]]\)?/
|
@@ -40,6 +42,8 @@ module Liquid
|
|
40
42
|
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
41
43
|
VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
|
42
44
|
|
45
|
+
RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }
|
46
|
+
|
43
47
|
singleton_class.send(:attr_accessor, :cache_classes)
|
44
48
|
self.cache_classes = true
|
45
49
|
end
|
@@ -55,11 +59,14 @@ require 'liquid/forloop_drop'
|
|
55
59
|
require 'liquid/extensions'
|
56
60
|
require 'liquid/errors'
|
57
61
|
require 'liquid/interrupts'
|
58
|
-
require 'liquid/
|
62
|
+
require 'liquid/strainer_factory'
|
63
|
+
require 'liquid/strainer_template'
|
59
64
|
require 'liquid/expression'
|
60
65
|
require 'liquid/context'
|
61
66
|
require 'liquid/parser_switching'
|
62
67
|
require 'liquid/tag'
|
68
|
+
require 'liquid/tag/disabler'
|
69
|
+
require 'liquid/tag/disableable'
|
63
70
|
require 'liquid/block'
|
64
71
|
require 'liquid/block_body'
|
65
72
|
require 'liquid/document'
|
@@ -74,6 +81,11 @@ require 'liquid/condition'
|
|
74
81
|
require 'liquid/utils'
|
75
82
|
require 'liquid/tokenizer'
|
76
83
|
require 'liquid/parse_context'
|
84
|
+
require 'liquid/partial_cache'
|
85
|
+
require 'liquid/usage'
|
86
|
+
require 'liquid/register'
|
87
|
+
require 'liquid/static_registers'
|
88
|
+
require 'liquid/template_factory'
|
77
89
|
|
78
90
|
# Load all the tags of the standard library
|
79
91
|
#
|