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 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
  - - ">="