liquid 4.0.0.rc3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +2 -0
- data/lib/liquid/block_body.rb +2 -2
- data/lib/liquid/context.rb +15 -26
- data/lib/liquid/errors.rb +1 -8
- data/lib/liquid/standardfilters.rb +14 -5
- data/lib/liquid/strainer.rb +1 -0
- data/lib/liquid/template.rb +7 -2
- data/lib/liquid/version.rb +1 -1
- data/test/integration/error_handling_test.rb +29 -11
- data/test/integration/standard_filter_test.rb +21 -2
- data/test/integration/template_test.rb +8 -4
- data/test/unit/strainer_unit_test.rb +12 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68c0e92a0c24e19463a4ef68801093b2be0211bb
|
4
|
+
data.tar.gz: d80ceddfb328477c4fd8ed52faa893088bfd86ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2232e4b2053dbcbad922fa89b53c79366aad0ad2f649a9d8e0dec5d6100c572c88bc8ec3a941df8063138cb02ffb9f68269dac3581d780c695b2c96e940cb366
|
7
|
+
data.tar.gz: 3695492ac392acc500f6ee10758dc5d15991289f0ca5fd16e0c770056cf0a4db928b2d7d29af1f7bb9c1eb4951c7e42854ab48409d373d6780ddd7ce2c27f357
|
data/History.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
## 4.0.0 / not yet released / branch "master"
|
4
4
|
|
5
5
|
### Changed
|
6
|
+
* Render an opaque internal error by default for non-Liquid::Error (#835) [Dylan Thacker-Smith]
|
7
|
+
* Ruby 2.0 support dropped (#832) [Dylan Thacker-Smith]
|
6
8
|
* Add to_number Drop method to allow custom drops to work with number filters (#731)
|
7
9
|
* Add strict_variables and strict_filters options to detect undefined references (#691)
|
8
10
|
* Improve loop performance (#681) [Florian Weingarten]
|
data/lib/liquid/block_body.rb
CHANGED
@@ -93,10 +93,10 @@ module Liquid
|
|
93
93
|
rescue MemoryError => e
|
94
94
|
raise e
|
95
95
|
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
96
|
-
context.handle_error(e, token.line_number)
|
96
|
+
context.handle_error(e, token.line_number, token.raw)
|
97
97
|
output << nil
|
98
98
|
rescue ::StandardError => e
|
99
|
-
output << context.handle_error(e, token.line_number)
|
99
|
+
output << context.handle_error(e, token.line_number, token.raw)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
data/lib/liquid/context.rb
CHANGED
@@ -13,7 +13,7 @@ module Liquid
|
|
13
13
|
# context['bob'] #=> nil class Context
|
14
14
|
class Context
|
15
15
|
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
|
16
|
-
attr_accessor :
|
16
|
+
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
17
17
|
|
18
18
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
|
19
19
|
@environments = [environments].flatten
|
@@ -27,8 +27,9 @@ module Liquid
|
|
27
27
|
|
28
28
|
@this_stack_used = false
|
29
29
|
|
30
|
+
self.exception_renderer = Template.default_exception_renderer
|
30
31
|
if rethrow_errors
|
31
|
-
self.
|
32
|
+
self.exception_renderer = ->(e) { raise }
|
32
33
|
end
|
33
34
|
|
34
35
|
@interrupts = []
|
@@ -73,31 +74,12 @@ module Liquid
|
|
73
74
|
@interrupts.pop
|
74
75
|
end
|
75
76
|
|
76
|
-
def handle_error(e, line_number = nil)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
output = nil
|
83
|
-
|
84
|
-
if exception_handler
|
85
|
-
result = exception_handler.call(e)
|
86
|
-
case result
|
87
|
-
when Exception
|
88
|
-
e = result
|
89
|
-
if e.is_a?(Liquid::Error)
|
90
|
-
e.template_name ||= template_name
|
91
|
-
e.line_number ||= line_number
|
92
|
-
end
|
93
|
-
when String
|
94
|
-
output = result
|
95
|
-
else
|
96
|
-
raise if result
|
97
|
-
end
|
98
|
-
end
|
77
|
+
def handle_error(e, line_number = nil, raw_token = nil)
|
78
|
+
e = internal_error unless e.is_a?(Liquid::Error)
|
79
|
+
e.template_name ||= template_name
|
80
|
+
e.line_number ||= line_number
|
99
81
|
errors.push(e)
|
100
|
-
|
82
|
+
exception_renderer.call(e).to_s
|
101
83
|
end
|
102
84
|
|
103
85
|
def invoke(method, *args)
|
@@ -221,6 +203,13 @@ module Liquid
|
|
221
203
|
|
222
204
|
private
|
223
205
|
|
206
|
+
def internal_error
|
207
|
+
# raise and catch to set backtrace and cause on exception
|
208
|
+
raise Liquid::InternalError, 'internal'
|
209
|
+
rescue Liquid::InternalError => exc
|
210
|
+
exc
|
211
|
+
end
|
212
|
+
|
224
213
|
def squash_instance_assigns_with_environments
|
225
214
|
@scopes.last.each_key do |k|
|
226
215
|
@environments.each do |env|
|
data/lib/liquid/errors.rb
CHANGED
@@ -17,14 +17,6 @@ module Liquid
|
|
17
17
|
str
|
18
18
|
end
|
19
19
|
|
20
|
-
def self.render(e)
|
21
|
-
if e.is_a?(Liquid::Error)
|
22
|
-
e.to_s
|
23
|
-
else
|
24
|
-
"Liquid error: #{e}"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
20
|
private
|
29
21
|
|
30
22
|
def message_prefix
|
@@ -60,4 +52,5 @@ module Liquid
|
|
60
52
|
UndefinedDropMethod = Class.new(Error)
|
61
53
|
UndefinedFilter = Class.new(Error)
|
62
54
|
MethodOverrideError = Class.new(Error)
|
55
|
+
InternalError = Class.new(Error)
|
63
56
|
end
|
@@ -65,9 +65,10 @@ module Liquid
|
|
65
65
|
return if input.nil?
|
66
66
|
input_str = input.to_s
|
67
67
|
length = Utils.to_integer(length)
|
68
|
-
|
68
|
+
truncate_string_str = truncate_string.to_s
|
69
|
+
l = length - truncate_string_str.length
|
69
70
|
l = 0 if l < 0
|
70
|
-
input_str.length > length ? input_str[0...l] +
|
71
|
+
input_str.length > length ? input_str[0...l] + truncate_string_str : input_str
|
71
72
|
end
|
72
73
|
|
73
74
|
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
@@ -76,7 +77,7 @@ module Liquid
|
|
76
77
|
words = Utils.to_integer(words)
|
77
78
|
l = words - 1
|
78
79
|
l = 0 if l < 0
|
79
|
-
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string : input
|
80
|
+
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string.to_s : input
|
80
81
|
end
|
81
82
|
|
82
83
|
# Split input string into an array of substrings separated by given pattern.
|
@@ -85,7 +86,7 @@ module Liquid
|
|
85
86
|
# <div class="summary">{{ post | split '//' | first }}</div>
|
86
87
|
#
|
87
88
|
def split(input, pattern)
|
88
|
-
input.to_s.split(pattern)
|
89
|
+
input.to_s.split(pattern.to_s)
|
89
90
|
end
|
90
91
|
|
91
92
|
def strip(input)
|
@@ -124,7 +125,15 @@ module Liquid
|
|
124
125
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
125
126
|
[]
|
126
127
|
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
127
|
-
ary.sort
|
128
|
+
ary.sort do |a, b|
|
129
|
+
a = a[property]
|
130
|
+
b = b[property]
|
131
|
+
if a && b
|
132
|
+
a <=> b
|
133
|
+
else
|
134
|
+
a ? -1 : 1
|
135
|
+
end
|
136
|
+
end
|
128
137
|
end
|
129
138
|
end
|
130
139
|
|
data/lib/liquid/strainer.rb
CHANGED
data/lib/liquid/template.rb
CHANGED
@@ -69,6 +69,11 @@ module Liquid
|
|
69
69
|
# :error raises an error when tainted output is used
|
70
70
|
attr_writer :taint_mode
|
71
71
|
|
72
|
+
attr_accessor :default_exception_renderer
|
73
|
+
Template.default_exception_renderer = lambda do |exception|
|
74
|
+
exception
|
75
|
+
end
|
76
|
+
|
72
77
|
def file_system
|
73
78
|
@@file_system
|
74
79
|
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
|
@@ -241,7 +246,7 @@ module Liquid
|
|
241
246
|
def apply_options_to_context(context, options)
|
242
247
|
context.add_filters(options[:filters]) if options[:filters]
|
243
248
|
context.global_filter = options[:global_filter] if options[:global_filter]
|
244
|
-
context.
|
249
|
+
context.exception_renderer = options[:exception_renderer] if options[:exception_renderer]
|
245
250
|
context.strict_variables = options[:strict_variables] if options[:strict_variables]
|
246
251
|
context.strict_filters = options[:strict_filters] if options[:strict_filters]
|
247
252
|
end
|
data/lib/liquid/version.rb
CHANGED
@@ -202,22 +202,40 @@ class ErrorHandlingTest < Minitest::Test
|
|
202
202
|
end
|
203
203
|
end
|
204
204
|
|
205
|
-
def
|
206
|
-
template = Liquid::Template.parse('This is
|
207
|
-
|
208
|
-
|
209
|
-
|
205
|
+
def test_default_exception_renderer_with_internal_error
|
206
|
+
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
207
|
+
|
208
|
+
output = template.render({ 'errors' => ErrorDrop.new })
|
209
|
+
|
210
|
+
assert_equal 'This is a runtime error: Liquid error (line 1): internal', output
|
211
|
+
assert_equal [Liquid::InternalError], template.errors.map(&:class)
|
210
212
|
end
|
211
213
|
|
212
|
-
|
214
|
+
def test_setting_default_exception_renderer
|
215
|
+
old_exception_renderer = Liquid::Template.default_exception_renderer
|
216
|
+
exceptions = []
|
217
|
+
Liquid::Template.default_exception_renderer = ->(e) { exceptions << e; '' }
|
218
|
+
template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}')
|
219
|
+
|
220
|
+
output = template.render({ 'errors' => ErrorDrop.new })
|
221
|
+
|
222
|
+
assert_equal 'This is a runtime error: ', output
|
223
|
+
assert_equal [Liquid::ArgumentError], template.errors.map(&:class)
|
224
|
+
ensure
|
225
|
+
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
|
213
226
|
end
|
214
227
|
|
215
|
-
def
|
228
|
+
def test_exception_renderer_exposing_non_liquid_error
|
216
229
|
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
230
|
+
exceptions = []
|
231
|
+
handler = ->(e) { exceptions << e; e.cause }
|
232
|
+
|
233
|
+
output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
|
234
|
+
|
235
|
+
assert_equal 'This is a runtime error: runtime error', output
|
236
|
+
assert_equal [Liquid::InternalError], exceptions.map(&:class)
|
237
|
+
assert_equal exceptions, template.errors
|
238
|
+
assert_equal '#<RuntimeError: runtime error>', exceptions.first.cause.inspect
|
221
239
|
end
|
222
240
|
|
223
241
|
class TestFileSystem
|
@@ -115,15 +115,15 @@ class StandardFiltersTest < Minitest::Test
|
|
115
115
|
assert_equal '...', @filters.truncate('1234567890', 0)
|
116
116
|
assert_equal '1234567890', @filters.truncate('1234567890')
|
117
117
|
assert_equal "测试...", @filters.truncate("测试测试测试测试", 5)
|
118
|
+
assert_equal '12341', @filters.truncate("1234567890", 5, 1)
|
118
119
|
end
|
119
120
|
|
120
121
|
def test_split
|
121
122
|
assert_equal ['12', '34'], @filters.split('12~34', '~')
|
122
123
|
assert_equal ['A? ', ' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~')
|
123
124
|
assert_equal ['A?Z'], @filters.split('A?Z', '~')
|
124
|
-
# Regexp works although Liquid does not support.
|
125
|
-
assert_equal ['A', 'Z'], @filters.split('AxZ', /x/)
|
126
125
|
assert_equal [], @filters.split(nil, ' ')
|
126
|
+
assert_equal ['A', 'Z'], @filters.split('A1Z', 1)
|
127
127
|
end
|
128
128
|
|
129
129
|
def test_escape
|
@@ -154,6 +154,7 @@ class StandardFiltersTest < Minitest::Test
|
|
154
154
|
assert_equal 'one two three', @filters.truncatewords('one two three')
|
155
155
|
assert_equal 'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...', @filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15)
|
156
156
|
assert_equal "测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5)
|
157
|
+
assert_equal 'one two1', @filters.truncatewords("one two three", 2, 1)
|
157
158
|
end
|
158
159
|
|
159
160
|
def test_strip_html
|
@@ -176,6 +177,24 @@ class StandardFiltersTest < Minitest::Test
|
|
176
177
|
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
|
177
178
|
end
|
178
179
|
|
180
|
+
def test_sort_when_property_is_sometimes_missing_puts_nils_last
|
181
|
+
input = [
|
182
|
+
{ "price" => 4, "handle" => "alpha" },
|
183
|
+
{ "handle" => "beta" },
|
184
|
+
{ "price" => 1, "handle" => "gamma" },
|
185
|
+
{ "handle" => "delta" },
|
186
|
+
{ "price" => 2, "handle" => "epsilon" }
|
187
|
+
]
|
188
|
+
expectation = [
|
189
|
+
{ "price" => 1, "handle" => "gamma" },
|
190
|
+
{ "price" => 2, "handle" => "epsilon" },
|
191
|
+
{ "price" => 4, "handle" => "alpha" },
|
192
|
+
{ "handle" => "delta" },
|
193
|
+
{ "handle" => "beta" }
|
194
|
+
]
|
195
|
+
assert_equal expectation, @filters.sort(input, "price")
|
196
|
+
end
|
197
|
+
|
179
198
|
def test_sort_empty_array
|
180
199
|
assert_equal [], @filters.sort([], "a")
|
181
200
|
end
|
@@ -215,16 +215,20 @@ class TemplateTest < Minitest::Test
|
|
215
215
|
assert_equal 'ruby error in drop', e.message
|
216
216
|
end
|
217
217
|
|
218
|
-
def
|
218
|
+
def test_exception_renderer_that_returns_string
|
219
219
|
exception = nil
|
220
|
-
|
220
|
+
handler = ->(e) { exception = e; '<!-- error -->' }
|
221
|
+
|
222
|
+
output = Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: handler)
|
223
|
+
|
221
224
|
assert exception.is_a?(Liquid::ZeroDivisionError)
|
225
|
+
assert_equal '<!-- error -->', output
|
222
226
|
end
|
223
227
|
|
224
|
-
def
|
228
|
+
def test_exception_renderer_that_raises
|
225
229
|
exception = nil
|
226
230
|
assert_raises(Liquid::ZeroDivisionError) do
|
227
|
-
Template.parse("{{ 1 | divided_by: 0 }}").render({},
|
231
|
+
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: ->(e) { exception = e; raise })
|
228
232
|
end
|
229
233
|
assert exception.is_a?(Liquid::ZeroDivisionError)
|
230
234
|
end
|
@@ -133,4 +133,16 @@ class StrainerUnitTest < Minitest::Test
|
|
133
133
|
strainer.class.add_filter(PublicMethodOverrideFilter)
|
134
134
|
assert strainer.class.filter_methods.include?('public_filter')
|
135
135
|
end
|
136
|
+
|
137
|
+
module LateAddedFilter
|
138
|
+
def late_added_filter(input)
|
139
|
+
"filtered"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_global_filter_clears_cache
|
144
|
+
assert_equal 'input', Strainer.create(nil).invoke('late_added_filter', 'input')
|
145
|
+
Strainer.global_filter(LateAddedFilter)
|
146
|
+
assert_equal 'filtered', Strainer.create(nil).invoke('late_added_filter', 'input')
|
147
|
+
end
|
136
148
|
end # StrainerTest
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: liquid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.0
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Lütke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '11.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '11.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -157,7 +157,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
157
157
|
requirements:
|
158
158
|
- - ">="
|
159
159
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
160
|
+
version: 2.1.0
|
161
161
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
162
|
requirements:
|
163
163
|
- - ">="
|