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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3d29364ce9b0e428bfcf9e63df9695f791d8e13e
4
- data.tar.gz: 08cc19791695840bd1d6230ea9643cfa3ee9147c
3
+ metadata.gz: 68c0e92a0c24e19463a4ef68801093b2be0211bb
4
+ data.tar.gz: d80ceddfb328477c4fd8ed52faa893088bfd86ef
5
5
  SHA512:
6
- metadata.gz: 12c6c427dca1570ce4eee3634d3d6fbfd2f35d231e32678f3d43f19583233a3af8fa2049e04edaf69b41d8df553b75da72a5f5431bfbe2a6d0f1f906b5a97add
7
- data.tar.gz: 318e400a0ea7e6fedd5d066eda0995b438846d925167a2159885f9806747af683859d4fb5d2ca1853efc9c2bbebc9d3528a2a9013d04bc3c28eb223e56fee1d0
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]
@@ -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
 
@@ -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 :exception_handler, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
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.exception_handler = ->(e) { raise }
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
- if e.is_a?(Liquid::Error)
78
- e.template_name ||= template_name
79
- e.line_number ||= line_number
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
- output || Liquid::Error.render(e)
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|
@@ -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
- l = length - truncate_string.length
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] + truncate_string : input_str
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 { |a, b| a[property] <=> b[property] }
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
 
@@ -39,6 +39,7 @@ module Liquid
39
39
  end
40
40
 
41
41
  def self.global_filter(filter)
42
+ @@strainer_class_cache.clear
42
43
  @@global_strainer.add_filter(filter)
43
44
  end
44
45
 
@@ -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.exception_handler = ->(e) { raise }
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.exception_handler = options[:exception_handler] if options[:exception_handler]
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
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Liquid
3
- VERSION = "4.0.0.rc3"
3
+ VERSION = "4.0.0"
4
4
  end
@@ -202,22 +202,40 @@ class ErrorHandlingTest < Minitest::Test
202
202
  end
203
203
  end
204
204
 
205
- def test_exception_handler_with_string_result
206
- template = Liquid::Template.parse('This is an argument error: {{ errors.argument_error }}')
207
- output = template.render({ 'errors' => ErrorDrop.new }, exception_handler: ->(e) { '' })
208
- assert_equal 'This is an argument error: ', output
209
- assert_equal [ArgumentError], template.errors.map(&:class)
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
- class InternalError < Liquid::Error
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 test_exception_handler_with_exception_result
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
- handler = ->(e) { e.is_a?(Liquid::Error) ? e : InternalError.new('internal') }
218
- output = template.render({ 'errors' => ErrorDrop.new }, exception_handler: handler)
219
- assert_equal 'This is a runtime error: Liquid error (line 1): internal', output
220
- assert_equal [InternalError], template.errors.map(&:class)
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&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...', @filters.truncatewords('Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; 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 test_exception_handler_doesnt_reraise_if_it_returns_false
218
+ def test_exception_renderer_that_returns_string
219
219
  exception = nil
220
- Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_handler: ->(e) { exception = e; false })
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 test_exception_handler_does_reraise_if_it_returns_true
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({}, exception_handler: ->(e) { exception = e; true })
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.rc3
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-09-13 00:00:00.000000000 Z
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: '0'
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: '0'
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: '0'
160
+ version: 2.1.0
161
161
  required_rubygems_version: !ruby/object:Gem::Requirement
162
162
  requirements:
163
163
  - - ">="