liquid 4.0.0.rc3 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|