liquid 4.0.0.rc3 → 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 +5 -5
- data/History.md +93 -2
- data/README.md +8 -0
- data/lib/liquid.rb +18 -5
- data/lib/liquid/block.rb +47 -20
- data/lib/liquid/block_body.rb +190 -76
- data/lib/liquid/condition.rb +69 -29
- data/lib/liquid/context.rb +122 -76
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +4 -2
- data/lib/liquid/errors.rb +20 -25
- data/lib/liquid/expression.rb +30 -31
- data/lib/liquid/extensions.rb +8 -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 +35 -26
- data/lib/liquid/locales/en.yml +4 -2
- data/lib/liquid/parse_context.rb +17 -4
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- 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.rb +67 -86
- data/lib/liquid/profiler/hooks.rb +26 -14
- 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 +171 -57
- 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.rb +28 -6
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tags/assign.rb +32 -10
- data/lib/liquid/tags/break.rb +8 -3
- data/lib/liquid/tags/capture.rb +11 -8
- data/lib/liquid/tags/case.rb +41 -27
- data/lib/liquid/tags/comment.rb +5 -3
- data/lib/liquid/tags/continue.rb +8 -3
- data/lib/liquid/tags/cycle.rb +35 -16
- data/lib/liquid/tags/decrement.rb +6 -3
- data/lib/liquid/tags/echo.rb +26 -0
- data/lib/liquid/tags/for.rb +79 -47
- data/lib/liquid/tags/if.rb +53 -30
- data/lib/liquid/tags/ifchanged.rb +11 -10
- data/lib/liquid/tags/include.rb +42 -44
- 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 +32 -20
- data/lib/liquid/tags/unless.rb +15 -15
- data/lib/liquid/template.rb +60 -71
- 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 +6 -4
- data/lib/liquid/variable.rb +55 -38
- data/lib/liquid/variable_lookup.rb +14 -6
- data/lib/liquid/version.rb +3 -1
- data/test/integration/assign_test.rb +74 -5
- data/test/integration/blank_test.rb +11 -8
- data/test/integration/block_test.rb +58 -0
- 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 -83
- data/test/integration/error_handling_test.rb +90 -60
- 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 +24 -8
- data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
- data/test/integration/security_test.rb +41 -18
- data/test/integration/standard_filter_test.rb +523 -205
- 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 +109 -53
- data/test/integration/tags/if_else_tag_test.rb +5 -3
- data/test/integration/tags/include_tag_test.rb +83 -52
- 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 +128 -121
- data/test/integration/trim_mode_test.rb +82 -44
- data/test/integration/variable_test.rb +46 -31
- data/test/test_helper.rb +75 -23
- data/test/unit/block_unit_test.rb +19 -24
- data/test/unit/condition_unit_test.rb +82 -72
- 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 +12 -10
- data/test/unit/parse_tree_visitor_test.rb +247 -0
- 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 +83 -50
- data/lib/liquid/strainer.rb +0 -65
- data/test/unit/context_unit_test.rb +0 -483
- data/test/unit/strainer_unit_test.rb +0 -136
data/lib/liquid/range_lookup.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class RangeLookup
|
3
5
|
def self.parse(start_markup, end_markup)
|
4
6
|
start_obj = Expression.parse(start_markup)
|
5
|
-
end_obj
|
7
|
+
end_obj = Expression.parse(end_markup)
|
6
8
|
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
7
9
|
new(start_obj, end_obj)
|
8
10
|
else
|
@@ -12,12 +14,12 @@ module Liquid
|
|
12
14
|
|
13
15
|
def initialize(start_obj, end_obj)
|
14
16
|
@start_obj = start_obj
|
15
|
-
@end_obj
|
17
|
+
@end_obj = end_obj
|
16
18
|
end
|
17
19
|
|
18
20
|
def evaluate(context)
|
19
21
|
start_int = to_integer(context.evaluate(@start_obj))
|
20
|
-
end_int
|
22
|
+
end_int = to_integer(context.evaluate(@end_obj))
|
21
23
|
start_int..end_int
|
22
24
|
end
|
23
25
|
|
@@ -1,23 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class ResourceLimits
|
3
|
-
attr_accessor :
|
4
|
-
|
5
|
+
attr_accessor :render_length_limit, :render_score_limit, :assign_score_limit
|
6
|
+
attr_reader :render_score, :assign_score
|
5
7
|
|
6
8
|
def initialize(limits)
|
7
9
|
@render_length_limit = limits[:render_length_limit]
|
8
|
-
@render_score_limit
|
9
|
-
@assign_score_limit
|
10
|
+
@render_score_limit = limits[:render_score_limit]
|
11
|
+
@assign_score_limit = limits[:assign_score_limit]
|
10
12
|
reset
|
11
13
|
end
|
12
14
|
|
15
|
+
def increment_render_score(amount)
|
16
|
+
@render_score += amount
|
17
|
+
raise_limits_reached if @render_score_limit && @render_score > @render_score_limit
|
18
|
+
end
|
19
|
+
|
20
|
+
def increment_assign_score(amount)
|
21
|
+
@assign_score += amount
|
22
|
+
raise_limits_reached if @assign_score_limit && @assign_score > @assign_score_limit
|
23
|
+
end
|
24
|
+
|
25
|
+
# update either render_length or assign_score based on whether or not the writes are captured
|
26
|
+
def increment_write_score(output)
|
27
|
+
if (last_captured = @last_capture_length)
|
28
|
+
captured = output.bytesize
|
29
|
+
increment = captured - last_captured
|
30
|
+
@last_capture_length = captured
|
31
|
+
increment_assign_score(increment)
|
32
|
+
elsif @render_length_limit && output.bytesize > @render_length_limit
|
33
|
+
raise_limits_reached
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def raise_limits_reached
|
38
|
+
@reached_limit = true
|
39
|
+
raise MemoryError, "Memory limits exceeded"
|
40
|
+
end
|
41
|
+
|
13
42
|
def reached?
|
14
|
-
|
15
|
-
(@render_score_limit && @render_score > @render_score_limit) ||
|
16
|
-
(@assign_score_limit && @assign_score > @assign_score_limit)
|
43
|
+
@reached_limit
|
17
44
|
end
|
18
45
|
|
19
46
|
def reset
|
20
|
-
@
|
47
|
+
@reached_limit = false
|
48
|
+
@last_capture_length = nil
|
49
|
+
@render_score = @assign_score = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
def with_capture
|
53
|
+
old_capture_length = @last_capture_length
|
54
|
+
begin
|
55
|
+
@last_capture_length = 0
|
56
|
+
yield
|
57
|
+
ensure
|
58
|
+
@last_capture_length = old_capture_length
|
59
|
+
end
|
21
60
|
end
|
22
61
|
end
|
23
62
|
end
|
@@ -1,16 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cgi'
|
2
4
|
require 'bigdecimal'
|
3
5
|
|
4
6
|
module Liquid
|
5
7
|
module StandardFilters
|
6
8
|
HTML_ESCAPE = {
|
7
|
-
'&'
|
8
|
-
'>'
|
9
|
-
'<'
|
10
|
-
'"'
|
11
|
-
"'"
|
12
|
-
}
|
9
|
+
'&' => '&',
|
10
|
+
'>' => '>',
|
11
|
+
'<' => '<',
|
12
|
+
'"' => '"',
|
13
|
+
"'" => ''',
|
14
|
+
}.freeze
|
13
15
|
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
16
|
+
STRIP_HTML_BLOCKS = Regexp.union(
|
17
|
+
%r{<script.*?</script>}m,
|
18
|
+
/<!--.*?-->/m,
|
19
|
+
%r{<style.*?</style>}m
|
20
|
+
)
|
21
|
+
STRIP_HTML_TAGS = /<.*?>/m
|
14
22
|
|
15
23
|
# Return the size of an array or of an string
|
16
24
|
def size(input)
|
@@ -33,7 +41,7 @@ module Liquid
|
|
33
41
|
end
|
34
42
|
|
35
43
|
def escape(input)
|
36
|
-
CGI.escapeHTML(input)
|
44
|
+
CGI.escapeHTML(input.to_s) unless input.nil?
|
37
45
|
end
|
38
46
|
alias_method :h, :escape
|
39
47
|
|
@@ -42,11 +50,16 @@ module Liquid
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def url_encode(input)
|
45
|
-
CGI.escape(input) unless input.nil?
|
53
|
+
CGI.escape(input.to_s) unless input.nil?
|
46
54
|
end
|
47
55
|
|
48
56
|
def url_decode(input)
|
49
|
-
|
57
|
+
return if input.nil?
|
58
|
+
|
59
|
+
result = CGI.unescape(input.to_s)
|
60
|
+
raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?
|
61
|
+
|
62
|
+
result
|
50
63
|
end
|
51
64
|
|
52
65
|
def slice(input, offset, length = nil)
|
@@ -61,22 +74,28 @@ module Liquid
|
|
61
74
|
end
|
62
75
|
|
63
76
|
# Truncate a string down to x characters
|
64
|
-
def truncate(input, length = 50, truncate_string = "..."
|
77
|
+
def truncate(input, length = 50, truncate_string = "...")
|
65
78
|
return if input.nil?
|
66
79
|
input_str = input.to_s
|
67
|
-
length
|
68
|
-
|
80
|
+
length = Utils.to_integer(length)
|
81
|
+
|
82
|
+
truncate_string_str = truncate_string.to_s
|
83
|
+
|
84
|
+
l = length - truncate_string_str.length
|
69
85
|
l = 0 if l < 0
|
70
|
-
|
86
|
+
|
87
|
+
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
71
88
|
end
|
72
89
|
|
73
|
-
def truncatewords(input, words = 15, truncate_string = "..."
|
90
|
+
def truncatewords(input, words = 15, truncate_string = "...")
|
74
91
|
return if input.nil?
|
75
92
|
wordlist = input.to_s.split
|
76
|
-
words
|
93
|
+
words = Utils.to_integer(words)
|
94
|
+
|
77
95
|
l = words - 1
|
78
96
|
l = 0 if l < 0
|
79
|
-
|
97
|
+
|
98
|
+
wordlist.length > l ? wordlist[0..l].join(" ").concat(truncate_string.to_s) : input
|
80
99
|
end
|
81
100
|
|
82
101
|
# Split input string into an array of substrings separated by given pattern.
|
@@ -85,7 +104,7 @@ module Liquid
|
|
85
104
|
# <div class="summary">{{ post | split '//' | first }}</div>
|
86
105
|
#
|
87
106
|
def split(input, pattern)
|
88
|
-
input.to_s.split(pattern)
|
107
|
+
input.to_s.split(pattern.to_s)
|
89
108
|
end
|
90
109
|
|
91
110
|
def strip(input)
|
@@ -101,113 +120,160 @@ module Liquid
|
|
101
120
|
end
|
102
121
|
|
103
122
|
def strip_html(input)
|
104
|
-
empty
|
105
|
-
input.to_s.gsub(
|
123
|
+
empty = ''
|
124
|
+
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
125
|
+
result.gsub!(STRIP_HTML_TAGS, empty)
|
126
|
+
result
|
106
127
|
end
|
107
128
|
|
108
129
|
# Remove all newlines from the string
|
109
130
|
def strip_newlines(input)
|
110
|
-
input.to_s.gsub(/\r?\n/, ''
|
131
|
+
input.to_s.gsub(/\r?\n/, '')
|
111
132
|
end
|
112
133
|
|
113
134
|
# Join elements of the array with certain character between them
|
114
|
-
def join(input, glue = ' '
|
115
|
-
InputIterator.new(input).join(glue)
|
135
|
+
def join(input, glue = ' ')
|
136
|
+
InputIterator.new(input, context).join(glue)
|
116
137
|
end
|
117
138
|
|
118
139
|
# Sort elements of the array
|
119
140
|
# provide optional property with which to sort an array of hashes or drops
|
120
141
|
def sort(input, property = nil)
|
121
|
-
ary = InputIterator.new(input)
|
142
|
+
ary = InputIterator.new(input, context)
|
143
|
+
|
144
|
+
return [] if ary.empty?
|
145
|
+
|
122
146
|
if property.nil?
|
123
|
-
ary.sort
|
124
|
-
|
125
|
-
|
126
|
-
elsif ary.
|
127
|
-
|
147
|
+
ary.sort do |a, b|
|
148
|
+
nil_safe_compare(a, b)
|
149
|
+
end
|
150
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
151
|
+
begin
|
152
|
+
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
|
153
|
+
rescue TypeError
|
154
|
+
raise_property_error(property)
|
155
|
+
end
|
128
156
|
end
|
129
157
|
end
|
130
158
|
|
131
159
|
# Sort elements of an array ignoring case if strings
|
132
160
|
# provide optional property with which to sort an array of hashes or drops
|
133
161
|
def sort_natural(input, property = nil)
|
134
|
-
ary = InputIterator.new(input)
|
162
|
+
ary = InputIterator.new(input, context)
|
163
|
+
|
164
|
+
return [] if ary.empty?
|
135
165
|
|
136
166
|
if property.nil?
|
137
|
-
ary.sort
|
138
|
-
|
167
|
+
ary.sort do |a, b|
|
168
|
+
nil_safe_casecmp(a, b)
|
169
|
+
end
|
170
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
171
|
+
begin
|
172
|
+
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
|
173
|
+
rescue TypeError
|
174
|
+
raise_property_error(property)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Filter the elements of an array to those with a certain property value.
|
180
|
+
# By default the target is any truthy value.
|
181
|
+
def where(input, property, target_value = nil)
|
182
|
+
ary = InputIterator.new(input, context)
|
183
|
+
|
184
|
+
if ary.empty?
|
139
185
|
[]
|
140
|
-
elsif ary.first.respond_to?(:[]) &&
|
141
|
-
|
186
|
+
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
187
|
+
begin
|
188
|
+
ary.select { |item| item[property] }
|
189
|
+
rescue TypeError
|
190
|
+
raise_property_error(property)
|
191
|
+
end
|
192
|
+
elsif ary.first.respond_to?(:[])
|
193
|
+
begin
|
194
|
+
ary.select { |item| item[property] == target_value }
|
195
|
+
rescue TypeError
|
196
|
+
raise_property_error(property)
|
197
|
+
end
|
142
198
|
end
|
143
199
|
end
|
144
200
|
|
145
201
|
# Remove duplicate elements from an array
|
146
202
|
# provide optional property with which to determine uniqueness
|
147
203
|
def uniq(input, property = nil)
|
148
|
-
ary = InputIterator.new(input)
|
204
|
+
ary = InputIterator.new(input, context)
|
149
205
|
|
150
206
|
if property.nil?
|
151
207
|
ary.uniq
|
152
208
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
153
209
|
[]
|
154
210
|
elsif ary.first.respond_to?(:[])
|
155
|
-
|
211
|
+
begin
|
212
|
+
ary.uniq { |a| a[property] }
|
213
|
+
rescue TypeError
|
214
|
+
raise_property_error(property)
|
215
|
+
end
|
156
216
|
end
|
157
217
|
end
|
158
218
|
|
159
219
|
# Reverse the elements of an array
|
160
220
|
def reverse(input)
|
161
|
-
ary = InputIterator.new(input)
|
221
|
+
ary = InputIterator.new(input, context)
|
162
222
|
ary.reverse
|
163
223
|
end
|
164
224
|
|
165
225
|
# map/collect on a given property
|
166
226
|
def map(input, property)
|
167
|
-
InputIterator.new(input).map do |e|
|
227
|
+
InputIterator.new(input, context).map do |e|
|
168
228
|
e = e.call if e.is_a?(Proc)
|
169
229
|
|
170
|
-
if property == "to_liquid"
|
230
|
+
if property == "to_liquid"
|
171
231
|
e
|
172
232
|
elsif e.respond_to?(:[])
|
173
233
|
r = e[property]
|
174
234
|
r.is_a?(Proc) ? r.call : r
|
175
235
|
end
|
176
236
|
end
|
237
|
+
rescue TypeError
|
238
|
+
raise_property_error(property)
|
177
239
|
end
|
178
240
|
|
179
241
|
# Remove nils within an array
|
180
242
|
# provide optional property with which to check for nil
|
181
243
|
def compact(input, property = nil)
|
182
|
-
ary = InputIterator.new(input)
|
244
|
+
ary = InputIterator.new(input, context)
|
183
245
|
|
184
246
|
if property.nil?
|
185
247
|
ary.compact
|
186
248
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
187
249
|
[]
|
188
250
|
elsif ary.first.respond_to?(:[])
|
189
|
-
|
251
|
+
begin
|
252
|
+
ary.reject { |a| a[property].nil? }
|
253
|
+
rescue TypeError
|
254
|
+
raise_property_error(property)
|
255
|
+
end
|
190
256
|
end
|
191
257
|
end
|
192
258
|
|
193
259
|
# Replace occurrences of a string with another
|
194
|
-
def replace(input, string, replacement = ''
|
260
|
+
def replace(input, string, replacement = '')
|
195
261
|
input.to_s.gsub(string.to_s, replacement.to_s)
|
196
262
|
end
|
197
263
|
|
198
264
|
# Replace the first occurrences of a string with another
|
199
|
-
def replace_first(input, string, replacement = ''
|
265
|
+
def replace_first(input, string, replacement = '')
|
200
266
|
input.to_s.sub(string.to_s, replacement.to_s)
|
201
267
|
end
|
202
268
|
|
203
269
|
# remove a substring
|
204
270
|
def remove(input, string)
|
205
|
-
input.to_s.gsub(string.to_s, ''
|
271
|
+
input.to_s.gsub(string.to_s, '')
|
206
272
|
end
|
207
273
|
|
208
274
|
# remove the first occurrences of a substring
|
209
275
|
def remove_first(input, string)
|
210
|
-
input.to_s.sub(string.to_s, ''
|
276
|
+
input.to_s.sub(string.to_s, '')
|
211
277
|
end
|
212
278
|
|
213
279
|
# add one string to another
|
@@ -217,9 +283,9 @@ module Liquid
|
|
217
283
|
|
218
284
|
def concat(input, array)
|
219
285
|
unless array.respond_to?(:to_ary)
|
220
|
-
raise ArgumentError
|
286
|
+
raise ArgumentError, "concat filter requires an array argument"
|
221
287
|
end
|
222
|
-
InputIterator.new(input).concat(array)
|
288
|
+
InputIterator.new(input, context).concat(array)
|
223
289
|
end
|
224
290
|
|
225
291
|
# prepend a string to another
|
@@ -229,7 +295,7 @@ module Liquid
|
|
229
295
|
|
230
296
|
# Add <br /> tags in front of all newlines in input string
|
231
297
|
def newline_to_br(input)
|
232
|
-
input.to_s.gsub(/\n/, "<br />\n"
|
298
|
+
input.to_s.gsub(/\n/, "<br />\n")
|
233
299
|
end
|
234
300
|
|
235
301
|
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
@@ -266,7 +332,7 @@ module Liquid
|
|
266
332
|
def date(input, format)
|
267
333
|
return input if format.to_s.empty?
|
268
334
|
|
269
|
-
return input unless date = Utils.to_date(input)
|
335
|
+
return input unless (date = Utils.to_date(input))
|
270
336
|
|
271
337
|
date.strftime(format.to_s)
|
272
338
|
end
|
@@ -344,26 +410,73 @@ module Liquid
|
|
344
410
|
raise Liquid::FloatDomainError, e.message
|
345
411
|
end
|
346
412
|
|
347
|
-
def
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
413
|
+
def at_least(input, n)
|
414
|
+
min_value = Utils.to_number(n)
|
415
|
+
|
416
|
+
result = Utils.to_number(input)
|
417
|
+
result = min_value if min_value > result
|
418
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
419
|
+
end
|
420
|
+
|
421
|
+
def at_most(input, n)
|
422
|
+
max_value = Utils.to_number(n)
|
423
|
+
|
424
|
+
result = Utils.to_number(input)
|
425
|
+
result = max_value if max_value < result
|
426
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
427
|
+
end
|
428
|
+
|
429
|
+
# Set a default value when the input is nil, false or empty
|
430
|
+
#
|
431
|
+
# Example:
|
432
|
+
# {{ product.title | default: "No Title" }}
|
433
|
+
#
|
434
|
+
# Use `allow_false` when an input should only be tested against nil or empty and not false.
|
435
|
+
#
|
436
|
+
# Example:
|
437
|
+
# {{ product.title | default: "No Title", allow_false: true }}
|
438
|
+
#
|
439
|
+
def default(input, default_value = '', options = {})
|
440
|
+
options = {} unless options.is_a?(Hash)
|
441
|
+
false_check = options['allow_false'] ? input.nil? : !input
|
442
|
+
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
353
443
|
end
|
354
444
|
|
355
445
|
private
|
356
446
|
|
447
|
+
attr_reader :context
|
448
|
+
|
449
|
+
def raise_property_error(property)
|
450
|
+
raise Liquid::ArgumentError, "cannot select the property '#{property}'"
|
451
|
+
end
|
452
|
+
|
357
453
|
def apply_operation(input, operand, operation)
|
358
454
|
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
359
455
|
result.is_a?(BigDecimal) ? result.to_f : result
|
360
456
|
end
|
361
457
|
|
458
|
+
def nil_safe_compare(a, b)
|
459
|
+
if !a.nil? && !b.nil?
|
460
|
+
a <=> b
|
461
|
+
else
|
462
|
+
a.nil? ? 1 : -1
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
def nil_safe_casecmp(a, b)
|
467
|
+
if !a.nil? && !b.nil?
|
468
|
+
a.to_s.casecmp(b.to_s)
|
469
|
+
else
|
470
|
+
a.nil? ? 1 : -1
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
362
474
|
class InputIterator
|
363
475
|
include Enumerable
|
364
476
|
|
365
|
-
def initialize(input)
|
366
|
-
@
|
477
|
+
def initialize(input, context)
|
478
|
+
@context = context
|
479
|
+
@input = if input.is_a?(Array)
|
367
480
|
input.flatten
|
368
481
|
elsif input.is_a?(Hash)
|
369
482
|
[input]
|
@@ -375,7 +488,7 @@ module Liquid
|
|
375
488
|
end
|
376
489
|
|
377
490
|
def join(glue)
|
378
|
-
to_a.join(glue)
|
491
|
+
to_a.join(glue.to_s)
|
379
492
|
end
|
380
493
|
|
381
494
|
def concat(args)
|
@@ -401,6 +514,7 @@ module Liquid
|
|
401
514
|
|
402
515
|
def each
|
403
516
|
@input.each do |e|
|
517
|
+
e.context = @context if e.respond_to?(:context=)
|
404
518
|
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
405
519
|
end
|
406
520
|
end
|