activesupport 7.0.8.7 → 7.1.5.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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +995 -294
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +30 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +37 -10
  14. data/lib/active_support/cache/mem_cache_store.rb +100 -76
  15. data/lib/active_support/cache/memory_store.rb +78 -24
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +151 -141
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +29 -14
  20. data/lib/active_support/cache.rb +333 -253
  21. data/lib/active_support/callbacks.rb +44 -21
  22. data/lib/active_support/code_generator.rb +15 -10
  23. data/lib/active_support/concern.rb +4 -2
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/configurable.rb +10 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  28. data/lib/active_support/core_ext/array.rb +0 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date.rb +0 -1
  32. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  33. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  34. data/lib/active_support/core_ext/date_time.rb +0 -1
  35. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  36. data/lib/active_support/core_ext/enumerable.rb +3 -75
  37. data/lib/active_support/core_ext/erb/util.rb +196 -0
  38. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  39. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  40. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  42. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  43. data/lib/active_support/core_ext/module/delegation.rb +81 -37
  44. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  45. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  46. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  47. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  48. data/lib/active_support/core_ext/numeric.rb +0 -1
  49. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  50. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  51. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  52. data/lib/active_support/core_ext/object/json.rb +16 -6
  53. data/lib/active_support/core_ext/object/with.rb +44 -0
  54. data/lib/active_support/core_ext/object/with_options.rb +4 -4
  55. data/lib/active_support/core_ext/object.rb +1 -0
  56. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  57. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  58. data/lib/active_support/core_ext/pathname.rb +1 -0
  59. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  60. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  61. data/lib/active_support/core_ext/range.rb +1 -2
  62. data/lib/active_support/core_ext/securerandom.rb +24 -12
  63. data/lib/active_support/core_ext/string/filters.rb +20 -14
  64. data/lib/active_support/core_ext/string/indent.rb +1 -1
  65. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  66. data/lib/active_support/core_ext/string/output_safety.rb +38 -174
  67. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  68. data/lib/active_support/core_ext/time/calculations.rb +18 -2
  69. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  70. data/lib/active_support/core_ext/time/zones.rb +4 -4
  71. data/lib/active_support/core_ext/time.rb +0 -1
  72. data/lib/active_support/current_attributes.rb +15 -6
  73. data/lib/active_support/deep_mergeable.rb +53 -0
  74. data/lib/active_support/dependencies/autoload.rb +17 -12
  75. data/lib/active_support/deprecation/behaviors.rb +65 -42
  76. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  77. data/lib/active_support/deprecation/deprecators.rb +104 -0
  78. data/lib/active_support/deprecation/disallowed.rb +3 -5
  79. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  80. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  81. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  82. data/lib/active_support/deprecation/reporting.rb +43 -26
  83. data/lib/active_support/deprecation.rb +32 -5
  84. data/lib/active_support/deprecator.rb +7 -0
  85. data/lib/active_support/descendants_tracker.rb +104 -132
  86. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  87. data/lib/active_support/duration.rb +2 -1
  88. data/lib/active_support/encrypted_configuration.rb +30 -9
  89. data/lib/active_support/encrypted_file.rb +8 -3
  90. data/lib/active_support/environment_inquirer.rb +22 -2
  91. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  92. data/lib/active_support/error_reporter.rb +121 -35
  93. data/lib/active_support/execution_wrapper.rb +4 -4
  94. data/lib/active_support/file_update_checker.rb +4 -2
  95. data/lib/active_support/fork_tracker.rb +10 -2
  96. data/lib/active_support/gem_version.rb +4 -4
  97. data/lib/active_support/gzip.rb +2 -0
  98. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  99. data/lib/active_support/html_safe_translation.rb +16 -6
  100. data/lib/active_support/i18n.rb +1 -1
  101. data/lib/active_support/i18n_railtie.rb +20 -13
  102. data/lib/active_support/inflector/inflections.rb +2 -0
  103. data/lib/active_support/inflector/methods.rb +23 -11
  104. data/lib/active_support/inflector/transliterate.rb +3 -1
  105. data/lib/active_support/isolated_execution_state.rb +26 -22
  106. data/lib/active_support/json/decoding.rb +2 -1
  107. data/lib/active_support/json/encoding.rb +25 -43
  108. data/lib/active_support/key_generator.rb +9 -1
  109. data/lib/active_support/lazy_load_hooks.rb +6 -4
  110. data/lib/active_support/locale/en.yml +2 -0
  111. data/lib/active_support/log_subscriber.rb +85 -33
  112. data/lib/active_support/logger.rb +9 -60
  113. data/lib/active_support/logger_thread_safe_level.rb +10 -24
  114. data/lib/active_support/message_encryptor.rb +197 -53
  115. data/lib/active_support/message_encryptors.rb +141 -0
  116. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  117. data/lib/active_support/message_pack/extensions.rb +292 -0
  118. data/lib/active_support/message_pack/serializer.rb +63 -0
  119. data/lib/active_support/message_pack.rb +50 -0
  120. data/lib/active_support/message_verifier.rb +212 -93
  121. data/lib/active_support/message_verifiers.rb +135 -0
  122. data/lib/active_support/messages/codec.rb +65 -0
  123. data/lib/active_support/messages/metadata.rb +111 -45
  124. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  125. data/lib/active_support/messages/rotator.rb +34 -32
  126. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  127. data/lib/active_support/multibyte/chars.rb +2 -0
  128. data/lib/active_support/multibyte/unicode.rb +9 -37
  129. data/lib/active_support/notifications/fanout.rb +245 -81
  130. data/lib/active_support/notifications/instrumenter.rb +87 -22
  131. data/lib/active_support/notifications.rb +1 -1
  132. data/lib/active_support/number_helper/number_converter.rb +14 -5
  133. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  134. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  135. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  136. data/lib/active_support/number_helper.rb +379 -318
  137. data/lib/active_support/ordered_hash.rb +3 -3
  138. data/lib/active_support/ordered_options.rb +14 -0
  139. data/lib/active_support/parameter_filter.rb +84 -69
  140. data/lib/active_support/proxy_object.rb +2 -0
  141. data/lib/active_support/railtie.rb +33 -21
  142. data/lib/active_support/reloader.rb +12 -4
  143. data/lib/active_support/rescuable.rb +2 -0
  144. data/lib/active_support/secure_compare_rotator.rb +16 -9
  145. data/lib/active_support/string_inquirer.rb +3 -1
  146. data/lib/active_support/subscriber.rb +9 -27
  147. data/lib/active_support/syntax_error_proxy.rb +60 -0
  148. data/lib/active_support/tagged_logging.rb +64 -24
  149. data/lib/active_support/test_case.rb +153 -6
  150. data/lib/active_support/testing/assertions.rb +26 -10
  151. data/lib/active_support/testing/autorun.rb +0 -2
  152. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  153. data/lib/active_support/testing/deprecation.rb +25 -25
  154. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  155. data/lib/active_support/testing/isolation.rb +1 -1
  156. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  157. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  158. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  159. data/lib/active_support/testing/stream.rb +1 -1
  160. data/lib/active_support/testing/strict_warnings.rb +39 -0
  161. data/lib/active_support/testing/time_helpers.rb +37 -15
  162. data/lib/active_support/time_with_zone.rb +4 -14
  163. data/lib/active_support/values/time_zone.rb +18 -7
  164. data/lib/active_support/version.rb +1 -1
  165. data/lib/active_support/xml_mini/jdom.rb +3 -10
  166. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  167. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  168. data/lib/active_support/xml_mini/rexml.rb +1 -1
  169. data/lib/active_support/xml_mini.rb +2 -2
  170. data/lib/active_support.rb +14 -3
  171. metadata +143 -14
  172. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  173. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  174. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  175. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  176. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  177. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  178. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  179. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  180. data/lib/active_support/core_ext/uri.rb +0 -5
  181. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -11,34 +11,50 @@ module ActiveSupport
11
11
  attr_accessor :gem_name
12
12
 
13
13
  # Outputs a deprecation warning to the output configured by
14
- # <tt>ActiveSupport::Deprecation.behavior</tt>.
14
+ # ActiveSupport::Deprecation#behavior.
15
15
  #
16
- # ActiveSupport::Deprecation.warn('something broke!')
16
+ # ActiveSupport::Deprecation.new.warn('something broke!')
17
17
  # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
18
18
  def warn(message = nil, callstack = nil)
19
19
  return if silenced
20
20
 
21
21
  callstack ||= caller_locations(2)
22
- deprecation_message(callstack, message).tap do |m|
22
+ deprecation_message(callstack, message).tap do |full_message|
23
23
  if deprecation_disallowed?(message)
24
- disallowed_behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
24
+ disallowed_behavior.each { |b| b.call(full_message, callstack, self) }
25
25
  else
26
- behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
26
+ behavior.each { |b| b.call(full_message, callstack, self) }
27
27
  end
28
28
  end
29
29
  end
30
30
 
31
31
  # Silence deprecation warnings within the block.
32
32
  #
33
- # ActiveSupport::Deprecation.warn('something broke!')
33
+ # deprecator = ActiveSupport::Deprecation.new
34
+ # deprecator.warn('something broke!')
34
35
  # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
35
36
  #
36
- # ActiveSupport::Deprecation.silence do
37
- # ActiveSupport::Deprecation.warn('something broke!')
37
+ # deprecator.silence do
38
+ # deprecator.warn('something broke!')
38
39
  # end
39
40
  # # => nil
40
41
  def silence(&block)
41
- @silenced_thread.bind(true, &block)
42
+ begin_silence
43
+ block.call
44
+ ensure
45
+ end_silence
46
+ end
47
+
48
+ def begin_silence # :nodoc:
49
+ @silence_counter.value += 1
50
+ end
51
+
52
+ def end_silence # :nodoc:
53
+ @silence_counter.value -= 1
54
+ end
55
+
56
+ def silenced
57
+ @silenced || @silence_counter.value.nonzero?
42
58
  end
43
59
 
44
60
  # Allow previously disallowed deprecation warnings within the block.
@@ -46,27 +62,28 @@ module ActiveSupport
46
62
  # expressions. (Symbols are treated as strings). These are compared against
47
63
  # the text of deprecation warning messages generated within the block.
48
64
  # Matching warnings will be exempt from the rules set by
49
- # +ActiveSupport::Deprecation.disallowed_warnings+
65
+ # ActiveSupport::Deprecation#disallowed_warnings.
50
66
  #
51
67
  # The optional <tt>if:</tt> argument accepts a truthy/falsy value or an object that
52
68
  # responds to <tt>.call</tt>. If truthy, then matching warnings will be allowed.
53
69
  # If falsey then the method yields to the block without allowing the warning.
54
70
  #
55
- # ActiveSupport::Deprecation.disallowed_behavior = :raise
56
- # ActiveSupport::Deprecation.disallowed_warnings = [
71
+ # deprecator = ActiveSupport::Deprecation.new
72
+ # deprecator.disallowed_behavior = :raise
73
+ # deprecator.disallowed_warnings = [
57
74
  # "something broke"
58
75
  # ]
59
76
  #
60
- # ActiveSupport::Deprecation.warn('something broke!')
77
+ # deprecator.warn('something broke!')
61
78
  # # => ActiveSupport::DeprecationException
62
79
  #
63
- # ActiveSupport::Deprecation.allow ['something broke'] do
64
- # ActiveSupport::Deprecation.warn('something broke!')
80
+ # deprecator.allow ['something broke'] do
81
+ # deprecator.warn('something broke!')
65
82
  # end
66
83
  # # => nil
67
84
  #
68
- # ActiveSupport::Deprecation.allow ['something broke'], if: Rails.env.production? do
69
- # ActiveSupport::Deprecation.warn('something broke!')
85
+ # deprecator.allow ['something broke'], if: Rails.env.production? do
86
+ # deprecator.warn('something broke!')
70
87
  # end
71
88
  # # => ActiveSupport::DeprecationException for dev/test, nil for production
72
89
  def allow(allowed_warnings = :all, if: true, &block)
@@ -79,10 +96,6 @@ module ActiveSupport
79
96
  end
80
97
  end
81
98
 
82
- def silenced
83
- @silenced || @silenced_thread.value
84
- end
85
-
86
99
  def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
87
100
  caller_backtrace ||= caller_locations(2)
88
101
  deprecated_method_warning(deprecated_method_name, message).tap do |msg|
@@ -125,10 +138,13 @@ module ActiveSupport
125
138
  end
126
139
 
127
140
  def extract_callstack(callstack)
141
+ return [] if callstack.empty?
128
142
  return _extract_callstack(callstack) if callstack.first.is_a? String
129
143
 
130
144
  offending_line = callstack.find { |frame|
131
- frame.absolute_path && !ignored_callstack(frame.absolute_path)
145
+ # Code generated with `eval` doesn't have an `absolute_path`, e.g. templates.
146
+ path = frame.absolute_path || frame.path
147
+ path && !ignored_callstack?(path)
132
148
  } || callstack.first
133
149
 
134
150
  [offending_line.path, offending_line.lineno, offending_line.label]
@@ -136,7 +152,7 @@ module ActiveSupport
136
152
 
137
153
  def _extract_callstack(callstack)
138
154
  warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
139
- offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first
155
+ offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
140
156
 
141
157
  if offending_line
142
158
  if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
@@ -147,10 +163,11 @@ module ActiveSupport
147
163
  end
148
164
  end
149
165
 
150
- RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
166
+ RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
167
+ LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
151
168
 
152
- def ignored_callstack(path)
153
- path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
169
+ def ignored_callstack?(path)
170
+ path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
154
171
  end
155
172
  end
156
173
  end
@@ -3,8 +3,35 @@
3
3
  require "singleton"
4
4
 
5
5
  module ActiveSupport
6
- # \Deprecation specifies the API used by Rails to deprecate methods, instance
7
- # variables, objects, and constants.
6
+ # = Active Support \Deprecation
7
+ #
8
+ # \Deprecation specifies the API used by \Rails to deprecate methods, instance variables, objects, and constants. It's
9
+ # also available for gems or applications.
10
+ #
11
+ # For a gem, use Deprecation.new to create a Deprecation object and store it in your module or class (in order for
12
+ # users to be able to configure it).
13
+ #
14
+ # module MyLibrary
15
+ # def self.deprecator
16
+ # @deprecator ||= ActiveSupport::Deprecation.new("2.0", "MyLibrary")
17
+ # end
18
+ # end
19
+ #
20
+ # For a Railtie or Engine, you may also want to add it to the application's deprecators, so that the application's
21
+ # configuration can be applied to it.
22
+ #
23
+ # module MyLibrary
24
+ # class Railtie < Rails::Railtie
25
+ # initializer "my_library.deprecator" do |app|
26
+ # app.deprecators[:my_library] = MyLibrary.deprecator
27
+ # end
28
+ # end
29
+ # end
30
+ #
31
+ # With the above initializer, configuration settings like the following will affect +MyLibrary.deprecator+:
32
+ #
33
+ # # in config/environments/test.rb
34
+ # config.active_support.deprecation = :raise
8
35
  class Deprecation
9
36
  # active_support.rb sets an autoload for ActiveSupport::Deprecation.
10
37
  #
@@ -21,10 +48,10 @@ module ActiveSupport
21
48
  require "active_support/deprecation/constant_accessor"
22
49
  require "active_support/deprecation/method_wrappers"
23
50
  require "active_support/deprecation/proxy_wrappers"
51
+ require "active_support/deprecation/deprecators"
24
52
  require "active_support/core_ext/module/deprecation"
25
53
  require "concurrent/atomic/thread_local_var"
26
54
 
27
- include Singleton
28
55
  include InstanceDelegator
29
56
  include Behavior
30
57
  include Reporting
@@ -38,13 +65,13 @@ module ActiveSupport
38
65
  # and the second is a library name.
39
66
  #
40
67
  # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
41
- def initialize(deprecation_horizon = "7.1", gem_name = "Rails")
68
+ def initialize(deprecation_horizon = "7.2", gem_name = "Rails")
42
69
  self.gem_name = gem_name
43
70
  self.deprecation_horizon = deprecation_horizon
44
71
  # By default, warnings are not silenced and debugging is off.
45
72
  self.silenced = false
46
73
  self.debug = false
47
- @silenced_thread = Concurrent::ThreadLocalVar.new(false)
74
+ @silence_counter = Concurrent::ThreadLocalVar.new(0)
48
75
  @explicitly_allowed_warnings = Concurrent::ThreadLocalVar.new(nil)
49
76
  end
50
77
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ def self.deprecator # :nodoc:
5
+ ActiveSupport::Deprecation._instance
6
+ end
7
+ end
@@ -4,174 +4,116 @@ require "weakref"
4
4
  require "active_support/ruby_features"
5
5
 
6
6
  module ActiveSupport
7
+ # = Active Support Descendants Tracker
8
+ #
7
9
  # This module provides an internal implementation to track descendants
8
- # which is faster than iterating through ObjectSpace.
10
+ # which is faster than iterating through +ObjectSpace+.
11
+ #
12
+ # However Ruby 3.1 provide a fast native +Class#subclasses+ method,
13
+ # so if you know your code won't be executed on older rubies, including
14
+ # +ActiveSupport::DescendantsTracker+ does not provide any benefit.
9
15
  module DescendantsTracker
10
- class << self
11
- def direct_descendants(klass)
12
- ActiveSupport::Deprecation.warn(<<~MSG)
13
- ActiveSupport::DescendantsTracker.direct_descendants is deprecated and will be removed in Rails 7.1.
14
- Use ActiveSupport::DescendantsTracker.subclasses instead.
15
- MSG
16
- subclasses(klass)
17
- end
18
- end
19
-
20
16
  @clear_disabled = false
21
17
 
22
- if RubyFeatures::CLASS_SUBCLASSES
23
- @@excluded_descendants = if RUBY_ENGINE == "ruby"
24
- # On MRI `ObjectSpace::WeakMap` keys are weak references.
25
- # So we can simply use WeakMap as a `Set`.
26
- ObjectSpace::WeakMap.new
27
- else
28
- # On TruffleRuby `ObjectSpace::WeakMap` keys are strong references.
29
- # So we use `object_id` as a key and the actual object as a value.
30
- #
31
- # JRuby for now doesn't have Class#descendant, but when it will, it will likely
32
- # have the same WeakMap semantic than Truffle so we future proof this as much as possible.
33
- class WeakSet # :nodoc:
34
- def initialize
35
- @map = ObjectSpace::WeakMap.new
36
- end
18
+ if RUBY_ENGINE == "ruby"
19
+ # On MRI `ObjectSpace::WeakMap` keys are weak references.
20
+ # So we can simply use WeakMap as a `Set`.
21
+ class WeakSet < ObjectSpace::WeakMap # :nodoc:
22
+ alias_method :to_a, :keys
37
23
 
38
- def [](object)
39
- @map.key?(object.object_id)
40
- end
41
-
42
- def []=(object, _present)
43
- @map[object.object_id] = object
44
- end
24
+ def <<(object)
25
+ self[object] = true
45
26
  end
46
- WeakSet.new
47
27
  end
48
-
49
- class << self
50
- def disable_clear! # :nodoc:
51
- unless @clear_disabled
52
- @clear_disabled = true
53
- remove_method(:subclasses)
54
- @@excluded_descendants = nil
55
- end
28
+ else
29
+ # On TruffleRuby `ObjectSpace::WeakMap` keys are strong references.
30
+ # So we use `object_id` as a key and the actual object as a value.
31
+ #
32
+ # JRuby for now doesn't have Class#descendant, but when it will, it will likely
33
+ # have the same WeakMap semantic than Truffle so we future proof this as much as possible.
34
+ class WeakSet # :nodoc:
35
+ def initialize
36
+ @map = ObjectSpace::WeakMap.new
56
37
  end
57
38
 
58
- def subclasses(klass)
59
- klass.subclasses
39
+ def [](object)
40
+ @map.key?(object.object_id)
60
41
  end
42
+ alias_method :include?, :[]
61
43
 
62
- def descendants(klass)
63
- klass.descendants
44
+ def []=(object, _present)
45
+ @map[object.object_id] = object
64
46
  end
65
47
 
66
- def clear(classes) # :nodoc:
67
- raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled
68
-
69
- classes.each do |klass|
70
- @@excluded_descendants[klass] = true
71
- klass.descendants.each do |descendant|
72
- @@excluded_descendants[descendant] = true
73
- end
74
- end
48
+ def to_a
49
+ @map.values
75
50
  end
76
51
 
77
- def native? # :nodoc:
78
- true
52
+ def <<(object)
53
+ self[object] = true
79
54
  end
80
55
  end
56
+ end
57
+ @excluded_descendants = WeakSet.new
81
58
 
59
+ module ReloadedClassesFiltering # :nodoc:
82
60
  def subclasses
83
- subclasses = super
84
- subclasses.reject! { |d| @@excluded_descendants[d] }
85
- subclasses
61
+ DescendantsTracker.reject!(super)
86
62
  end
87
63
 
88
64
  def descendants
89
- subclasses.concat(subclasses.flat_map(&:descendants))
90
- end
91
-
92
- def direct_descendants
93
- ActiveSupport::Deprecation.warn(<<~MSG)
94
- ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1.
95
- Use #subclasses instead.
96
- MSG
97
- subclasses
65
+ DescendantsTracker.reject!(super)
98
66
  end
99
- else
100
- @@direct_descendants = {}
67
+ end
101
68
 
102
- class << self
103
- def disable_clear! # :nodoc:
69
+ class << self
70
+ def disable_clear! # :nodoc:
71
+ unless @clear_disabled
104
72
  @clear_disabled = true
73
+ ReloadedClassesFiltering.remove_method(:subclasses)
74
+ ReloadedClassesFiltering.remove_method(:descendants)
75
+ @excluded_descendants = nil
105
76
  end
77
+ end
106
78
 
107
- def subclasses(klass)
108
- descendants = @@direct_descendants[klass]
109
- descendants ? descendants.to_a : []
110
- end
79
+ def clear(classes) # :nodoc:
80
+ raise "DescendantsTracker.clear was disabled because config.enable_reloading is false" if @clear_disabled
111
81
 
112
- def descendants(klass)
113
- arr = []
114
- accumulate_descendants(klass, arr)
115
- arr
116
- end
117
-
118
- def clear(classes) # :nodoc:
119
- raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled
120
-
121
- @@direct_descendants.each do |klass, direct_descendants_of_klass|
122
- if classes.member?(klass)
123
- @@direct_descendants.delete(klass)
124
- else
125
- direct_descendants_of_klass.reject! do |direct_descendant_of_class|
126
- classes.member?(direct_descendant_of_class)
127
- end
128
- end
82
+ classes.each do |klass|
83
+ @excluded_descendants << klass
84
+ klass.descendants.each do |descendant|
85
+ @excluded_descendants << descendant
129
86
  end
130
87
  end
131
-
132
- def native? # :nodoc:
133
- false
134
- end
135
-
136
- # This is the only method that is not thread safe, but is only ever called
137
- # during the eager loading phase.
138
- def store_inherited(klass, descendant)
139
- (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
140
- end
141
-
142
- private
143
- def accumulate_descendants(klass, acc)
144
- if direct_descendants = @@direct_descendants[klass]
145
- direct_descendants.each do |direct_descendant|
146
- acc << direct_descendant
147
- accumulate_descendants(direct_descendant, acc)
148
- end
149
- end
150
- end
151
88
  end
152
89
 
153
- def inherited(base)
154
- DescendantsTracker.store_inherited(self, base)
155
- super
90
+ def reject!(classes) # :nodoc:
91
+ if @excluded_descendants
92
+ classes.reject! { |d| @excluded_descendants.include?(d) }
93
+ end
94
+ classes
156
95
  end
96
+ end
157
97
 
158
- def direct_descendants
159
- ActiveSupport::Deprecation.warn(<<~MSG)
160
- ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1.
161
- Use #subclasses instead.
162
- MSG
163
- DescendantsTracker.subclasses(self)
164
- end
98
+ if RubyFeatures::CLASS_SUBCLASSES
99
+ class << self
100
+ def subclasses(klass)
101
+ klass.subclasses
102
+ end
165
103
 
166
- def subclasses
167
- DescendantsTracker.subclasses(self)
104
+ def descendants(klass)
105
+ klass.descendants
106
+ end
168
107
  end
169
108
 
170
109
  def descendants
171
- DescendantsTracker.descendants(self)
110
+ subclasses = DescendantsTracker.reject!(self.subclasses)
111
+ subclasses.concat(subclasses.flat_map(&:descendants))
172
112
  end
173
-
113
+ else
174
114
  # DescendantsArray is an array that contains weak references to classes.
115
+ # Note: DescendantsArray is redundant with WeakSet, however WeakSet when used
116
+ # on Ruby 2.7 or 3.0 can trigger a Ruby crash: https://bugs.ruby-lang.org/issues/18928
175
117
  class DescendantsArray # :nodoc:
176
118
  include Enumerable
177
119
 
@@ -179,10 +121,6 @@ module ActiveSupport
179
121
  @refs = []
180
122
  end
181
123
 
182
- def initialize_copy(orig)
183
- @refs = @refs.dup
184
- end
185
-
186
124
  def <<(klass)
187
125
  @refs << WeakRef.new(klass)
188
126
  end
@@ -213,6 +151,40 @@ module ActiveSupport
213
151
  end
214
152
  end
215
153
  end
154
+
155
+ @direct_descendants = {}
156
+
157
+ class << self
158
+ def subclasses(klass)
159
+ descendants = @direct_descendants[klass]
160
+ descendants ? DescendantsTracker.reject!(descendants.to_a) : []
161
+ end
162
+
163
+ def descendants(klass)
164
+ subclasses = self.subclasses(klass)
165
+ subclasses.concat(subclasses.flat_map { |k| descendants(k) })
166
+ end
167
+
168
+ # This is the only method that is not thread safe, but is only ever called
169
+ # during the eager loading phase.
170
+ def store_inherited(klass, descendant) # :nodoc:
171
+ (@direct_descendants[klass] ||= DescendantsArray.new) << descendant
172
+ end
173
+ end
174
+
175
+ def subclasses
176
+ DescendantsTracker.subclasses(self)
177
+ end
178
+
179
+ def descendants
180
+ DescendantsTracker.descendants(self)
181
+ end
182
+
183
+ private
184
+ def inherited(base) # :nodoc:
185
+ DescendantsTracker.store_inherited(self, base)
186
+ super
187
+ end
216
188
  end
217
189
  end
218
190
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/object/blank"
4
-
5
3
  module ActiveSupport
6
4
  class Duration
7
5
  # Serializes duration to string according to ISO 8601 Duration format.
@@ -3,9 +3,10 @@
3
3
  require "active_support/core_ext/array/conversions"
4
4
  require "active_support/core_ext/module/delegation"
5
5
  require "active_support/core_ext/object/acts_like"
6
- require "active_support/core_ext/string/filters"
7
6
 
8
7
  module ActiveSupport
8
+ # = Active Support \Duration
9
+ #
9
10
  # Provides accurate date and time measurements using Date#advance and
10
11
  # Time#advance, respectively. It mainly supports the methods on Numeric.
11
12
  #
@@ -4,9 +4,12 @@ require "yaml"
4
4
  require "active_support/encrypted_file"
5
5
  require "active_support/ordered_options"
6
6
  require "active_support/core_ext/object/inclusion"
7
+ require "active_support/core_ext/hash/keys"
7
8
  require "active_support/core_ext/module/delegation"
8
9
 
9
10
  module ActiveSupport
11
+ # = Encrypted Configuration
12
+ #
10
13
  # Provides convenience methods on top of EncryptedFile to access values stored
11
14
  # as encrypted YAML.
12
15
  #
@@ -30,12 +33,23 @@ module ActiveSupport
30
33
  # # => KeyError
31
34
  #
32
35
  class EncryptedConfiguration < EncryptedFile
33
- delegate :[], :fetch, to: :config
36
+ class InvalidContentError < RuntimeError
37
+ def initialize(content_path)
38
+ super "Invalid YAML in '#{content_path}'."
39
+ end
40
+
41
+ def message
42
+ cause.is_a?(Psych::SyntaxError) ? "#{super}\n\n #{cause.message}" : super
43
+ end
44
+ end
45
+
34
46
  delegate_missing_to :options
35
47
 
36
48
  def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
37
49
  super content_path: config_path, key_path: key_path,
38
50
  env_key: env_key, raise_if_missing_key: raise_if_missing_key
51
+ @config = nil
52
+ @options = nil
39
53
  end
40
54
 
41
55
  # Reads the file and returns the decrypted content. See EncryptedFile#read.
@@ -46,10 +60,8 @@ module ActiveSupport
46
60
  ""
47
61
  end
48
62
 
49
- def write(contents)
50
- deserialize(contents)
51
-
52
- super
63
+ def validate! # :nodoc:
64
+ deserialize(read)
53
65
  end
54
66
 
55
67
  # Returns the decrypted content as a Hash with symbolized keys.
@@ -64,11 +76,15 @@ module ActiveSupport
64
76
  @config ||= deserialize(read).deep_symbolize_keys
65
77
  end
66
78
 
79
+ def inspect # :nodoc:
80
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
81
+ end
82
+
67
83
  private
68
84
  def deep_transform(hash)
69
85
  return hash unless hash.is_a?(Hash)
70
86
 
71
- h = ActiveSupport::InheritableOptions.new
87
+ h = ActiveSupport::OrderedOptions.new
72
88
  hash.each do |k, v|
73
89
  h[k] = deep_transform(v)
74
90
  end
@@ -79,9 +95,14 @@ module ActiveSupport
79
95
  @options ||= deep_transform(config)
80
96
  end
81
97
 
82
- def deserialize(config)
83
- doc = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(config) : YAML.load(config)
84
- doc.presence || {}
98
+ def deserialize(content)
99
+ config = YAML.respond_to?(:unsafe_load) ?
100
+ YAML.unsafe_load(content, filename: content_path) :
101
+ YAML.load(content, filename: content_path)
102
+
103
+ config.presence || {}
104
+ rescue Psych::SyntaxError
105
+ raise InvalidContentError.new(content_path)
85
106
  end
86
107
  end
87
108
  end
@@ -53,6 +53,12 @@ module ActiveSupport
53
53
  read_env_key || read_key_file || handle_missing_key
54
54
  end
55
55
 
56
+ # Returns truthy if #key is truthy. Returns falsy otherwise. Unlike #key,
57
+ # does not raise MissingKeyError when +raise_if_missing_key+ is true.
58
+ def key?
59
+ read_env_key || read_key_file
60
+ end
61
+
56
62
  # Reads the file and returns the decrypted content.
57
63
  #
58
64
  # Raises:
@@ -104,7 +110,7 @@ module ActiveSupport
104
110
  end
105
111
 
106
112
  def encryptor
107
- @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
113
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER, serializer: Marshal)
108
114
  end
109
115
 
110
116
 
@@ -113,8 +119,7 @@ module ActiveSupport
113
119
  end
114
120
 
115
121
  def read_key_file
116
- return @key_file_contents if defined?(@key_file_contents)
117
- @key_file_contents = (key_path.binread.strip if key_path.exist?)
122
+ @key_file_contents ||= (key_path.binread.strip if key_path.exist?)
118
123
  end
119
124
 
120
125
  def handle_missing_key