activesupport 7.2.3.1 → 8.1.4.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +584 -234
  3. data/lib/active_support/backtrace_cleaner.rb +72 -1
  4. data/lib/active_support/benchmark.rb +21 -0
  5. data/lib/active_support/benchmarkable.rb +3 -2
  6. data/lib/active_support/cache/file_store.rb +13 -2
  7. data/lib/active_support/cache/mem_cache_store.rb +17 -14
  8. data/lib/active_support/cache/memory_store.rb +9 -3
  9. data/lib/active_support/cache/redis_cache_store.rb +51 -34
  10. data/lib/active_support/cache/strategy/local_cache.rb +17 -8
  11. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  12. data/lib/active_support/cache.rb +86 -18
  13. data/lib/active_support/callbacks.rb +25 -11
  14. data/lib/active_support/class_attribute.rb +33 -0
  15. data/lib/active_support/code_generator.rb +9 -0
  16. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
  17. data/lib/active_support/concurrency/share_lock.rb +0 -1
  18. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  19. data/lib/active_support/configurable.rb +34 -0
  20. data/lib/active_support/configuration_file.rb +15 -6
  21. data/lib/active_support/continuous_integration.rb +145 -0
  22. data/lib/active_support/core_ext/array/conversions.rb +3 -3
  23. data/lib/active_support/core_ext/array.rb +7 -7
  24. data/lib/active_support/core_ext/benchmark.rb +0 -16
  25. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  26. data/lib/active_support/core_ext/class/attribute.rb +24 -18
  27. data/lib/active_support/core_ext/class.rb +2 -2
  28. data/lib/active_support/core_ext/date/conversions.rb +2 -0
  29. data/lib/active_support/core_ext/date.rb +5 -5
  30. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
  31. data/lib/active_support/core_ext/date_time/calculations.rb +1 -0
  32. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  33. data/lib/active_support/core_ext/date_time.rb +5 -5
  34. data/lib/active_support/core_ext/digest.rb +1 -1
  35. data/lib/active_support/core_ext/enumerable.rb +8 -3
  36. data/lib/active_support/core_ext/erb/util.rb +3 -3
  37. data/lib/active_support/core_ext/file.rb +1 -1
  38. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
  39. data/lib/active_support/core_ext/hash/except.rb +0 -12
  40. data/lib/active_support/core_ext/hash.rb +8 -8
  41. data/lib/active_support/core_ext/integer.rb +3 -3
  42. data/lib/active_support/core_ext/kernel.rb +3 -3
  43. data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
  44. data/lib/active_support/core_ext/module.rb +11 -11
  45. data/lib/active_support/core_ext/numeric.rb +3 -3
  46. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  47. data/lib/active_support/core_ext/object/json.rb +24 -11
  48. data/lib/active_support/core_ext/object/to_query.rb +7 -1
  49. data/lib/active_support/core_ext/object.rb +13 -13
  50. data/lib/active_support/core_ext/pathname.rb +2 -2
  51. data/lib/active_support/core_ext/range/compare_range.rb +2 -14
  52. data/lib/active_support/core_ext/range/overlap.rb +3 -3
  53. data/lib/active_support/core_ext/range/sole.rb +2 -0
  54. data/lib/active_support/core_ext/range.rb +4 -5
  55. data/lib/active_support/core_ext/string/filters.rb +2 -0
  56. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  57. data/lib/active_support/core_ext/string/multibyte.rb +10 -1
  58. data/lib/active_support/core_ext/string/output_safety.rb +27 -13
  59. data/lib/active_support/core_ext/string.rb +13 -13
  60. data/lib/active_support/core_ext/symbol.rb +1 -1
  61. data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
  62. data/lib/active_support/core_ext/time/calculations.rb +11 -2
  63. data/lib/active_support/core_ext/time/compatibility.rb +2 -27
  64. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  65. data/lib/active_support/core_ext/time.rb +5 -5
  66. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  67. data/lib/active_support/current_attributes.rb +13 -10
  68. data/lib/active_support/delegation.rb +25 -44
  69. data/lib/active_support/dependencies/interlock.rb +11 -5
  70. data/lib/active_support/dependencies.rb +6 -2
  71. data/lib/active_support/deprecation/reporting.rb +4 -21
  72. data/lib/active_support/deprecation.rb +1 -1
  73. data/lib/active_support/duration.rb +20 -16
  74. data/lib/active_support/editor.rb +70 -0
  75. data/lib/active_support/encrypted_configuration.rb +20 -2
  76. data/lib/active_support/error_reporter.rb +76 -2
  77. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  78. data/lib/active_support/event_reporter.rb +592 -0
  79. data/lib/active_support/evented_file_update_checker.rb +5 -2
  80. data/lib/active_support/execution_context.rb +75 -7
  81. data/lib/active_support/file_update_checker.rb +7 -5
  82. data/lib/active_support/gem_version.rb +3 -3
  83. data/lib/active_support/gzip.rb +1 -0
  84. data/lib/active_support/hash_with_indifferent_access.rb +44 -24
  85. data/lib/active_support/i18n_railtie.rb +19 -11
  86. data/lib/active_support/inflector/inflections.rb +34 -16
  87. data/lib/active_support/inflector/methods.rb +6 -22
  88. data/lib/active_support/inflector/transliterate.rb +18 -10
  89. data/lib/active_support/isolated_execution_state.rb +17 -17
  90. data/lib/active_support/json/decoding.rb +9 -3
  91. data/lib/active_support/json/encoding.rb +149 -19
  92. data/lib/active_support/log_subscriber.rb +2 -6
  93. data/lib/active_support/logger_thread_safe_level.rb +6 -3
  94. data/lib/active_support/message_encryptors.rb +52 -0
  95. data/lib/active_support/message_pack/extensions.rb +23 -1
  96. data/lib/active_support/message_verifiers.rb +52 -0
  97. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  98. data/lib/active_support/messages/rotator.rb +5 -0
  99. data/lib/active_support/messages/serializer_with_fallback.rb +1 -1
  100. data/lib/active_support/multibyte/chars.rb +8 -1
  101. data/lib/active_support/multibyte.rb +4 -0
  102. data/lib/active_support/notifications/fanout.rb +64 -43
  103. data/lib/active_support/notifications/instrumenter.rb +1 -1
  104. data/lib/active_support/notifications.rb +2 -2
  105. data/lib/active_support/number_helper/number_converter.rb +4 -1
  106. data/lib/active_support/number_helper/number_to_currency_converter.rb +1 -1
  107. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -4
  108. data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -7
  109. data/lib/active_support/number_helper/rounding_helper.rb +1 -1
  110. data/lib/active_support/number_helper.rb +22 -0
  111. data/lib/active_support/ordered_options.rb +2 -2
  112. data/lib/active_support/railtie.rb +32 -9
  113. data/lib/active_support/security_utils.rb +4 -5
  114. data/lib/active_support/structured_event_subscriber.rb +99 -0
  115. data/lib/active_support/subscriber.rb +0 -5
  116. data/lib/active_support/syntax_error_proxy.rb +9 -1
  117. data/lib/active_support/tagged_logging.rb +5 -0
  118. data/lib/active_support/test_case.rb +67 -6
  119. data/lib/active_support/testing/assertions.rb +118 -27
  120. data/lib/active_support/testing/autorun.rb +5 -0
  121. data/lib/active_support/testing/error_reporter_assertions.rb +17 -0
  122. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  123. data/lib/active_support/testing/isolation.rb +0 -2
  124. data/lib/active_support/testing/notification_assertions.rb +92 -0
  125. data/lib/active_support/testing/parallelization/worker.rb +7 -1
  126. data/lib/active_support/testing/parallelization.rb +17 -0
  127. data/lib/active_support/testing/tests_without_assertions.rb +1 -1
  128. data/lib/active_support/testing/time_helpers.rb +9 -4
  129. data/lib/active_support/time_with_zone.rb +36 -23
  130. data/lib/active_support/values/time_zone.rb +22 -13
  131. data/lib/active_support/xml_mini.rb +1 -2
  132. data/lib/active_support.rb +21 -9
  133. metadata +33 -17
  134. data/lib/active_support/core_ext/range/each.rb +0 -24
  135. data/lib/active_support/proxy_object.rb +0 -20
@@ -36,6 +36,7 @@ module ActiveSupport
36
36
  :serializer,
37
37
  :skip_nil,
38
38
  :raw,
39
+ :max_key_size,
39
40
  ]
40
41
 
41
42
  # Mapping of canonical option names to aliases that a store will recognize.
@@ -187,6 +188,12 @@ module ActiveSupport
187
188
  # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
188
189
  #
189
190
  class Store
191
+ # Default +ConnectionPool+ options
192
+ DEFAULT_POOL_OPTIONS = { size: 5, timeout: 5 }.freeze
193
+
194
+ # Keys are truncated with the Active Support digest if they exceed the limit.
195
+ MAX_KEY_SIZE = 250
196
+
190
197
  cattr_accessor :logger, instance_writer: true
191
198
  cattr_accessor :raise_on_invalid_cache_expiration_time, default: false
192
199
 
@@ -195,9 +202,6 @@ module ActiveSupport
195
202
 
196
203
  class << self
197
204
  private
198
- DEFAULT_POOL_OPTIONS = { size: 5, timeout: 5 }.freeze
199
- private_constant :DEFAULT_POOL_OPTIONS
200
-
201
205
  def retrieve_pool_options(options)
202
206
  if options.key?(:pool)
203
207
  pool_options = options.delete(:pool)
@@ -287,7 +291,7 @@ module ActiveSupport
287
291
  # <tt>coder: nil</tt> to avoid the overhead of safeguarding against
288
292
  # mutation.
289
293
  #
290
- # The +:coder+ option is mutally exclusive with the +:serializer+ and
294
+ # The +:coder+ option is mutually exclusive with the +:serializer+ and
291
295
  # +:compressor+ options. Specifying them together will raise an
292
296
  # +ArgumentError+.
293
297
  #
@@ -299,6 +303,9 @@ module ActiveSupport
299
303
  @options[:compress] = true unless @options.key?(:compress)
300
304
  @options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT
301
305
 
306
+ @max_key_size = @options.delete(:max_key_size)
307
+ @max_key_size = MAX_KEY_SIZE if @max_key_size.nil? # allow 'false' as a value
308
+
302
309
  @coder = @options.delete(:coder) do
303
310
  legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer]
304
311
  serializer = @options.delete(:serializer) || default_serializer
@@ -420,7 +427,7 @@ module ActiveSupport
420
427
  # t1.join
421
428
  #
422
429
  # p val_1 # => "new value 1"
423
- # p val_2 # => "oritinal value"
430
+ # p val_2 # => "original value"
424
431
  # p cache.fetch("foo") # => "new value 1"
425
432
  #
426
433
  # # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
@@ -539,10 +546,11 @@ module ActiveSupport
539
546
 
540
547
  options = names.extract_options!
541
548
  options = merged_options(options)
549
+ keys = names.map { |name| normalize_key(name, options) }
542
550
 
543
- instrument_multi :read_multi, names, options do |payload|
551
+ instrument_multi :read_multi, keys, options do |payload|
544
552
  read_multi_entries(names, **options, event: payload).tap do |results|
545
- payload[:hits] = results.keys
553
+ payload[:hits] = results.keys.map { |name| normalize_key(name, options) }
546
554
  end
547
555
  end
548
556
  end
@@ -552,10 +560,11 @@ module ActiveSupport
552
560
  return hash if hash.empty?
553
561
 
554
562
  options = merged_options(options)
563
+ normalized_hash = hash.transform_keys { |key| normalize_key(key, options) }
555
564
 
556
- instrument_multi :write_multi, hash, options do |payload|
565
+ instrument_multi :write_multi, normalized_hash, options do |payload|
557
566
  entries = hash.each_with_object({}) do |(name, value), memo|
558
- memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
567
+ memo[normalize_key(name, options)] = Entry.new(value, **options, version: normalize_version(name, options))
559
568
  end
560
569
 
561
570
  write_multi_entries entries, **options
@@ -597,9 +606,9 @@ module ActiveSupport
597
606
 
598
607
  options = names.extract_options!
599
608
  options = merged_options(options)
600
-
609
+ keys = names.map { |name| normalize_key(name, options) }
601
610
  writes = {}
602
- ordered = instrument_multi :read_multi, names, options do |payload|
611
+ ordered = instrument_multi :read_multi, keys, options do |payload|
603
612
  if options[:force]
604
613
  reads = {}
605
614
  else
@@ -611,7 +620,7 @@ module ActiveSupport
611
620
  end
612
621
  writes.compact! if options[:skip_nil]
613
622
 
614
- payload[:hits] = reads.keys
623
+ payload[:hits] = reads.keys.map { |name| normalize_key(name, options) }
615
624
  payload[:super_operation] = :fetch_multi
616
625
 
617
626
  ordered
@@ -657,13 +666,15 @@ module ActiveSupport
657
666
  # version, the read will be treated as a cache miss. This feature is
658
667
  # used to support recyclable cache keys.
659
668
  #
669
+ # * +:unless_exist+ - Prevents overwriting an existing cache entry.
670
+ #
660
671
  # Other options will be handled by the specific cache store implementation.
661
672
  def write(name, value, options = nil)
662
673
  options = merged_options(options)
663
674
  key = normalize_key(name, options)
664
675
 
665
676
  instrument(:write, key, options) do
666
- entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
677
+ entry = Entry.new(value, **options, version: normalize_version(name, options))
667
678
  write_entry(key, entry, **options)
668
679
  end
669
680
  end
@@ -689,7 +700,7 @@ module ActiveSupport
689
700
  return 0 if names.empty?
690
701
 
691
702
  options = merged_options(options)
692
- names.map! { |key| normalize_key(key, options) }
703
+ names = names.map { |key| normalize_key(key, options) }
693
704
 
694
705
  instrument_multi(:delete_multi, names, options) do
695
706
  delete_multi_entries(names, **options)
@@ -740,6 +751,32 @@ module ActiveSupport
740
751
  raise NotImplementedError.new("#{self.class.name} does not support decrement")
741
752
  end
742
753
 
754
+ # Reads a counter that was set by #increment / #decrement.
755
+ #
756
+ # cache.write_counter("foo", 1)
757
+ # cache.read_counter("foo") # => 1
758
+ # cache.increment("foo")
759
+ # cache.read_counter("foo") # => 2
760
+ #
761
+ # Options are passed to the underlying cache implementation.
762
+ def read_counter(name, **options)
763
+ options = merged_options(options).merge(raw: true)
764
+ read(name, **options)&.to_i
765
+ end
766
+
767
+ # Writes a counter that can then be modified by #increment / #decrement.
768
+ #
769
+ # cache.write_counter("foo", 1)
770
+ # cache.read_counter("foo") # => 1
771
+ # cache.increment("foo")
772
+ # cache.read_counter("foo") # => 2
773
+ #
774
+ # Options are passed to the underlying cache implementation.
775
+ def write_counter(name, value, **options)
776
+ options = merged_options(options).merge(raw: true)
777
+ write(name, value.to_i, **options)
778
+ end
779
+
743
780
  # Cleans up the cache by removing expired entries.
744
781
  #
745
782
  # Options are passed to the underlying cache implementation.
@@ -759,6 +796,17 @@ module ActiveSupport
759
796
  raise NotImplementedError.new("#{self.class.name} does not support clear")
760
797
  end
761
798
 
799
+ # Get the current namespace
800
+ def namespace
801
+ @options[:namespace]
802
+ end
803
+
804
+ # Set the current namespace. Note, this will be ignored if custom
805
+ # options are passed to cache wills with a namespace key.
806
+ def namespace=(namespace)
807
+ @options[:namespace] = namespace
808
+ end
809
+
762
810
  private
763
811
  def default_serializer
764
812
  case Cache.format_version
@@ -925,16 +973,33 @@ module ActiveSupport
925
973
  options
926
974
  end
927
975
 
928
- # Expands and namespaces the cache key.
976
+ # Expands, namespaces and truncates the cache key.
929
977
  # Raises an exception when the key is +nil+ or an empty string.
930
978
  # May be overridden by cache stores to do additional normalization.
931
979
  def normalize_key(key, options = nil)
980
+ key = expand_and_namespace_key(key, options)
981
+ truncate_key(key)
982
+ end
983
+
984
+ def expand_and_namespace_key(key, options = nil)
932
985
  str_key = expanded_key(key)
933
986
  raise(ArgumentError, "key cannot be blank") if !str_key || str_key.empty?
934
987
 
935
988
  namespace_key str_key, options
936
989
  end
937
990
 
991
+ def truncate_key(key)
992
+ if key && @max_key_size && key.bytesize > @max_key_size
993
+ suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}"
994
+ truncate_at = @max_key_size - suffix.bytesize
995
+ key = key.byteslice(0, truncate_at)
996
+ key.scrub!("")
997
+ "#{key}#{suffix}"
998
+ else
999
+ key
1000
+ end
1001
+ end
1002
+
938
1003
  # Prefix the key with a namespace string:
939
1004
  #
940
1005
  # namespace_key 'foo', namespace: 'cache'
@@ -944,9 +1009,12 @@ module ActiveSupport
944
1009
  #
945
1010
  # namespace_key 'foo', namespace: -> { 'cache' }
946
1011
  # # => 'cache:foo'
947
- def namespace_key(key, options = nil)
948
- options = merged_options(options)
949
- namespace = options[:namespace]
1012
+ def namespace_key(key, call_options = nil)
1013
+ namespace = if call_options&.key?(:namespace)
1014
+ call_options[:namespace]
1015
+ else
1016
+ options[:namespace]
1017
+ end
950
1018
 
951
1019
  if namespace.respond_to?(:call)
952
1020
  namespace = namespace.call
@@ -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
@@ -574,7 +574,7 @@ module ActiveSupport
574
574
  @name = name
575
575
  @config = {
576
576
  scope: [:kind],
577
- terminator: default_terminator
577
+ terminator: DEFAULT_TERMINATOR
578
578
  }.merge!(config)
579
579
  @chain = []
580
580
  @all_callbacks = nil
@@ -662,8 +662,8 @@ module ActiveSupport
662
662
  @chain.delete_if { |c| callback.duplicates?(c) }
663
663
  end
664
664
 
665
- def default_terminator
666
- Proc.new do |target, result_lambda|
665
+ class DefaultTerminator # :nodoc:
666
+ def call(target, result_lambda)
667
667
  terminate = true
668
668
  catch(:abort) do
669
669
  result_lambda.call
@@ -672,6 +672,7 @@ module ActiveSupport
672
672
  terminate
673
673
  end
674
674
  end
675
+ DEFAULT_TERMINATOR = DefaultTerminator.new.freeze
675
676
  end
676
677
 
677
678
  module ClassMethods
@@ -905,12 +906,13 @@ module ActiveSupport
905
906
  names.each do |name|
906
907
  name = name.to_sym
907
908
 
908
- ([self] + self.descendants).each do |target|
909
- target.set_callbacks name, CallbackChain.new(name, options)
910
- end
911
-
912
909
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
913
- 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)
914
916
  run_callbacks #{name.inspect}, &block
915
917
  end
916
918
 
@@ -926,6 +928,10 @@ module ActiveSupport
926
928
  __callbacks[#{name.inspect}]
927
929
  end
928
930
  RUBY
931
+
932
+ ([self] + self.descendants).each do |target|
933
+ target.set_callbacks name, CallbackChain.new(name, options)
934
+ end
929
935
  end
930
936
  end
931
937
 
@@ -935,9 +941,17 @@ module ActiveSupport
935
941
  end
936
942
 
937
943
  def set_callbacks(name, callbacks) # :nodoc:
938
- 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)
939
948
  self.__callbacks = __callbacks.dup
940
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
941
955
  self.__callbacks[name.to_sym] = callbacks
942
956
  self.__callbacks
943
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
+ The previous behavior can be emulated with `attr_accessor`s, `class_attribute`s, or combinations of `class_attribute` and `InheritableOptions` depending on requirements.
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.config.allowed_access # => nil
67
+ #
68
+ # User.configure do |config|
69
+ # config.allowed_access = true
70
+ # end
71
+ #
72
+ # User.config.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