activesupport 6.0.3 → 6.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +341 -455
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support.rb +13 -1
  5. data/lib/active_support/array_inquirer.rb +4 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache.rb +80 -39
  9. data/lib/active_support/cache/file_store.rb +4 -3
  10. data/lib/active_support/cache/mem_cache_store.rb +20 -23
  11. data/lib/active_support/cache/memory_store.rb +38 -26
  12. data/lib/active_support/cache/redis_cache_store.rb +31 -26
  13. data/lib/active_support/cache/strategy/local_cache.rb +14 -5
  14. data/lib/active_support/callbacks.rb +65 -56
  15. data/lib/active_support/concern.rb +46 -2
  16. data/lib/active_support/configurable.rb +3 -3
  17. data/lib/active_support/configuration_file.rb +46 -0
  18. data/lib/active_support/core_ext/benchmark.rb +2 -2
  19. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  20. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  21. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  22. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  23. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  24. data/lib/active_support/core_ext/enumerable.rb +76 -4
  25. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  26. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  27. data/lib/active_support/core_ext/hash/except.rb +1 -1
  28. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  29. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  30. data/lib/active_support/core_ext/load_error.rb +1 -1
  31. data/lib/active_support/core_ext/marshal.rb +2 -0
  32. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  33. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  34. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  35. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  36. data/lib/active_support/core_ext/module/delegation.rb +38 -28
  37. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  38. data/lib/active_support/core_ext/name_error.rb +29 -2
  39. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  40. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  41. data/lib/active_support/core_ext/object/json.rb +6 -2
  42. data/lib/active_support/core_ext/object/try.rb +2 -2
  43. data/lib/active_support/core_ext/range/compare_range.rb +9 -3
  44. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  45. data/lib/active_support/core_ext/string/access.rb +5 -24
  46. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  47. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  48. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  49. data/lib/active_support/core_ext/string/output_safety.rb +2 -3
  50. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  51. data/lib/active_support/core_ext/symbol.rb +3 -0
  52. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  53. data/lib/active_support/core_ext/time/calculations.rb +19 -1
  54. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  55. data/lib/active_support/core_ext/uri.rb +5 -1
  56. data/lib/active_support/current_attributes.rb +7 -2
  57. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  58. data/lib/active_support/dependencies.rb +37 -18
  59. data/lib/active_support/deprecation.rb +6 -1
  60. data/lib/active_support/deprecation/behaviors.rb +15 -2
  61. data/lib/active_support/deprecation/disallowed.rb +56 -0
  62. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  63. data/lib/active_support/deprecation/method_wrappers.rb +3 -2
  64. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  65. data/lib/active_support/deprecation/reporting.rb +50 -7
  66. data/lib/active_support/descendants_tracker.rb +6 -2
  67. data/lib/active_support/duration.rb +71 -22
  68. data/lib/active_support/duration/iso8601_serializer.rb +15 -9
  69. data/lib/active_support/encrypted_file.rb +19 -2
  70. data/lib/active_support/environment_inquirer.rb +20 -0
  71. data/lib/active_support/evented_file_update_checker.rb +69 -133
  72. data/lib/active_support/fork_tracker.rb +58 -0
  73. data/lib/active_support/gem_version.rb +3 -3
  74. data/lib/active_support/hash_with_indifferent_access.rb +35 -22
  75. data/lib/active_support/i18n_railtie.rb +14 -19
  76. data/lib/active_support/inflector/inflections.rb +1 -2
  77. data/lib/active_support/inflector/methods.rb +35 -31
  78. data/lib/active_support/inflector/transliterate.rb +4 -4
  79. data/lib/active_support/json/decoding.rb +4 -4
  80. data/lib/active_support/json/encoding.rb +5 -1
  81. data/lib/active_support/key_generator.rb +1 -1
  82. data/lib/active_support/locale/en.yml +7 -3
  83. data/lib/active_support/log_subscriber.rb +8 -0
  84. data/lib/active_support/logger.rb +1 -1
  85. data/lib/active_support/logger_silence.rb +2 -26
  86. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  87. data/lib/active_support/message_encryptor.rb +4 -7
  88. data/lib/active_support/message_verifier.rb +5 -5
  89. data/lib/active_support/messages/metadata.rb +9 -1
  90. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  91. data/lib/active_support/messages/rotator.rb +6 -5
  92. data/lib/active_support/multibyte/chars.rb +4 -42
  93. data/lib/active_support/multibyte/unicode.rb +9 -83
  94. data/lib/active_support/notifications.rb +31 -4
  95. data/lib/active_support/notifications/fanout.rb +23 -8
  96. data/lib/active_support/notifications/instrumenter.rb +6 -15
  97. data/lib/active_support/number_helper.rb +29 -14
  98. data/lib/active_support/number_helper/number_converter.rb +1 -1
  99. data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -7
  100. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  101. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  102. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
  103. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  104. data/lib/active_support/option_merger.rb +3 -2
  105. data/lib/active_support/ordered_options.rb +8 -2
  106. data/lib/active_support/parameter_filter.rb +15 -10
  107. data/lib/active_support/per_thread_registry.rb +1 -1
  108. data/lib/active_support/rails.rb +1 -4
  109. data/lib/active_support/railtie.rb +23 -1
  110. data/lib/active_support/secure_compare_rotator.rb +51 -0
  111. data/lib/active_support/security_utils.rb +19 -12
  112. data/lib/active_support/string_inquirer.rb +4 -2
  113. data/lib/active_support/subscriber.rb +12 -7
  114. data/lib/active_support/tagged_logging.rb +29 -4
  115. data/lib/active_support/testing/assertions.rb +18 -11
  116. data/lib/active_support/testing/parallelization.rb +12 -95
  117. data/lib/active_support/testing/parallelization/server.rb +78 -0
  118. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  119. data/lib/active_support/testing/time_helpers.rb +40 -3
  120. data/lib/active_support/time_with_zone.rb +66 -42
  121. data/lib/active_support/values/time_zone.rb +20 -10
  122. data/lib/active_support/xml_mini/rexml.rb +8 -1
  123. metadata +36 -38
  124. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  125. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  126. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  127. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  128. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  129. data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -18,11 +18,27 @@ module ActiveSupport
18
18
  #
19
19
  # MemoryStore is thread-safe.
20
20
  class MemoryStore < Store
21
+ module DupCoder # :nodoc:
22
+ class << self
23
+ def load(entry)
24
+ entry = entry.dup
25
+ entry.dup_value!
26
+ entry
27
+ end
28
+
29
+ def dump(entry)
30
+ entry.dup_value!
31
+ entry
32
+ end
33
+ end
34
+ end
35
+
36
+ DEFAULT_CODER = DupCoder
37
+
21
38
  def initialize(options = nil)
22
39
  options ||= {}
23
40
  super(options)
24
41
  @data = {}
25
- @key_access = {}
26
42
  @max_size = options[:size] || 32.megabytes
27
43
  @max_prune_time = options[:max_prune_time] || 2
28
44
  @cache_size = 0
@@ -39,7 +55,6 @@ module ActiveSupport
39
55
  def clear(options = nil)
40
56
  synchronize do
41
57
  @data.clear
42
- @key_access.clear
43
58
  @cache_size = 0
44
59
  end
45
60
  end
@@ -65,7 +80,7 @@ module ActiveSupport
65
80
  start_time = Concurrent.monotonic_time
66
81
  cleanup
67
82
  instrument(:prune, target_size, from: @cache_size) do
68
- keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
83
+ keys = synchronize { @data.keys }
69
84
  keys.each do |key|
70
85
  delete_entry(key, **options)
71
86
  return if @cache_size <= target_size || (max_time && Concurrent.monotonic_time - start_time > max_time)
@@ -104,7 +119,7 @@ module ActiveSupport
104
119
  end
105
120
 
106
121
  def inspect # :nodoc:
107
- "<##{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
122
+ "#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
108
123
  end
109
124
 
110
125
  # Synchronize calls to the cache. This should be called wherever the underlying cache implementation
@@ -116,36 +131,34 @@ module ActiveSupport
116
131
  private
117
132
  PER_ENTRY_OVERHEAD = 240
118
133
 
119
- def cached_size(key, entry)
120
- key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
134
+ def cached_size(key, payload)
135
+ key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
121
136
  end
122
137
 
123
138
  def read_entry(key, **options)
124
- entry = @data[key]
139
+ entry = nil
125
140
  synchronize do
126
- if entry
127
- entry = entry.dup
128
- entry.dup_value!
129
- @key_access[key] = Time.now.to_f
130
- else
131
- @key_access.delete(key)
141
+ payload = @data.delete(key)
142
+ if payload
143
+ @data[key] = payload
144
+ entry = deserialize_entry(payload)
132
145
  end
133
146
  end
134
147
  entry
135
148
  end
136
149
 
137
150
  def write_entry(key, entry, **options)
138
- entry.dup_value!
151
+ payload = serialize_entry(entry)
139
152
  synchronize do
140
- old_entry = @data[key]
141
- return false if @data.key?(key) && options[:unless_exist]
142
- if old_entry
143
- @cache_size -= (old_entry.size - entry.size)
153
+ return false if options[:unless_exist] && @data.key?(key)
154
+
155
+ old_payload = @data[key]
156
+ if old_payload
157
+ @cache_size -= (old_payload.bytesize - payload.bytesize)
144
158
  else
145
- @cache_size += cached_size(key, entry)
159
+ @cache_size += cached_size(key, payload)
146
160
  end
147
- @key_access[key] = Time.now.to_f
148
- @data[key] = entry
161
+ @data[key] = payload
149
162
  prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
150
163
  true
151
164
  end
@@ -153,16 +166,15 @@ module ActiveSupport
153
166
 
154
167
  def delete_entry(key, **options)
155
168
  synchronize do
156
- @key_access.delete(key)
157
- entry = @data.delete(key)
158
- @cache_size -= cached_size(key, entry) if entry
159
- !!entry
169
+ payload = @data.delete(key)
170
+ @cache_size -= cached_size(key, payload) if payload
171
+ !!payload
160
172
  end
161
173
  end
162
174
 
163
175
  def modify_value(name, amount, options)
176
+ options = merged_options(options)
164
177
  synchronize do
165
- options = merged_options(options)
166
178
  if num = read(name, options)
167
179
  num = num.to_i + amount
168
180
  write(name, num, options)
@@ -74,14 +74,6 @@ module ActiveSupport
74
74
  # Support raw values in the local cache strategy.
75
75
  module LocalCacheWithRaw # :nodoc:
76
76
  private
77
- def read_entry(key, **options)
78
- entry = super
79
- if options[:raw] && local_cache && entry
80
- entry = deserialize_entry(entry.value)
81
- end
82
- entry
83
- end
84
-
85
77
  def write_entry(key, entry, **options)
86
78
  if options[:raw] && local_cache
87
79
  raw_entry = Entry.new(serialize_entry(entry, raw: true))
@@ -177,7 +169,7 @@ module ActiveSupport
177
169
  # Race condition TTL is not set by default. This can be used to avoid
178
170
  # "thundering herd" cache writes when hot cache entries are expired.
179
171
  # See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
180
- def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
172
+ def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, coder: DEFAULT_CODER, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
181
173
  @redis_options = redis_options
182
174
 
183
175
  @max_key_bytesize = MAX_KEY_BYTESIZE
@@ -185,7 +177,8 @@ module ActiveSupport
185
177
 
186
178
  super namespace: namespace,
187
179
  compress: compress, compress_threshold: compress_threshold,
188
- expires_in: expires_in, race_condition_ttl: race_condition_ttl
180
+ expires_in: expires_in, race_condition_ttl: race_condition_ttl,
181
+ coder: coder
189
182
  end
190
183
 
191
184
  def redis
@@ -203,7 +196,7 @@ module ActiveSupport
203
196
 
204
197
  def inspect
205
198
  instance = @redis || @redis_options
206
- "<##{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
199
+ "#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
207
200
  end
208
201
 
209
202
  # Cache Store API implementation.
@@ -246,10 +239,14 @@ module ActiveSupport
246
239
  pattern = namespace_key(matcher, options)
247
240
  cursor = "0"
248
241
  # Fetch keys in batches using SCAN to avoid blocking the Redis server.
249
- begin
250
- cursor, keys = c.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
251
- c.del(*keys) unless keys.empty?
252
- end until cursor == "0"
242
+ nodes = c.respond_to?(:nodes) ? c.nodes : [c]
243
+
244
+ nodes.each do |node|
245
+ begin
246
+ cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
247
+ node.del(*keys) unless keys.empty?
248
+ end until cursor == "0"
249
+ end
253
250
  end
254
251
  end
255
252
  end
@@ -348,13 +345,14 @@ module ActiveSupport
348
345
  # Read an entry from the cache.
349
346
  def read_entry(key, **options)
350
347
  failsafe :read_entry do
351
- deserialize_entry redis.with { |c| c.get(key) }
348
+ raw = options&.fetch(:raw, false)
349
+ deserialize_entry(redis.with { |c| c.get(key) }, raw: raw)
352
350
  end
353
351
  end
354
352
 
355
353
  def read_multi_entries(names, **options)
356
354
  if mget_capable?
357
- read_multi_mget(*names)
355
+ read_multi_mget(*names, **options)
358
356
  else
359
357
  super
360
358
  end
@@ -364,6 +362,7 @@ module ActiveSupport
364
362
  options = names.extract_options!
365
363
  options = merged_options(options)
366
364
  return {} if names == []
365
+ raw = options&.fetch(:raw, false)
367
366
 
368
367
  keys = names.map { |name| normalize_key(name, options) }
369
368
 
@@ -373,7 +372,7 @@ module ActiveSupport
373
372
 
374
373
  names.zip(values).each_with_object({}) do |(name, value), results|
375
374
  if value
376
- entry = deserialize_entry(value)
375
+ entry = deserialize_entry(value, raw: raw)
377
376
  unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
378
377
  results[name] = entry.value
379
378
  end
@@ -400,7 +399,7 @@ module ActiveSupport
400
399
  modifiers[:nx] = unless_exist
401
400
  modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
402
401
 
403
- redis.with { |c| c.set key, serialized_entry, modifiers }
402
+ redis.with { |c| c.set key, serialized_entry, **modifiers }
404
403
  else
405
404
  redis.with { |c| c.set key, serialized_entry }
406
405
  end
@@ -420,6 +419,11 @@ module ActiveSupport
420
419
  end
421
420
  end
422
421
 
422
+ # Deletes multiple entries in the cache. Returns the number of entries deleted.
423
+ def delete_multi_entries(entries, **_options)
424
+ redis.with { |c| c.del(entries) }
425
+ end
426
+
423
427
  # Nonstandard store provider API to write multiple values at once.
424
428
  def write_multi_entries(entries, expires_in: nil, **options)
425
429
  if entries.any?
@@ -435,11 +439,11 @@ module ActiveSupport
435
439
 
436
440
  # Truncate keys that exceed 1kB.
437
441
  def normalize_key(key, options)
438
- truncate_key super.b
442
+ truncate_key super&.b
439
443
  end
440
444
 
441
445
  def truncate_key(key)
442
- if key.bytesize > max_key_bytesize
446
+ if key && key.bytesize > max_key_bytesize
443
447
  suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
444
448
  truncate_at = max_key_bytesize - suffix.bytesize
445
449
  "#{key.byteslice(0, truncate_at)}#{suffix}"
@@ -448,10 +452,11 @@ module ActiveSupport
448
452
  end
449
453
  end
450
454
 
451
- def deserialize_entry(serialized_entry)
452
- if serialized_entry
453
- entry = Marshal.load(serialized_entry) rescue serialized_entry
454
- entry.is_a?(Entry) ? entry : Entry.new(entry)
455
+ def deserialize_entry(payload, raw:)
456
+ if payload && raw
457
+ Entry.new(payload, compress: false)
458
+ else
459
+ super(payload)
455
460
  end
456
461
  end
457
462
 
@@ -459,7 +464,7 @@ module ActiveSupport
459
464
  if raw
460
465
  entry.value.to_s
461
466
  else
462
- Marshal.dump(entry)
467
+ super(entry)
463
468
  end
464
469
  end
465
470
 
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/object/duplicable"
4
3
  require "active_support/core_ext/string/inflections"
5
4
  require "active_support/per_thread_registry"
6
5
 
@@ -65,8 +64,9 @@ module ActiveSupport
65
64
  values
66
65
  end
67
66
 
68
- def write_entry(key, value, **options)
69
- @data[key] = value
67
+ def write_entry(key, entry, **options)
68
+ entry.dup_value!
69
+ @data[key] = entry
70
70
  true
71
71
  end
72
72
 
@@ -75,7 +75,10 @@ module ActiveSupport
75
75
  end
76
76
 
77
77
  def fetch_entry(key, options = nil) # :nodoc:
78
- @data.fetch(key) { @data[key] = yield }
78
+ entry = @data.fetch(key) { @data[key] = yield }
79
+ dup_entry = entry.dup
80
+ dup_entry&.dup_value!
81
+ dup_entry
79
82
  end
80
83
  end
81
84
 
@@ -104,6 +107,12 @@ module ActiveSupport
104
107
  super
105
108
  end
106
109
 
110
+ def delete_matched(matcher, options = nil) # :nodoc:
111
+ return super unless cache = local_cache
112
+ cache.clear
113
+ super
114
+ end
115
+
107
116
  def increment(name, amount = 1, **options) # :nodoc:
108
117
  return super unless local_cache
109
118
  value = bypass_local_cache { super }
@@ -160,7 +169,7 @@ module ActiveSupport
160
169
  cache = local_cache
161
170
  cache.mute do
162
171
  if value
163
- cache.write(name, value, **options)
172
+ cache.write(name, value, options)
164
173
  else
165
174
  cache.delete(name, **options)
166
175
  end
@@ -4,10 +4,7 @@ require "active_support/concern"
4
4
  require "active_support/descendants_tracker"
5
5
  require "active_support/core_ext/array/extract_options"
6
6
  require "active_support/core_ext/class/attribute"
7
- require "active_support/core_ext/kernel/reporting"
8
- require "active_support/core_ext/kernel/singleton_class"
9
7
  require "active_support/core_ext/string/filters"
10
- require "active_support/deprecation"
11
8
  require "thread"
12
9
 
13
10
  module ActiveSupport
@@ -103,32 +100,6 @@ module ActiveSupport
103
100
  env = Filters::Environment.new(self, false, nil)
104
101
  next_sequence = callbacks.compile
105
102
 
106
- invoke_sequence = Proc.new do
107
- skipped = nil
108
- while true
109
- current = next_sequence
110
- current.invoke_before(env)
111
- if current.final?
112
- env.value = !env.halted && (!block_given? || yield)
113
- elsif current.skip?(env)
114
- (skipped ||= []) << current
115
- next_sequence = next_sequence.nested
116
- next
117
- else
118
- next_sequence = next_sequence.nested
119
- begin
120
- target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
121
- target.send(method, *arguments, &block)
122
- ensure
123
- next_sequence = current
124
- end
125
- end
126
- current.invoke_after(env)
127
- skipped.pop.invoke_after(env) while skipped && skipped.first
128
- break env.value
129
- end
130
- end
131
-
132
103
  # Common case: no 'around' callbacks defined
133
104
  if next_sequence.final?
134
105
  next_sequence.invoke_before(env)
@@ -136,6 +107,33 @@ module ActiveSupport
136
107
  next_sequence.invoke_after(env)
137
108
  env.value
138
109
  else
110
+ invoke_sequence = Proc.new do
111
+ skipped = nil
112
+
113
+ while true
114
+ current = next_sequence
115
+ current.invoke_before(env)
116
+ if current.final?
117
+ env.value = !env.halted && (!block_given? || yield)
118
+ elsif current.skip?(env)
119
+ (skipped ||= []) << current
120
+ next_sequence = next_sequence.nested
121
+ next
122
+ else
123
+ next_sequence = next_sequence.nested
124
+ begin
125
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
126
+ target.send(method, *arguments, &block)
127
+ ensure
128
+ next_sequence = current
129
+ end
130
+ end
131
+ current.invoke_after(env)
132
+ skipped.pop.invoke_after(env) while skipped&.first
133
+ break env.value
134
+ end
135
+ end
136
+
139
137
  invoke_sequence.call
140
138
  end
141
139
  end
@@ -145,7 +143,7 @@ module ActiveSupport
145
143
  # A hook invoked every time a before callback is halted.
146
144
  # This can be overridden in ActiveSupport::Callbacks implementors in order
147
145
  # to provide better debugging/logging.
148
- def halted_callback_hook(filter)
146
+ def halted_callback_hook(filter, name)
149
147
  end
150
148
 
151
149
  module Conditionals # :nodoc:
@@ -161,17 +159,17 @@ module ActiveSupport
161
159
  Environment = Struct.new(:target, :halted, :value)
162
160
 
163
161
  class Before
164
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
162
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
165
163
  halted_lambda = chain_config[:terminator]
166
164
 
167
165
  if user_conditions.any?
168
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
166
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
169
167
  else
170
- halting(callback_sequence, user_callback, halted_lambda, filter)
168
+ halting(callback_sequence, user_callback, halted_lambda, filter, name)
171
169
  end
172
170
  end
173
171
 
174
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
172
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
175
173
  callback_sequence.before do |env|
176
174
  target = env.target
177
175
  value = env.value
@@ -181,7 +179,7 @@ module ActiveSupport
181
179
  result_lambda = -> { user_callback.call target, value }
182
180
  env.halted = halted_lambda.call(target, result_lambda)
183
181
  if env.halted
184
- target.send :halted_callback_hook, filter
182
+ target.send :halted_callback_hook, filter, name
185
183
  end
186
184
  end
187
185
 
@@ -190,7 +188,7 @@ module ActiveSupport
190
188
  end
191
189
  private_class_method :halting_and_conditional
192
190
 
193
- def self.halting(callback_sequence, user_callback, halted_lambda, filter)
191
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
194
192
  callback_sequence.before do |env|
195
193
  target = env.target
196
194
  value = env.value
@@ -199,9 +197,8 @@ module ActiveSupport
199
197
  unless halted
200
198
  result_lambda = -> { user_callback.call target, value }
201
199
  env.halted = halted_lambda.call(target, result_lambda)
202
-
203
200
  if env.halted
204
- target.send :halted_callback_hook, filter
201
+ target.send :halted_callback_hook, filter, name
205
202
  end
206
203
  end
207
204
 
@@ -300,8 +297,8 @@ module ActiveSupport
300
297
  @kind = kind
301
298
  @filter = filter
302
299
  @key = compute_identifier filter
303
- @if = check_conditionals(Array(options[:if]))
304
- @unless = check_conditionals(Array(options[:unless]))
300
+ @if = check_conditionals(options[:if])
301
+ @unless = check_conditionals(options[:unless])
305
302
  end
306
303
 
307
304
  def filter; @key; end
@@ -339,7 +336,7 @@ module ActiveSupport
339
336
 
340
337
  case kind
341
338
  when :before
342
- Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
339
+ Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
343
340
  when :after
344
341
  Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
345
342
  when :around
@@ -352,7 +349,13 @@ module ActiveSupport
352
349
  end
353
350
 
354
351
  private
352
+ EMPTY_ARRAY = [].freeze
353
+ private_constant :EMPTY_ARRAY
354
+
355
355
  def check_conditionals(conditionals)
356
+ return EMPTY_ARRAY if conditionals.blank?
357
+
358
+ conditionals = Array(conditionals)
356
359
  if conditionals.any? { |c| c.is_a?(String) }
357
360
  raise ArgumentError, <<-MSG.squish
358
361
  Passing string to be evaluated in :if and :unless conditional
@@ -361,7 +364,7 @@ module ActiveSupport
361
364
  MSG
362
365
  end
363
366
 
364
- conditionals
367
+ conditionals.freeze
365
368
  end
366
369
 
367
370
  def compute_identifier(filter)
@@ -403,21 +406,17 @@ module ActiveSupport
403
406
  # The actual invocation is left up to the caller to minimize
404
407
  # call stack pollution.
405
408
  def expand(target, value, block)
406
- result = @arguments.map { |arg|
409
+ expanded = [@override_target || target, @override_block || block, @method_name]
410
+
411
+ @arguments.each do |arg|
407
412
  case arg
408
- when :value; value
409
- when :target; target
410
- when :block; block || raise(ArgumentError)
413
+ when :value then expanded << value
414
+ when :target then expanded << target
415
+ when :block then expanded << (block || raise(ArgumentError))
411
416
  end
412
- }
413
-
414
- result.unshift @method_name
415
- result.unshift @override_block || block
416
- result.unshift @override_target || target
417
+ end
417
418
 
418
- # target, block, method, *arguments = result
419
- # target.send(method, *arguments, &block)
420
- result
419
+ expanded
421
420
  end
422
421
 
423
422
  # Return a lambda that will make this call when given the input
@@ -845,8 +844,18 @@ module ActiveSupport
845
844
  __callbacks[name.to_sym]
846
845
  end
847
846
 
848
- def set_callbacks(name, callbacks) # :nodoc:
849
- self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
847
+ if Module.instance_method(:method_defined?).arity == 1 # Ruby 2.5 and older
848
+ def set_callbacks(name, callbacks) # :nodoc:
849
+ self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
850
+ end
851
+ else # Ruby 2.6 and newer
852
+ def set_callbacks(name, callbacks) # :nodoc:
853
+ unless singleton_class.method_defined?(:__callbacks, false)
854
+ self.__callbacks = __callbacks.dup
855
+ end
856
+ self.__callbacks[name.to_sym] = callbacks
857
+ self.__callbacks
858
+ end
850
859
  end
851
860
  end
852
861
  end