activesupport 6.0.0 → 6.1.3

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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +381 -349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_support.rb +13 -1
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +3 -4
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache.rb +101 -59
  10. data/lib/active_support/cache/file_store.rb +11 -11
  11. data/lib/active_support/cache/mem_cache_store.rb +34 -33
  12. data/lib/active_support/cache/memory_store.rb +52 -31
  13. data/lib/active_support/cache/null_store.rb +3 -3
  14. data/lib/active_support/cache/redis_cache_store.rb +38 -33
  15. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  16. data/lib/active_support/callbacks.rb +65 -59
  17. data/lib/active_support/concern.rb +46 -2
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +3 -3
  21. data/lib/active_support/configuration_file.rb +46 -0
  22. data/lib/active_support/core_ext.rb +1 -1
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/benchmark.rb +2 -2
  25. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  26. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  27. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  28. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  29. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  30. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  31. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  32. data/lib/active_support/core_ext/enumerable.rb +76 -4
  33. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  34. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  35. data/lib/active_support/core_ext/hash/except.rb +1 -1
  36. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  37. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  38. data/lib/active_support/core_ext/load_error.rb +1 -1
  39. data/lib/active_support/core_ext/marshal.rb +2 -0
  40. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  41. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  42. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  43. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  44. data/lib/active_support/core_ext/module/delegation.rb +46 -29
  45. data/lib/active_support/core_ext/module/introspection.rb +2 -25
  46. data/lib/active_support/core_ext/name_error.rb +29 -2
  47. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  48. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  49. data/lib/active_support/core_ext/object/json.rb +13 -2
  50. data/lib/active_support/core_ext/object/try.rb +4 -2
  51. data/lib/active_support/core_ext/range/compare_range.rb +15 -3
  52. data/lib/active_support/core_ext/range/each.rb +0 -1
  53. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  54. data/lib/active_support/core_ext/regexp.rb +8 -1
  55. data/lib/active_support/core_ext/string/access.rb +5 -24
  56. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  57. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  58. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  59. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  60. data/lib/active_support/core_ext/string/output_safety.rb +12 -11
  61. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  62. data/lib/active_support/core_ext/symbol.rb +3 -0
  63. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  64. data/lib/active_support/core_ext/time/calculations.rb +27 -3
  65. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  66. data/lib/active_support/core_ext/uri.rb +5 -1
  67. data/lib/active_support/current_attributes.rb +7 -2
  68. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  69. data/lib/active_support/dependencies.rb +42 -20
  70. data/lib/active_support/dependencies/zeitwerk_integration.rb +9 -2
  71. data/lib/active_support/deprecation.rb +6 -1
  72. data/lib/active_support/deprecation/behaviors.rb +15 -2
  73. data/lib/active_support/deprecation/disallowed.rb +56 -0
  74. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  75. data/lib/active_support/deprecation/method_wrappers.rb +13 -6
  76. data/lib/active_support/deprecation/proxy_wrappers.rb +6 -2
  77. data/lib/active_support/deprecation/reporting.rb +50 -7
  78. data/lib/active_support/descendants_tracker.rb +6 -3
  79. data/lib/active_support/duration.rb +86 -35
  80. data/lib/active_support/duration/iso8601_parser.rb +0 -1
  81. data/lib/active_support/duration/iso8601_serializer.rb +15 -10
  82. data/lib/active_support/encrypted_file.rb +20 -3
  83. data/lib/active_support/environment_inquirer.rb +20 -0
  84. data/lib/active_support/evented_file_update_checker.rb +69 -134
  85. data/lib/active_support/file_update_checker.rb +0 -1
  86. data/lib/active_support/fork_tracker.rb +62 -0
  87. data/lib/active_support/gem_version.rb +2 -2
  88. data/lib/active_support/hash_with_indifferent_access.rb +43 -24
  89. data/lib/active_support/i18n_railtie.rb +15 -16
  90. data/lib/active_support/inflector/inflections.rb +1 -3
  91. data/lib/active_support/inflector/methods.rb +36 -33
  92. data/lib/active_support/inflector/transliterate.rb +4 -4
  93. data/lib/active_support/json/decoding.rb +4 -5
  94. data/lib/active_support/json/encoding.rb +5 -1
  95. data/lib/active_support/key_generator.rb +1 -1
  96. data/lib/active_support/lazy_load_hooks.rb +0 -1
  97. data/lib/active_support/locale/en.rb +4 -2
  98. data/lib/active_support/locale/en.yml +7 -3
  99. data/lib/active_support/log_subscriber.rb +8 -1
  100. data/lib/active_support/logger.rb +2 -2
  101. data/lib/active_support/logger_silence.rb +2 -26
  102. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  103. data/lib/active_support/message_encryptor.rb +5 -8
  104. data/lib/active_support/message_verifier.rb +7 -7
  105. data/lib/active_support/messages/metadata.rb +11 -2
  106. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  107. data/lib/active_support/messages/rotator.rb +10 -9
  108. data/lib/active_support/multibyte/chars.rb +5 -44
  109. data/lib/active_support/multibyte/unicode.rb +9 -84
  110. data/lib/active_support/notifications.rb +32 -5
  111. data/lib/active_support/notifications/fanout.rb +23 -8
  112. data/lib/active_support/notifications/instrumenter.rb +7 -16
  113. data/lib/active_support/number_helper.rb +33 -14
  114. data/lib/active_support/number_helper/number_converter.rb +5 -6
  115. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -7
  116. data/lib/active_support/number_helper/number_to_delimited_converter.rb +0 -1
  117. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -2
  118. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -2
  119. data/lib/active_support/number_helper/number_to_phone_converter.rb +0 -1
  120. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -4
  121. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  122. data/lib/active_support/option_merger.rb +22 -3
  123. data/lib/active_support/ordered_hash.rb +1 -1
  124. data/lib/active_support/ordered_options.rb +13 -3
  125. data/lib/active_support/parameter_filter.rb +17 -13
  126. data/lib/active_support/per_thread_registry.rb +1 -1
  127. data/lib/active_support/rails.rb +1 -4
  128. data/lib/active_support/railtie.rb +23 -1
  129. data/lib/active_support/rescuable.rb +4 -4
  130. data/lib/active_support/secure_compare_rotator.rb +51 -0
  131. data/lib/active_support/security_utils.rb +19 -12
  132. data/lib/active_support/string_inquirer.rb +4 -3
  133. data/lib/active_support/subscriber.rb +12 -7
  134. data/lib/active_support/tagged_logging.rb +29 -4
  135. data/lib/active_support/testing/assertions.rb +18 -11
  136. data/lib/active_support/testing/parallelization.rb +12 -89
  137. data/lib/active_support/testing/parallelization/server.rb +78 -0
  138. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  139. data/lib/active_support/testing/stream.rb +0 -1
  140. data/lib/active_support/testing/time_helpers.rb +40 -5
  141. data/lib/active_support/time_with_zone.rb +67 -43
  142. data/lib/active_support/values/time_zone.rb +20 -10
  143. data/lib/active_support/xml_mini.rb +0 -1
  144. data/lib/active_support/xml_mini/jdom.rb +0 -1
  145. data/lib/active_support/xml_mini/rexml.rb +8 -1
  146. metadata +39 -38
  147. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  148. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  149. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  150. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  151. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  152. data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -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
 
@@ -50,32 +49,36 @@ module ActiveSupport
50
49
  @data.clear
51
50
  end
52
51
 
53
- def read_entry(key, options)
52
+ def read_entry(key, **options)
54
53
  @data[key]
55
54
  end
56
55
 
57
- def read_multi_entries(keys, options)
56
+ def read_multi_entries(keys, **options)
58
57
  values = {}
59
58
 
60
59
  keys.each do |name|
61
- entry = read_entry(name, options)
60
+ entry = read_entry(name, **options)
62
61
  values[name] = entry.value if entry
63
62
  end
64
63
 
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
 
73
- def delete_entry(key, options)
73
+ def delete_entry(key, **options)
74
74
  !!@data.delete(key)
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
 
@@ -92,77 +95,89 @@ module ActiveSupport
92
95
  local_cache_key)
93
96
  end
94
97
 
95
- def clear(options = nil) # :nodoc:
98
+ def clear(**options) # :nodoc:
96
99
  return super unless cache = local_cache
97
100
  cache.clear(options)
98
101
  super
99
102
  end
100
103
 
101
- def cleanup(options = nil) # :nodoc:
104
+ def cleanup(**options) # :nodoc:
102
105
  return super unless cache = local_cache
103
106
  cache.clear
104
107
  super
105
108
  end
106
109
 
107
- def increment(name, amount = 1, options = nil) # :nodoc:
110
+ def delete_matched(matcher, options = nil) # :nodoc:
111
+ return super unless cache = local_cache
112
+ cache.clear
113
+ super
114
+ end
115
+
116
+ def increment(name, amount = 1, **options) # :nodoc:
108
117
  return super unless local_cache
109
118
  value = bypass_local_cache { super }
110
- write_cache_value(name, value, options)
119
+ write_cache_value(name, value, **options)
111
120
  value
112
121
  end
113
122
 
114
- def decrement(name, amount = 1, options = nil) # :nodoc:
123
+ def decrement(name, amount = 1, **options) # :nodoc:
115
124
  return super unless local_cache
116
125
  value = bypass_local_cache { super }
117
- write_cache_value(name, value, options)
126
+ write_cache_value(name, value, **options)
118
127
  value
119
128
  end
120
129
 
121
130
  private
122
- def read_entry(key, options)
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
128
143
  end
129
144
 
130
- def read_multi_entries(keys, options)
145
+ def read_multi_entries(keys, **options)
131
146
  return super unless local_cache
132
147
 
133
- local_entries = local_cache.read_multi_entries(keys, options)
148
+ local_entries = local_cache.read_multi_entries(keys, **options)
134
149
  missed_keys = keys - local_entries.keys
135
150
 
136
151
  if missed_keys.any?
137
- local_entries.merge!(super(missed_keys, options))
152
+ local_entries.merge!(super(missed_keys, **options))
138
153
  else
139
154
  local_entries
140
155
  end
141
156
  end
142
157
 
143
- def write_entry(key, entry, options)
158
+ def write_entry(key, entry, **options)
144
159
  if options[:unless_exist]
145
- local_cache.delete_entry(key, options) if local_cache
160
+ local_cache.delete_entry(key, **options) if local_cache
146
161
  else
147
- local_cache.write_entry(key, entry, options) if local_cache
162
+ local_cache.write_entry(key, entry, **options) if local_cache
148
163
  end
149
164
 
150
165
  super
151
166
  end
152
167
 
153
- def delete_entry(key, options)
154
- local_cache.delete_entry(key, options) if local_cache
168
+ def delete_entry(key, **options)
169
+ local_cache.delete_entry(key, **options) if local_cache
155
170
  super
156
171
  end
157
172
 
158
- def write_cache_value(name, value, options)
173
+ def write_cache_value(name, value, **options)
159
174
  name = normalize_key(name, options)
160
175
  cache = local_cache
161
176
  cache.mute do
162
177
  if value
163
178
  cache.write(name, value, options)
164
179
  else
165
- cache.delete(name, options)
180
+ cache.delete(name, **options)
166
181
  end
167
182
  end
168
183
  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,17 +107,43 @@ 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
142
140
  end
143
141
 
144
142
  private
145
-
146
143
  # A hook invoked every time a before callback is halted.
147
144
  # This can be overridden in ActiveSupport::Callbacks implementors in order
148
145
  # to provide better debugging/logging.
149
- def halted_callback_hook(filter)
146
+ def halted_callback_hook(filter, name)
150
147
  end
151
148
 
152
149
  module Conditionals # :nodoc:
@@ -162,17 +159,17 @@ module ActiveSupport
162
159
  Environment = Struct.new(:target, :halted, :value)
163
160
 
164
161
  class Before
165
- 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)
166
163
  halted_lambda = chain_config[:terminator]
167
164
 
168
165
  if user_conditions.any?
169
- 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)
170
167
  else
171
- halting(callback_sequence, user_callback, halted_lambda, filter)
168
+ halting(callback_sequence, user_callback, halted_lambda, filter, name)
172
169
  end
173
170
  end
174
171
 
175
- 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)
176
173
  callback_sequence.before do |env|
177
174
  target = env.target
178
175
  value = env.value
@@ -182,7 +179,7 @@ module ActiveSupport
182
179
  result_lambda = -> { user_callback.call target, value }
183
180
  env.halted = halted_lambda.call(target, result_lambda)
184
181
  if env.halted
185
- target.send :halted_callback_hook, filter
182
+ target.send :halted_callback_hook, filter, name
186
183
  end
187
184
  end
188
185
 
@@ -191,7 +188,7 @@ module ActiveSupport
191
188
  end
192
189
  private_class_method :halting_and_conditional
193
190
 
194
- def self.halting(callback_sequence, user_callback, halted_lambda, filter)
191
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
195
192
  callback_sequence.before do |env|
196
193
  target = env.target
197
194
  value = env.value
@@ -200,9 +197,8 @@ module ActiveSupport
200
197
  unless halted
201
198
  result_lambda = -> { user_callback.call target, value }
202
199
  env.halted = halted_lambda.call(target, result_lambda)
203
-
204
200
  if env.halted
205
- target.send :halted_callback_hook, filter
201
+ target.send :halted_callback_hook, filter, name
206
202
  end
207
203
  end
208
204
 
@@ -301,8 +297,8 @@ module ActiveSupport
301
297
  @kind = kind
302
298
  @filter = filter
303
299
  @key = compute_identifier filter
304
- @if = check_conditionals(Array(options[:if]))
305
- @unless = check_conditionals(Array(options[:unless]))
300
+ @if = check_conditionals(options[:if])
301
+ @unless = check_conditionals(options[:unless])
306
302
  end
307
303
 
308
304
  def filter; @key; end
@@ -340,7 +336,7 @@ module ActiveSupport
340
336
 
341
337
  case kind
342
338
  when :before
343
- 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)
344
340
  when :after
345
341
  Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
346
342
  when :around
@@ -353,7 +349,13 @@ module ActiveSupport
353
349
  end
354
350
 
355
351
  private
352
+ EMPTY_ARRAY = [].freeze
353
+ private_constant :EMPTY_ARRAY
354
+
356
355
  def check_conditionals(conditionals)
356
+ return EMPTY_ARRAY if conditionals.blank?
357
+
358
+ conditionals = Array(conditionals)
357
359
  if conditionals.any? { |c| c.is_a?(String) }
358
360
  raise ArgumentError, <<-MSG.squish
359
361
  Passing string to be evaluated in :if and :unless conditional
@@ -362,7 +364,7 @@ module ActiveSupport
362
364
  MSG
363
365
  end
364
366
 
365
- conditionals
367
+ conditionals.freeze
366
368
  end
367
369
 
368
370
  def compute_identifier(filter)
@@ -404,21 +406,17 @@ module ActiveSupport
404
406
  # The actual invocation is left up to the caller to minimize
405
407
  # call stack pollution.
406
408
  def expand(target, value, block)
407
- result = @arguments.map { |arg|
409
+ expanded = [@override_target || target, @override_block || block, @method_name]
410
+
411
+ @arguments.each do |arg|
408
412
  case arg
409
- when :value; value
410
- when :target; target
411
- 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))
412
416
  end
413
- }
414
-
415
- result.unshift @method_name
416
- result.unshift @override_block || block
417
- result.unshift @override_target || target
417
+ end
418
418
 
419
- # target, block, method, *arguments = result
420
- # target.send(method, *arguments, &block)
421
- result
419
+ expanded
422
420
  end
423
421
 
424
422
  # Return a lambda that will make this call when given the input
@@ -582,7 +580,6 @@ module ActiveSupport
582
580
  attr_reader :chain
583
581
 
584
582
  private
585
-
586
583
  def append_one(callback)
587
584
  @callbacks = nil
588
585
  remove_duplicates(callback)
@@ -843,13 +840,22 @@ module ActiveSupport
843
840
  end
844
841
 
845
842
  protected
846
-
847
843
  def get_callbacks(name) # :nodoc:
848
844
  __callbacks[name.to_sym]
849
845
  end
850
846
 
851
- def set_callbacks(name, callbacks) # :nodoc:
852
- 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
853
859
  end
854
860
  end
855
861
  end
@@ -19,7 +19,7 @@ module ActiveSupport
19
19
  # By using <tt>ActiveSupport::Concern</tt> the above module could instead be
20
20
  # written as:
21
21
  #
22
- # require 'active_support/concern'
22
+ # require "active_support/concern"
23
23
  #
24
24
  # module M
25
25
  # extend ActiveSupport::Concern
@@ -76,7 +76,7 @@ module ActiveSupport
76
76
  # is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
77
77
  # module dependencies are properly resolved:
78
78
  #
79
- # require 'active_support/concern'
79
+ # require "active_support/concern"
80
80
  #
81
81
  # module Foo
82
82
  # extend ActiveSupport::Concern
@@ -99,6 +99,14 @@ module ActiveSupport
99
99
  # class Host
100
100
  # include Bar # It works, now Bar takes care of its dependencies
101
101
  # end
102
+ #
103
+ # === Prepending concerns
104
+ #
105
+ # Just like <tt>include</tt>, concerns also support <tt>prepend</tt> with a corresponding
106
+ # <tt>prepended do</tt> callback. <tt>module ClassMethods</tt> or <tt>class_methods do</tt> are
107
+ # prepended as well.
108
+ #
109
+ # <tt>prepend</tt> is also used for any dependencies.
102
110
  module Concern
103
111
  class MultipleIncludedBlocks < StandardError #:nodoc:
104
112
  def initialize
@@ -106,6 +114,12 @@ module ActiveSupport
106
114
  end
107
115
  end
108
116
 
117
+ class MultiplePrependBlocks < StandardError #:nodoc:
118
+ def initialize
119
+ super "Cannot define multiple 'prepended' blocks for a Concern"
120
+ end
121
+ end
122
+
109
123
  def self.extended(base) #:nodoc:
110
124
  base.instance_variable_set(:@_dependencies, [])
111
125
  end
@@ -123,6 +137,19 @@ module ActiveSupport
123
137
  end
124
138
  end
125
139
 
140
+ def prepend_features(base) #:nodoc:
141
+ if base.instance_variable_defined?(:@_dependencies)
142
+ base.instance_variable_get(:@_dependencies).unshift self
143
+ false
144
+ else
145
+ return false if base < self
146
+ @_dependencies.each { |dep| base.prepend(dep) }
147
+ super
148
+ base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods)
149
+ base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block)
150
+ end
151
+ end
152
+
126
153
  # Evaluate given block in context of base class,
127
154
  # so that you can write class macros here.
128
155
  # When you define more than one +included+ block, it raises an exception.
@@ -140,6 +167,23 @@ module ActiveSupport
140
167
  end
141
168
  end
142
169
 
170
+ # Evaluate given block in context of base class,
171
+ # so that you can write class macros here.
172
+ # When you define more than one +prepended+ block, it raises an exception.
173
+ def prepended(base = nil, &block)
174
+ if base.nil?
175
+ if instance_variable_defined?(:@_prepended_block)
176
+ if @_prepended_block.source_location != block.source_location
177
+ raise MultiplePrependBlocks
178
+ end
179
+ else
180
+ @_prepended_block = block
181
+ end
182
+ else
183
+ super
184
+ end
185
+ end
186
+
143
187
  # Define class methods from given block.
144
188
  # You can define private class methods as well.
145
189
  #