activesupport 6.0.3.6 → 6.1.1

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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -475
  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 +85 -44
  9. data/lib/active_support/cache/file_store.rb +4 -3
  10. data/lib/active_support/cache/mem_cache_store.rb +21 -14
  11. data/lib/active_support/cache/memory_store.rb +46 -26
  12. data/lib/active_support/cache/redis_cache_store.rb +27 -27
  13. data/lib/active_support/cache/strategy/local_cache.rb +21 -6
  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.rb +1 -1
  19. data/lib/active_support/core_ext/benchmark.rb +2 -2
  20. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  21. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  22. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  23. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  24. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  25. data/lib/active_support/core_ext/enumerable.rb +76 -4
  26. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  27. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  28. data/lib/active_support/core_ext/hash/except.rb +1 -1
  29. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  30. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  31. data/lib/active_support/core_ext/load_error.rb +1 -1
  32. data/lib/active_support/core_ext/marshal.rb +2 -0
  33. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  34. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  35. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  36. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  37. data/lib/active_support/core_ext/module/delegation.rb +38 -28
  38. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  39. data/lib/active_support/core_ext/name_error.rb +29 -2
  40. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  41. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  42. data/lib/active_support/core_ext/object/json.rb +13 -2
  43. data/lib/active_support/core_ext/object/try.rb +2 -2
  44. data/lib/active_support/core_ext/range/compare_range.rb +9 -3
  45. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  46. data/lib/active_support/core_ext/regexp.rb +8 -1
  47. data/lib/active_support/core_ext/string/access.rb +5 -24
  48. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  49. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  50. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  51. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  52. data/lib/active_support/core_ext/string/output_safety.rb +10 -10
  53. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  54. data/lib/active_support/core_ext/symbol.rb +3 -0
  55. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  56. data/lib/active_support/core_ext/time/calculations.rb +19 -1
  57. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  58. data/lib/active_support/core_ext/uri.rb +5 -1
  59. data/lib/active_support/current_attributes.rb +7 -2
  60. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  61. data/lib/active_support/dependencies.rb +43 -19
  62. data/lib/active_support/deprecation.rb +6 -1
  63. data/lib/active_support/deprecation/behaviors.rb +15 -2
  64. data/lib/active_support/deprecation/disallowed.rb +56 -0
  65. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  66. data/lib/active_support/deprecation/method_wrappers.rb +3 -2
  67. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  68. data/lib/active_support/deprecation/reporting.rb +50 -7
  69. data/lib/active_support/descendants_tracker.rb +6 -2
  70. data/lib/active_support/duration.rb +71 -22
  71. data/lib/active_support/duration/iso8601_serializer.rb +15 -9
  72. data/lib/active_support/encrypted_file.rb +19 -2
  73. data/lib/active_support/environment_inquirer.rb +20 -0
  74. data/lib/active_support/evented_file_update_checker.rb +69 -133
  75. data/lib/active_support/fork_tracker.rb +62 -0
  76. data/lib/active_support/gem_version.rb +3 -3
  77. data/lib/active_support/hash_with_indifferent_access.rb +42 -23
  78. data/lib/active_support/i18n_railtie.rb +14 -19
  79. data/lib/active_support/inflector/inflections.rb +1 -2
  80. data/lib/active_support/inflector/methods.rb +35 -31
  81. data/lib/active_support/inflector/transliterate.rb +4 -4
  82. data/lib/active_support/json/decoding.rb +4 -4
  83. data/lib/active_support/json/encoding.rb +5 -1
  84. data/lib/active_support/key_generator.rb +1 -1
  85. data/lib/active_support/locale/en.yml +7 -3
  86. data/lib/active_support/log_subscriber.rb +8 -0
  87. data/lib/active_support/logger.rb +1 -1
  88. data/lib/active_support/logger_silence.rb +2 -26
  89. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  90. data/lib/active_support/message_encryptor.rb +4 -7
  91. data/lib/active_support/message_verifier.rb +5 -5
  92. data/lib/active_support/messages/metadata.rb +9 -1
  93. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  94. data/lib/active_support/messages/rotator.rb +6 -5
  95. data/lib/active_support/multibyte/chars.rb +4 -42
  96. data/lib/active_support/multibyte/unicode.rb +9 -83
  97. data/lib/active_support/notifications.rb +31 -4
  98. data/lib/active_support/notifications/fanout.rb +23 -8
  99. data/lib/active_support/notifications/instrumenter.rb +6 -15
  100. data/lib/active_support/number_helper.rb +29 -14
  101. data/lib/active_support/number_helper/number_converter.rb +1 -1
  102. data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -7
  103. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  104. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  105. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
  106. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  107. data/lib/active_support/option_merger.rb +3 -2
  108. data/lib/active_support/ordered_options.rb +8 -2
  109. data/lib/active_support/parameter_filter.rb +15 -10
  110. data/lib/active_support/per_thread_registry.rb +1 -1
  111. data/lib/active_support/rails.rb +1 -4
  112. data/lib/active_support/railtie.rb +23 -1
  113. data/lib/active_support/rescuable.rb +4 -4
  114. data/lib/active_support/secure_compare_rotator.rb +51 -0
  115. data/lib/active_support/security_utils.rb +19 -12
  116. data/lib/active_support/string_inquirer.rb +4 -2
  117. data/lib/active_support/subscriber.rb +12 -7
  118. data/lib/active_support/tagged_logging.rb +29 -4
  119. data/lib/active_support/testing/assertions.rb +18 -11
  120. data/lib/active_support/testing/parallelization.rb +12 -95
  121. data/lib/active_support/testing/parallelization/server.rb +78 -0
  122. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  123. data/lib/active_support/testing/time_helpers.rb +40 -3
  124. data/lib/active_support/time_with_zone.rb +66 -42
  125. data/lib/active_support/values/time_zone.rb +20 -10
  126. data/lib/active_support/xml_mini/rexml.rb +8 -1
  127. metadata +37 -39
  128. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  129. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  130. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  131. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  132. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  133. data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -16,13 +16,37 @@ module ActiveSupport
16
16
  # a cleanup will occur which tries to prune the cache down to three quarters
17
17
  # of the maximum size by removing the least recently used entries.
18
18
  #
19
+ # Unlike other Cache store implementations, MemoryStore does not compress
20
+ # values by default. MemoryStore does not benefit from compression as much
21
+ # as other Store implementations, as it does not send data over a network.
22
+ # However, when compression is enabled, it still pays the full cost of
23
+ # compression in terms of cpu use.
24
+ #
19
25
  # MemoryStore is thread-safe.
20
26
  class MemoryStore < Store
27
+ module DupCoder # :nodoc:
28
+ class << self
29
+ def load(entry)
30
+ entry = entry.dup
31
+ entry.dup_value!
32
+ entry
33
+ end
34
+
35
+ def dump(entry)
36
+ entry.dup_value!
37
+ entry
38
+ end
39
+ end
40
+ end
41
+
42
+ DEFAULT_CODER = DupCoder
43
+
21
44
  def initialize(options = nil)
22
45
  options ||= {}
46
+ # Disable compression by default.
47
+ options[:compress] ||= false
23
48
  super(options)
24
49
  @data = {}
25
- @key_access = {}
26
50
  @max_size = options[:size] || 32.megabytes
27
51
  @max_prune_time = options[:max_prune_time] || 2
28
52
  @cache_size = 0
@@ -39,7 +63,6 @@ module ActiveSupport
39
63
  def clear(options = nil)
40
64
  synchronize do
41
65
  @data.clear
42
- @key_access.clear
43
66
  @cache_size = 0
44
67
  end
45
68
  end
@@ -65,7 +88,7 @@ module ActiveSupport
65
88
  start_time = Concurrent.monotonic_time
66
89
  cleanup
67
90
  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 } }
91
+ keys = synchronize { @data.keys }
69
92
  keys.each do |key|
70
93
  delete_entry(key, **options)
71
94
  return if @cache_size <= target_size || (max_time && Concurrent.monotonic_time - start_time > max_time)
@@ -104,7 +127,7 @@ module ActiveSupport
104
127
  end
105
128
 
106
129
  def inspect # :nodoc:
107
- "<##{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
130
+ "#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
108
131
  end
109
132
 
110
133
  # Synchronize calls to the cache. This should be called wherever the underlying cache implementation
@@ -116,36 +139,34 @@ module ActiveSupport
116
139
  private
117
140
  PER_ENTRY_OVERHEAD = 240
118
141
 
119
- def cached_size(key, entry)
120
- key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
142
+ def cached_size(key, payload)
143
+ key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
121
144
  end
122
145
 
123
146
  def read_entry(key, **options)
124
- entry = @data[key]
147
+ entry = nil
125
148
  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)
149
+ payload = @data.delete(key)
150
+ if payload
151
+ @data[key] = payload
152
+ entry = deserialize_entry(payload)
132
153
  end
133
154
  end
134
155
  entry
135
156
  end
136
157
 
137
158
  def write_entry(key, entry, **options)
138
- entry.dup_value!
159
+ payload = serialize_entry(entry)
139
160
  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)
161
+ return false if options[:unless_exist] && @data.key?(key)
162
+
163
+ old_payload = @data[key]
164
+ if old_payload
165
+ @cache_size -= (old_payload.bytesize - payload.bytesize)
144
166
  else
145
- @cache_size += cached_size(key, entry)
167
+ @cache_size += cached_size(key, payload)
146
168
  end
147
- @key_access[key] = Time.now.to_f
148
- @data[key] = entry
169
+ @data[key] = payload
149
170
  prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
150
171
  true
151
172
  end
@@ -153,16 +174,15 @@ module ActiveSupport
153
174
 
154
175
  def delete_entry(key, **options)
155
176
  synchronize do
156
- @key_access.delete(key)
157
- entry = @data.delete(key)
158
- @cache_size -= cached_size(key, entry) if entry
159
- !!entry
177
+ payload = @data.delete(key)
178
+ @cache_size -= cached_size(key, payload) if payload
179
+ !!payload
160
180
  end
161
181
  end
162
182
 
163
183
  def modify_value(name, amount, options)
184
+ options = merged_options(options)
164
185
  synchronize do
165
- options = merged_options(options)
166
186
  if num = read(name, options)
167
187
  num = num.to_i + amount
168
188
  write(name, num, options)
@@ -169,7 +169,7 @@ module ActiveSupport
169
169
  # Race condition TTL is not set by default. This can be used to avoid
170
170
  # "thundering herd" cache writes when hot cache entries are expired.
171
171
  # See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
172
- 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)
173
173
  @redis_options = redis_options
174
174
 
175
175
  @max_key_bytesize = MAX_KEY_BYTESIZE
@@ -177,7 +177,8 @@ module ActiveSupport
177
177
 
178
178
  super namespace: namespace,
179
179
  compress: compress, compress_threshold: compress_threshold,
180
- expires_in: expires_in, race_condition_ttl: race_condition_ttl
180
+ expires_in: expires_in, race_condition_ttl: race_condition_ttl,
181
+ coder: coder
181
182
  end
182
183
 
183
184
  def redis
@@ -195,7 +196,7 @@ module ActiveSupport
195
196
 
196
197
  def inspect
197
198
  instance = @redis || @redis_options
198
- "<##{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
199
+ "#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
199
200
  end
200
201
 
201
202
  # Cache Store API implementation.
@@ -238,10 +239,14 @@ module ActiveSupport
238
239
  pattern = namespace_key(matcher, options)
239
240
  cursor = "0"
240
241
  # Fetch keys in batches using SCAN to avoid blocking the Redis server.
241
- begin
242
- cursor, keys = c.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
243
- c.del(*keys) unless keys.empty?
244
- 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
245
250
  end
246
251
  end
247
252
  end
@@ -347,7 +352,7 @@ module ActiveSupport
347
352
 
348
353
  def read_multi_entries(names, **options)
349
354
  if mget_capable?
350
- read_multi_mget(*names)
355
+ read_multi_mget(*names, **options)
351
356
  else
352
357
  super
353
358
  end
@@ -394,7 +399,7 @@ module ActiveSupport
394
399
  modifiers[:nx] = unless_exist
395
400
  modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
396
401
 
397
- redis.with { |c| c.set key, serialized_entry, modifiers }
402
+ redis.with { |c| c.set key, serialized_entry, **modifiers }
398
403
  else
399
404
  redis.with { |c| c.set key, serialized_entry }
400
405
  end
@@ -414,6 +419,11 @@ module ActiveSupport
414
419
  end
415
420
  end
416
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
+
417
427
  # Nonstandard store provider API to write multiple values at once.
418
428
  def write_multi_entries(entries, expires_in: nil, **options)
419
429
  if entries.any?
@@ -429,11 +439,11 @@ module ActiveSupport
429
439
 
430
440
  # Truncate keys that exceed 1kB.
431
441
  def normalize_key(key, options)
432
- truncate_key super.b
442
+ truncate_key super&.b
433
443
  end
434
444
 
435
445
  def truncate_key(key)
436
- if key.bytesize > max_key_bytesize
446
+ if key && key.bytesize > max_key_bytesize
437
447
  suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
438
448
  truncate_at = max_key_bytesize - suffix.bytesize
439
449
  "#{key.byteslice(0, truncate_at)}#{suffix}"
@@ -442,21 +452,11 @@ module ActiveSupport
442
452
  end
443
453
  end
444
454
 
445
- def deserialize_entry(serialized_entry, raw:)
446
- if serialized_entry
447
- entry = Marshal.load(serialized_entry) rescue serialized_entry
448
-
449
- written_raw = serialized_entry.equal?(entry)
450
- if raw != written_raw
451
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
452
- Using a different value for the raw option when reading and writing
453
- to a cache key is deprecated for :redis_cache_store and Rails 6.0
454
- will stop automatically detecting the format when reading to avoid
455
- marshal loading untrusted raw strings.
456
- MSG
457
- end
458
-
459
- 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)
460
460
  end
461
461
  end
462
462
 
@@ -464,7 +464,7 @@ module ActiveSupport
464
464
  if raw
465
465
  entry.value.to_s
466
466
  else
467
- Marshal.dump(entry)
467
+ super(entry)
468
468
  end
469
469
  end
470
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 }
@@ -121,7 +130,13 @@ module ActiveSupport
121
130
  private
122
131
  def read_entry(key, **options)
123
132
  if cache = local_cache
124
- cache.fetch_entry(key) { super }
133
+ hit = true
134
+ value = cache.fetch_entry(key) do
135
+ hit = false
136
+ super
137
+ end
138
+ options[:event][:store] = cache.class.name if hit && options[:event]
139
+ value
125
140
  else
126
141
  super
127
142
  end
@@ -160,7 +175,7 @@ module ActiveSupport
160
175
  cache = local_cache
161
176
  cache.mute do
162
177
  if value
163
- cache.write(name, value, **options)
178
+ cache.write(name, value, options)
164
179
  else
165
180
  cache.delete(name, **options)
166
181
  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