activesupport 7.1.6 → 8.1.1

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.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +256 -1133
  3. data/README.rdoc +1 -1
  4. data/lib/active_support/array_inquirer.rb +1 -1
  5. data/lib/active_support/backtrace_cleaner.rb +81 -3
  6. data/lib/active_support/benchmark.rb +21 -0
  7. data/lib/active_support/benchmarkable.rb +3 -2
  8. data/lib/active_support/broadcast_logger.rb +65 -78
  9. data/lib/active_support/cache/file_store.rb +29 -14
  10. data/lib/active_support/cache/mem_cache_store.rb +42 -102
  11. data/lib/active_support/cache/memory_store.rb +11 -6
  12. data/lib/active_support/cache/null_store.rb +2 -2
  13. data/lib/active_support/cache/redis_cache_store.rb +58 -46
  14. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  15. data/lib/active_support/cache/strategy/local_cache.rb +72 -27
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  17. data/lib/active_support/cache.rb +146 -86
  18. data/lib/active_support/callbacks.rb +102 -126
  19. data/lib/active_support/class_attribute.rb +33 -0
  20. data/lib/active_support/code_generator.rb +9 -0
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
  22. data/lib/active_support/concurrency/share_lock.rb +0 -1
  23. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  24. data/lib/active_support/configurable.rb +34 -0
  25. data/lib/active_support/configuration_file.rb +15 -6
  26. data/lib/active_support/continuous_integration.rb +145 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +3 -5
  28. data/lib/active_support/core_ext/array.rb +7 -7
  29. data/lib/active_support/core_ext/benchmark.rb +4 -14
  30. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  31. data/lib/active_support/core_ext/class/attribute.rb +26 -19
  32. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  33. data/lib/active_support/core_ext/class.rb +2 -2
  34. data/lib/active_support/core_ext/date/blank.rb +4 -0
  35. data/lib/active_support/core_ext/date/conversions.rb +2 -2
  36. data/lib/active_support/core_ext/date.rb +5 -5
  37. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -9
  38. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  39. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  40. data/lib/active_support/core_ext/date_time/conversions.rb +4 -6
  41. data/lib/active_support/core_ext/date_time.rb +5 -5
  42. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  43. data/lib/active_support/core_ext/digest.rb +1 -1
  44. data/lib/active_support/core_ext/enumerable.rb +25 -8
  45. data/lib/active_support/core_ext/erb/util.rb +10 -5
  46. data/lib/active_support/core_ext/file.rb +1 -1
  47. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
  48. data/lib/active_support/core_ext/hash/except.rb +0 -12
  49. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  50. data/lib/active_support/core_ext/hash.rb +8 -8
  51. data/lib/active_support/core_ext/integer.rb +3 -3
  52. data/lib/active_support/core_ext/kernel.rb +3 -3
  53. data/lib/active_support/core_ext/module/attr_internal.rb +16 -6
  54. data/lib/active_support/core_ext/module/delegation.rb +20 -163
  55. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  56. data/lib/active_support/core_ext/module/introspection.rb +3 -0
  57. data/lib/active_support/core_ext/module.rb +11 -11
  58. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  59. data/lib/active_support/core_ext/numeric.rb +3 -3
  60. data/lib/active_support/core_ext/object/blank.rb +45 -1
  61. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  62. data/lib/active_support/core_ext/object/json.rb +24 -11
  63. data/lib/active_support/core_ext/object/to_query.rb +7 -1
  64. data/lib/active_support/core_ext/object/try.rb +2 -2
  65. data/lib/active_support/core_ext/object/with.rb +5 -3
  66. data/lib/active_support/core_ext/object.rb +13 -13
  67. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  68. data/lib/active_support/core_ext/pathname.rb +2 -2
  69. data/lib/active_support/core_ext/range/overlap.rb +4 -4
  70. data/lib/active_support/core_ext/range/sole.rb +17 -0
  71. data/lib/active_support/core_ext/range.rb +4 -4
  72. data/lib/active_support/core_ext/securerandom.rb +4 -4
  73. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  74. data/lib/active_support/core_ext/string/filters.rb +4 -4
  75. data/lib/active_support/core_ext/string/multibyte.rb +13 -4
  76. data/lib/active_support/core_ext/string/output_safety.rb +19 -19
  77. data/lib/active_support/core_ext/string.rb +13 -13
  78. data/lib/active_support/core_ext/symbol.rb +1 -1
  79. data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
  80. data/lib/active_support/core_ext/time/calculations.rb +25 -30
  81. data/lib/active_support/core_ext/time/compatibility.rb +2 -3
  82. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  83. data/lib/active_support/core_ext/time/zones.rb +1 -1
  84. data/lib/active_support/core_ext/time.rb +5 -5
  85. data/lib/active_support/core_ext.rb +1 -2
  86. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  87. data/lib/active_support/current_attributes.rb +58 -50
  88. data/lib/active_support/delegation.rb +200 -0
  89. data/lib/active_support/dependencies/autoload.rb +0 -12
  90. data/lib/active_support/dependencies/interlock.rb +11 -5
  91. data/lib/active_support/dependencies.rb +6 -2
  92. data/lib/active_support/deprecation/constant_accessor.rb +47 -26
  93. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  94. data/lib/active_support/deprecation/reporting.rb +5 -17
  95. data/lib/active_support/deprecation.rb +8 -5
  96. data/lib/active_support/descendants_tracker.rb +9 -87
  97. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  98. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  99. data/lib/active_support/duration.rb +25 -16
  100. data/lib/active_support/editor.rb +70 -0
  101. data/lib/active_support/encrypted_configuration.rb +20 -2
  102. data/lib/active_support/encrypted_file.rb +1 -1
  103. data/lib/active_support/error_reporter.rb +121 -6
  104. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  105. data/lib/active_support/event_reporter.rb +592 -0
  106. data/lib/active_support/evented_file_update_checker.rb +5 -3
  107. data/lib/active_support/execution_context.rb +64 -7
  108. data/lib/active_support/execution_wrapper.rb +1 -2
  109. data/lib/active_support/file_update_checker.rb +9 -7
  110. data/lib/active_support/fork_tracker.rb +2 -38
  111. data/lib/active_support/gem_version.rb +2 -2
  112. data/lib/active_support/gzip.rb +1 -0
  113. data/lib/active_support/hash_with_indifferent_access.rb +66 -45
  114. data/lib/active_support/html_safe_translation.rb +3 -0
  115. data/lib/active_support/i18n_railtie.rb +19 -11
  116. data/lib/active_support/inflector/inflections.rb +31 -15
  117. data/lib/active_support/inflector/transliterate.rb +6 -8
  118. data/lib/active_support/isolated_execution_state.rb +12 -17
  119. data/lib/active_support/json/decoding.rb +6 -4
  120. data/lib/active_support/json/encoding.rb +157 -21
  121. data/lib/active_support/lazy_load_hooks.rb +1 -1
  122. data/lib/active_support/log_subscriber.rb +2 -18
  123. data/lib/active_support/logger.rb +15 -2
  124. data/lib/active_support/logger_thread_safe_level.rb +4 -9
  125. data/lib/active_support/message_encryptors.rb +54 -2
  126. data/lib/active_support/message_pack/extensions.rb +20 -2
  127. data/lib/active_support/message_verifier.rb +21 -0
  128. data/lib/active_support/message_verifiers.rb +57 -3
  129. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  130. data/lib/active_support/messages/rotator.rb +10 -0
  131. data/lib/active_support/multibyte/chars.rb +14 -4
  132. data/lib/active_support/multibyte.rb +4 -0
  133. data/lib/active_support/notifications/fanout.rb +68 -50
  134. data/lib/active_support/notifications/instrumenter.rb +22 -19
  135. data/lib/active_support/notifications.rb +28 -27
  136. data/lib/active_support/number_helper/number_converter.rb +2 -2
  137. data/lib/active_support/number_helper.rb +22 -0
  138. data/lib/active_support/option_merger.rb +2 -2
  139. data/lib/active_support/ordered_options.rb +53 -15
  140. data/lib/active_support/railtie.rb +36 -20
  141. data/lib/active_support/string_inquirer.rb +1 -1
  142. data/lib/active_support/structured_event_subscriber.rb +99 -0
  143. data/lib/active_support/subscriber.rb +1 -5
  144. data/lib/active_support/syntax_error_proxy.rb +3 -0
  145. data/lib/active_support/tagged_logging.rb +5 -1
  146. data/lib/active_support/test_case.rb +63 -6
  147. data/lib/active_support/testing/assertions.rb +113 -27
  148. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  149. data/lib/active_support/testing/deprecation.rb +5 -12
  150. data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
  151. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  152. data/lib/active_support/testing/isolation.rb +19 -9
  153. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  154. data/lib/active_support/testing/notification_assertions.rb +92 -0
  155. data/lib/active_support/testing/parallelization/server.rb +18 -2
  156. data/lib/active_support/testing/parallelization/worker.rb +4 -2
  157. data/lib/active_support/testing/parallelization.rb +25 -1
  158. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  159. data/lib/active_support/testing/time_helpers.rb +11 -6
  160. data/lib/active_support/time_with_zone.rb +39 -26
  161. data/lib/active_support/values/time_zone.rb +26 -17
  162. data/lib/active_support/xml_mini.rb +14 -4
  163. data/lib/active_support.rb +22 -9
  164. metadata +31 -17
  165. data/lib/active_support/core_ext/range/each.rb +0 -24
  166. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  167. data/lib/active_support/proxy_object.rb +0 -17
  168. data/lib/active_support/ruby_features.rb +0 -7
  169. data/lib/active_support/testing/strict_warnings.rb +0 -39
@@ -4,9 +4,9 @@ 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/module/redefine_method"
7
8
  require "active_support/core_ext/string/filters"
8
9
  require "active_support/core_ext/object/blank"
9
- require "thread"
10
10
 
11
11
  module ActiveSupport
12
12
  # = Active Support \Callbacks
@@ -67,7 +67,7 @@ module ActiveSupport
67
67
 
68
68
  included do
69
69
  extend ActiveSupport::DescendantsTracker
70
- class_attribute :__callbacks, instance_writer: false, default: {}
70
+ class_attribute :__callbacks, instance_writer: false, instance_predicate: false, default: {}
71
71
  end
72
72
 
73
73
  CALLBACK_FILTER_TYPES = [:before, :after, :around].freeze
@@ -150,7 +150,7 @@ module ActiveSupport
150
150
  def halted_callback_hook(filter, name)
151
151
  end
152
152
 
153
- module Conditionals # :nodoc:
153
+ module Conditionals # :nodoc: all
154
154
  class Value
155
155
  def initialize(&block)
156
156
  @block = block
@@ -159,128 +159,76 @@ module ActiveSupport
159
159
  end
160
160
  end
161
161
 
162
- module Filters
162
+ module Filters # :nodoc: all
163
163
  Environment = Struct.new(:target, :halted, :value)
164
164
 
165
165
  class Before
166
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
166
+ def initialize(user_callback, user_conditions, chain_config, filter, name)
167
167
  halted_lambda = chain_config[:terminator]
168
-
169
- if user_conditions.any?
170
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
171
- else
172
- halting(callback_sequence, user_callback, halted_lambda, filter, name)
173
- end
168
+ @user_callback, @user_conditions, @halted_lambda, @filter, @name = user_callback, user_conditions, halted_lambda, filter, name
169
+ freeze
174
170
  end
171
+ attr_reader :user_callback, :user_conditions, :halted_lambda, :filter, :name
175
172
 
176
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
177
- callback_sequence.before do |env|
178
- target = env.target
179
- value = env.value
180
- halted = env.halted
173
+ def call(env)
174
+ target = env.target
175
+ value = env.value
176
+ halted = env.halted
181
177
 
182
- if !halted && user_conditions.all? { |c| c.call(target, value) }
183
- result_lambda = -> { user_callback.call target, value }
184
- env.halted = halted_lambda.call(target, result_lambda)
185
- if env.halted
186
- target.send :halted_callback_hook, filter, name
187
- end
178
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
179
+ result_lambda = -> { user_callback.call target, value }
180
+ env.halted = halted_lambda.call(target, result_lambda)
181
+ if env.halted
182
+ target.send :halted_callback_hook, filter, name
188
183
  end
189
-
190
- env
191
184
  end
192
- end
193
- private_class_method :halting_and_conditional
194
-
195
- def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
196
- callback_sequence.before do |env|
197
- target = env.target
198
- value = env.value
199
- halted = env.halted
200
185
 
201
- unless halted
202
- result_lambda = -> { user_callback.call target, value }
203
- env.halted = halted_lambda.call(target, result_lambda)
204
- if env.halted
205
- target.send :halted_callback_hook, filter, name
206
- end
207
- end
186
+ env
187
+ end
208
188
 
209
- env
210
- end
189
+ def apply(callback_sequence)
190
+ callback_sequence.before(self)
211
191
  end
212
- private_class_method :halting
213
192
  end
214
193
 
215
194
  class After
216
- def self.build(callback_sequence, user_callback, user_conditions, chain_config)
217
- if chain_config[:skip_after_callbacks_if_terminated]
218
- if user_conditions.any?
219
- halting_and_conditional(callback_sequence, user_callback, user_conditions)
220
- else
221
- halting(callback_sequence, user_callback)
222
- end
223
- else
224
- if user_conditions.any?
225
- conditional callback_sequence, user_callback, user_conditions
226
- else
227
- simple callback_sequence, user_callback
228
- end
229
- end
195
+ attr_reader :user_callback, :user_conditions, :halting
196
+ def initialize(user_callback, user_conditions, chain_config)
197
+ halting = chain_config[:skip_after_callbacks_if_terminated]
198
+ @user_callback, @user_conditions, @halting = user_callback, user_conditions, halting
199
+ freeze
230
200
  end
231
201
 
232
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
233
- callback_sequence.after do |env|
234
- target = env.target
235
- value = env.value
236
- halted = env.halted
237
-
238
- if !halted && user_conditions.all? { |c| c.call(target, value) }
239
- user_callback.call target, value
240
- end
202
+ def call(env)
203
+ target = env.target
204
+ value = env.value
205
+ halted = env.halted
241
206
 
242
- env
207
+ if (!halted || !@halting) && user_conditions.all? { |c| c.call(target, value) }
208
+ user_callback.call target, value
243
209
  end
244
- end
245
- private_class_method :halting_and_conditional
246
-
247
- def self.halting(callback_sequence, user_callback)
248
- callback_sequence.after do |env|
249
- unless env.halted
250
- user_callback.call env.target, env.value
251
- end
252
210
 
253
- env
254
- end
211
+ env
255
212
  end
256
- private_class_method :halting
257
-
258
- def self.conditional(callback_sequence, user_callback, user_conditions)
259
- callback_sequence.after do |env|
260
- target = env.target
261
- value = env.value
262
-
263
- if user_conditions.all? { |c| c.call(target, value) }
264
- user_callback.call target, value
265
- end
266
213
 
267
- env
268
- end
214
+ def apply(callback_sequence)
215
+ callback_sequence.after(self)
269
216
  end
270
- private_class_method :conditional
217
+ end
271
218
 
272
- def self.simple(callback_sequence, user_callback)
273
- callback_sequence.after do |env|
274
- user_callback.call env.target, env.value
219
+ class Around
220
+ def initialize(user_callback, user_conditions)
221
+ @user_callback, @user_conditions = user_callback, user_conditions
222
+ freeze
223
+ end
275
224
 
276
- env
277
- end
225
+ def apply(callback_sequence)
226
+ callback_sequence.around(@user_callback, @user_conditions)
278
227
  end
279
- private_class_method :simple
280
228
  end
281
229
  end
282
230
 
283
- class Callback # :nodoc:#
231
+ class Callback # :nodoc:
284
232
  def self.build(chain, filter, kind, options)
285
233
  if filter.is_a?(String)
286
234
  raise ArgumentError, <<-MSG.squish
@@ -302,6 +250,8 @@ module ActiveSupport
302
250
  @filter = filter
303
251
  @if = check_conditionals(options[:if])
304
252
  @unless = check_conditionals(options[:unless])
253
+
254
+ compiled
305
255
  end
306
256
 
307
257
  def merge_conditional_options(chain, if_option:, unless_option:)
@@ -329,19 +279,26 @@ module ActiveSupport
329
279
  end
330
280
  end
331
281
 
282
+ def compiled
283
+ @compiled ||=
284
+ begin
285
+ user_conditions = conditions_lambdas
286
+ user_callback = CallTemplate.build(@filter, self)
287
+
288
+ case kind
289
+ when :before
290
+ Filters::Before.new(user_callback.make_lambda, user_conditions, chain_config, @filter, name)
291
+ when :after
292
+ Filters::After.new(user_callback.make_lambda, user_conditions, chain_config)
293
+ when :around
294
+ Filters::Around.new(user_callback, user_conditions)
295
+ end
296
+ end
297
+ end
298
+
332
299
  # Wraps code with filter
333
300
  def apply(callback_sequence)
334
- user_conditions = conditions_lambdas
335
- user_callback = CallTemplate.build(@filter, self)
336
-
337
- case kind
338
- when :before
339
- Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
340
- when :after
341
- Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
342
- when :around
343
- callback_sequence.around(user_callback, user_conditions)
344
- end
301
+ compiled.apply(callback_sequence)
345
302
  end
346
303
 
347
304
  def current_scopes
@@ -368,14 +325,16 @@ module ActiveSupport
368
325
  end
369
326
 
370
327
  def conditions_lambdas
371
- @if.map { |c| CallTemplate.build(c, self).make_lambda } +
328
+ conditions =
329
+ @if.map { |c| CallTemplate.build(c, self).make_lambda } +
372
330
  @unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
331
+ conditions.empty? ? EMPTY_ARRAY : conditions
373
332
  end
374
333
  end
375
334
 
376
335
  # A future invocation of user-supplied code (either as a callback,
377
336
  # or a condition filter).
378
- module CallTemplate # :nodoc:
337
+ module CallTemplate # :nodoc: all
379
338
  class MethodCall
380
339
  def initialize(method)
381
340
  @method_name = method
@@ -540,9 +499,10 @@ module ActiveSupport
540
499
  when Conditionals::Value
541
500
  ProcCall.new(filter)
542
501
  when ::Proc
543
- if filter.arity > 1
502
+ case filter.arity
503
+ when 2
544
504
  InstanceExec2.new(filter)
545
- elsif filter.arity > 0
505
+ when 1, -2
546
506
  InstanceExec1.new(filter)
547
507
  else
548
508
  InstanceExec0.new(filter)
@@ -562,16 +522,18 @@ module ActiveSupport
562
522
  @call_template = call_template
563
523
  @user_conditions = user_conditions
564
524
 
565
- @before = []
566
- @after = []
525
+ @before = nil
526
+ @after = nil
567
527
  end
568
528
 
569
- def before(&before)
529
+ def before(before)
530
+ @before ||= []
570
531
  @before.unshift(before)
571
532
  self
572
533
  end
573
534
 
574
- def after(&after)
535
+ def after(after)
536
+ @after ||= []
575
537
  @after.push(after)
576
538
  self
577
539
  end
@@ -595,11 +557,11 @@ module ActiveSupport
595
557
  end
596
558
 
597
559
  def invoke_before(arg)
598
- @before.each { |b| b.call(arg) }
560
+ @before&.each { |b| b.call(arg) }
599
561
  end
600
562
 
601
563
  def invoke_after(arg)
602
- @after.each { |a| a.call(arg) }
564
+ @after&.each { |a| a.call(arg) }
603
565
  end
604
566
  end
605
567
 
@@ -612,7 +574,7 @@ module ActiveSupport
612
574
  @name = name
613
575
  @config = {
614
576
  scope: [:kind],
615
- terminator: default_terminator
577
+ terminator: DEFAULT_TERMINATOR
616
578
  }.merge!(config)
617
579
  @chain = []
618
580
  @all_callbacks = nil
@@ -700,8 +662,8 @@ module ActiveSupport
700
662
  @chain.delete_if { |c| callback.duplicates?(c) }
701
663
  end
702
664
 
703
- def default_terminator
704
- Proc.new do |target, result_lambda|
665
+ class DefaultTerminator # :nodoc:
666
+ def call(target, result_lambda)
705
667
  terminate = true
706
668
  catch(:abort) do
707
669
  result_lambda.call
@@ -710,6 +672,7 @@ module ActiveSupport
710
672
  terminate
711
673
  end
712
674
  end
675
+ DEFAULT_TERMINATOR = DefaultTerminator.new.freeze
713
676
  end
714
677
 
715
678
  module ClassMethods
@@ -943,12 +906,13 @@ module ActiveSupport
943
906
  names.each do |name|
944
907
  name = name.to_sym
945
908
 
946
- ([self] + self.descendants).each do |target|
947
- target.set_callbacks name, CallbackChain.new(name, options)
948
- end
949
-
950
909
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
951
- def _run_#{name}_callbacks(&block)
910
+ def _run_#{name}_callbacks
911
+ yield if block_given?
912
+ end
913
+ silence_redefinition_of_method(:_run_#{name}_callbacks)
914
+
915
+ def _run_#{name}_callbacks!(&block)
952
916
  run_callbacks #{name.inspect}, &block
953
917
  end
954
918
 
@@ -964,6 +928,10 @@ module ActiveSupport
964
928
  __callbacks[#{name.inspect}]
965
929
  end
966
930
  RUBY
931
+
932
+ ([self] + self.descendants).each do |target|
933
+ target.set_callbacks name, CallbackChain.new(name, options)
934
+ end
967
935
  end
968
936
  end
969
937
 
@@ -973,9 +941,17 @@ module ActiveSupport
973
941
  end
974
942
 
975
943
  def set_callbacks(name, callbacks) # :nodoc:
976
- unless singleton_class.method_defined?(:__callbacks, false)
944
+ # HACK: We're making assumption on how `class_attribute` is implemented
945
+ # to save constantly duping the callback hash. If this desync with class_attribute
946
+ # we'll lose the optimization, but won't cause an actual behavior bug.
947
+ unless singleton_class.private_method_defined?(:__class_attr__callbacks, false)
977
948
  self.__callbacks = __callbacks.dup
978
949
  end
950
+ name = name.to_sym
951
+ callbacks_was = self.__callbacks[name.to_sym]
952
+ if (callbacks_was.nil? || callbacks_was.empty?) && !callbacks.empty?
953
+ alias_method("_run_#{name}_callbacks", "_run_#{name}_callbacks!")
954
+ end
979
955
  self.__callbacks[name.to_sym] = callbacks
980
956
  self.__callbacks
981
957
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module ClassAttribute # :nodoc:
5
+ class << self
6
+ def redefine(owner, name, namespaced_name, value)
7
+ if owner.singleton_class?
8
+ if owner.attached_object.is_a?(Module)
9
+ redefine_method(owner, namespaced_name, private: true) { value }
10
+ else
11
+ redefine_method(owner, name) { value }
12
+ end
13
+ end
14
+
15
+ redefine_method(owner.singleton_class, namespaced_name, private: true) { value }
16
+
17
+ redefine_method(owner.singleton_class, "#{namespaced_name}=", private: true) do |new_value|
18
+ if owner.equal?(self)
19
+ value = new_value
20
+ else
21
+ ::ActiveSupport::ClassAttribute.redefine(self, name, namespaced_name, new_value)
22
+ end
23
+ end
24
+ end
25
+
26
+ def redefine_method(owner, name, private: false, &block)
27
+ owner.silence_redefinition_of_method(name)
28
+ owner.define_method(name, &block)
29
+ owner.send(:private, name) if private
30
+ end
31
+ end
32
+ end
33
+ end
@@ -55,6 +55,11 @@ module ActiveSupport
55
55
  @path = path
56
56
  @line = line
57
57
  @namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
58
+ @sources = []
59
+ end
60
+
61
+ def class_eval
62
+ yield @sources
58
63
  end
59
64
 
60
65
  def define_cached_method(canonical_name, namespace:, as: nil, &block)
@@ -65,6 +70,10 @@ module ActiveSupport
65
70
  @namespaces.each_value do |method_set|
66
71
  method_set.apply(@owner, @path, @line - 1)
67
72
  end
73
+
74
+ unless @sources.empty?
75
+ @owner.class_eval("# frozen_string_literal: true\n" + @sources.join(";"), @path, @line - 1)
76
+ end
68
77
  end
69
78
  end
70
79
  end
@@ -4,69 +4,15 @@ require "monitor"
4
4
 
5
5
  module ActiveSupport
6
6
  module Concurrency
7
- module LoadInterlockAwareMonitorMixin # :nodoc:
8
- EXCEPTION_NEVER = { Exception => :never }.freeze
9
- EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
10
- private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
11
-
12
- # Enters an exclusive section, but allows dependency loading while blocked
13
- def mon_enter
14
- mon_try_enter ||
15
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
16
- end
17
-
18
- def synchronize(&block)
19
- Thread.handle_interrupt(EXCEPTION_NEVER) do
20
- mon_enter
21
-
22
- begin
23
- Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
24
- ensure
25
- mon_exit
26
- end
27
- end
28
- end
29
- end
30
7
  # A monitor that will permit dependency loading while blocked waiting for
31
8
  # the lock.
32
- class LoadInterlockAwareMonitor < Monitor
33
- include LoadInterlockAwareMonitorMixin
34
- end
35
-
36
- class ThreadLoadInterlockAwareMonitor # :nodoc:
37
- prepend LoadInterlockAwareMonitorMixin
38
-
39
- def initialize
40
- @owner = nil
41
- @count = 0
42
- @mutex = Mutex.new
43
- end
44
-
45
- private
46
- def mon_try_enter
47
- if @owner != Thread.current
48
- return false unless @mutex.try_lock
49
- @owner = Thread.current
50
- end
51
- @count += 1
52
- end
53
-
54
- def mon_enter
55
- @mutex.lock if @owner != Thread.current
56
- @owner = Thread.current
57
- @count += 1
58
- end
59
-
60
- def mon_exit
61
- unless @owner == Thread.current
62
- raise ThreadError, "current thread not owner"
63
- end
64
-
65
- @count -= 1
66
- return unless @count == 0
67
- @owner = nil
68
- @mutex.unlock
69
- end
70
- end
9
+ LoadInterlockAwareMonitor = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
10
+ "ActiveSupport::Concurrency::LoadInterlockAwareMonitor",
11
+ "::Monitor",
12
+ ActiveSupport.deprecator,
13
+ message: "ActiveSupport::Concurrency::LoadInterlockAwareMonitor is deprecated and will be " \
14
+ "removed in Rails 9.0. Use Monitor directly instead, as the loading interlock is " \
15
+ "no longer used."
16
+ )
71
17
  end
72
18
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "thread"
4
3
  require "monitor"
5
4
 
6
5
  module ActiveSupport
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Concurrency
5
+ class ThreadMonitor # :nodoc:
6
+ EXCEPTION_NEVER = { Exception => :never }.freeze
7
+ EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
8
+ private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
9
+
10
+ def initialize
11
+ @owner = nil
12
+ @count = 0
13
+ @mutex = Mutex.new
14
+ end
15
+
16
+ def synchronize(&block)
17
+ Thread.handle_interrupt(EXCEPTION_NEVER) do
18
+ mon_enter
19
+
20
+ begin
21
+ Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
22
+ ensure
23
+ mon_exit
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+ def mon_try_enter
30
+ if @owner != Thread.current
31
+ return false unless @mutex.try_lock
32
+ @owner = Thread.current
33
+ end
34
+ @count += 1
35
+ end
36
+
37
+ def mon_enter
38
+ @mutex.lock if @owner != Thread.current
39
+ @owner = Thread.current
40
+ @count += 1
41
+ end
42
+
43
+ def mon_exit
44
+ unless @owner == Thread.current
45
+ raise ThreadError, "current thread not owner"
46
+ end
47
+
48
+ @count -= 1
49
+ return unless @count == 0
50
+ @owner = nil
51
+ @mutex.unlock
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ActiveSupport.deprecator.warn <<~MSG
4
+ ActiveSupport::Configurable is deprecated without replacement, and will be removed in Rails 8.2.
5
+
6
+ You can emulate the previous behavior with `class_attribute`.
7
+ MSG
8
+
3
9
  require "active_support/concern"
4
10
  require "active_support/ordered_options"
5
11
 
@@ -27,6 +33,19 @@ module ActiveSupport
27
33
  end
28
34
 
29
35
  module ClassMethods
36
+ # Reads and writes attributes from a configuration OrderedOptions.
37
+ #
38
+ # require "active_support/configurable"
39
+ #
40
+ # class User
41
+ # include ActiveSupport::Configurable
42
+ # end
43
+ #
44
+ # User.config.allowed_access = true
45
+ # User.config.level = 1
46
+ #
47
+ # User.config.allowed_access # => true
48
+ # User.config.level # => 1
30
49
  def config
31
50
  @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
32
51
  superclass.config.inheritable_copy
@@ -36,6 +55,21 @@ module ActiveSupport
36
55
  end
37
56
  end
38
57
 
58
+ # Configure values from within the passed block.
59
+ #
60
+ # require "active_support/configurable"
61
+ #
62
+ # class User
63
+ # include ActiveSupport::Configurable
64
+ # end
65
+ #
66
+ # User.allowed_access # => nil
67
+ #
68
+ # User.configure do |config|
69
+ # config.allowed_access = true
70
+ # end
71
+ #
72
+ # User.allowed_access # => true
39
73
  def configure
40
74
  yield config
41
75
  end
@@ -19,11 +19,20 @@ module ActiveSupport
19
19
  end
20
20
 
21
21
  def parse(context: nil, **options)
22
- source = render(context)
23
- if YAML.respond_to?(:unsafe_load)
24
- YAML.unsafe_load(source, **options) || {}
22
+ source = @content.include?("<%") ? render(context) : @content
23
+
24
+ if source == @content
25
+ if YAML.respond_to?(:unsafe_load)
26
+ YAML.unsafe_load_file(@content_path, **options) || {}
27
+ else
28
+ YAML.load_file(@content_path, **options) || {}
29
+ end
25
30
  else
26
- YAML.load(source, **options) || {}
31
+ if YAML.respond_to?(:unsafe_load)
32
+ YAML.unsafe_load(source, **options) || {}
33
+ else
34
+ YAML.load(source, **options) || {}
35
+ end
27
36
  end
28
37
  rescue Psych::SyntaxError => error
29
38
  raise "YAML syntax error occurred while parsing #{@content_path}. " \
@@ -33,8 +42,7 @@ module ActiveSupport
33
42
 
34
43
  private
35
44
  def read(content_path)
36
- require "yaml"
37
- require "erb"
45
+ require "yaml" unless defined?(YAML)
38
46
 
39
47
  File.read(content_path).tap do |content|
40
48
  if content.include?("\u00A0")
@@ -44,6 +52,7 @@ module ActiveSupport
44
52
  end
45
53
 
46
54
  def render(context)
55
+ require "erb" unless defined?(ERB)
47
56
  erb = ERB.new(@content).tap { |e| e.filename = @content_path }
48
57
  context ? erb.result(context) : erb.result
49
58
  end