activesupport 7.1.3.4 → 7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +123 -1084
  3. data/lib/active_support/array_inquirer.rb +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +15 -3
  5. data/lib/active_support/broadcast_logger.rb +5 -4
  6. data/lib/active_support/cache/file_store.rb +15 -10
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -74
  8. data/lib/active_support/cache/memory_store.rb +2 -1
  9. data/lib/active_support/cache/redis_cache_store.rb +16 -13
  10. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  11. data/lib/active_support/cache.rb +61 -68
  12. data/lib/active_support/callbacks.rb +74 -113
  13. data/lib/active_support/code_generator.rb +15 -10
  14. data/lib/active_support/core_ext/array/conversions.rb +0 -2
  15. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  16. data/lib/active_support/core_ext/date/blank.rb +4 -0
  17. data/lib/active_support/core_ext/date/conversions.rb +0 -2
  18. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  19. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  20. data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
  21. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  22. data/lib/active_support/core_ext/erb/util.rb +5 -0
  23. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  24. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  25. data/lib/active_support/core_ext/module/delegation.rb +20 -148
  26. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  27. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  28. data/lib/active_support/core_ext/object/blank.rb +45 -1
  29. data/lib/active_support/core_ext/object/duplicable.rb +24 -15
  30. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  31. data/lib/active_support/core_ext/object/json.rb +1 -1
  32. data/lib/active_support/core_ext/object/with.rb +5 -3
  33. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  34. data/lib/active_support/core_ext/range/overlap.rb +1 -1
  35. data/lib/active_support/core_ext/securerandom.rb +8 -24
  36. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/string/filters.rb +1 -1
  38. data/lib/active_support/core_ext/string/output_safety.rb +0 -7
  39. data/lib/active_support/core_ext/time/calculations.rb +18 -28
  40. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  41. data/lib/active_support/core_ext/time/conversions.rb +0 -2
  42. data/lib/active_support/core_ext.rb +0 -1
  43. data/lib/active_support/current_attributes.rb +34 -40
  44. data/lib/active_support/delegation.rb +202 -0
  45. data/lib/active_support/dependencies/autoload.rb +0 -12
  46. data/lib/active_support/deprecation/constant_accessor.rb +47 -26
  47. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  48. data/lib/active_support/deprecation/reporting.rb +7 -2
  49. data/lib/active_support/deprecation.rb +8 -5
  50. data/lib/active_support/descendants_tracker.rb +9 -87
  51. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  52. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  53. data/lib/active_support/duration.rb +11 -6
  54. data/lib/active_support/error_reporter.rb +41 -3
  55. data/lib/active_support/evented_file_update_checker.rb +0 -1
  56. data/lib/active_support/execution_wrapper.rb +0 -1
  57. data/lib/active_support/file_update_checker.rb +1 -1
  58. data/lib/active_support/fork_tracker.rb +2 -38
  59. data/lib/active_support/gem_version.rb +3 -3
  60. data/lib/active_support/hash_with_indifferent_access.rb +6 -8
  61. data/lib/active_support/html_safe_translation.rb +7 -4
  62. data/lib/active_support/json/encoding.rb +1 -1
  63. data/lib/active_support/log_subscriber.rb +1 -12
  64. data/lib/active_support/logger.rb +15 -2
  65. data/lib/active_support/logger_thread_safe_level.rb +0 -8
  66. data/lib/active_support/message_pack/extensions.rb +15 -2
  67. data/lib/active_support/message_verifier.rb +12 -0
  68. data/lib/active_support/messages/codec.rb +1 -1
  69. data/lib/active_support/multibyte/chars.rb +2 -2
  70. data/lib/active_support/notifications/fanout.rb +4 -7
  71. data/lib/active_support/notifications/instrumenter.rb +32 -21
  72. data/lib/active_support/notifications.rb +28 -27
  73. data/lib/active_support/number_helper/number_converter.rb +2 -2
  74. data/lib/active_support/option_merger.rb +2 -2
  75. data/lib/active_support/ordered_options.rb +53 -15
  76. data/lib/active_support/proxy_object.rb +8 -5
  77. data/lib/active_support/railtie.rb +4 -11
  78. data/lib/active_support/string_inquirer.rb +1 -1
  79. data/lib/active_support/subscriber.rb +1 -0
  80. data/lib/active_support/syntax_error_proxy.rb +1 -11
  81. data/lib/active_support/tagged_logging.rb +4 -1
  82. data/lib/active_support/test_case.rb +3 -1
  83. data/lib/active_support/testing/assertions.rb +4 -4
  84. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  85. data/lib/active_support/testing/deprecation.rb +5 -12
  86. data/lib/active_support/testing/isolation.rb +18 -8
  87. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  88. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  89. data/lib/active_support/testing/strict_warnings.rb +5 -4
  90. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  91. data/lib/active_support/testing/time_helpers.rb +3 -3
  92. data/lib/active_support/time_with_zone.rb +7 -3
  93. data/lib/active_support/values/time_zone.rb +10 -1
  94. data/lib/active_support/xml_mini.rb +11 -2
  95. data/lib/active_support.rb +3 -2
  96. metadata +35 -15
  97. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  98. data/lib/active_support/ruby_features.rb +0 -7
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/callbacks"
4
+ require "active_support/core_ext/object/with"
4
5
  require "active_support/core_ext/enumerable"
5
6
  require "active_support/core_ext/module/delegation"
6
7
 
@@ -101,7 +102,14 @@ module ActiveSupport
101
102
  end
102
103
 
103
104
  # Declares one or more attributes that will be given both class and instance accessor methods.
104
- def attribute(*names)
105
+ #
106
+ # ==== Options
107
+ #
108
+ # * <tt>:default</tt> - The default value for the attributes. If the value
109
+ # is a proc or lambda, it will be called whenever an instance is
110
+ # constructed. Otherwise, the value will be duplicated with +#dup+.
111
+ # Default values are re-assigned when the attributes are reset.
112
+ def attribute(*names, default: nil)
105
113
  invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
106
114
  if invalid_attribute_names.any?
107
115
  raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
@@ -124,22 +132,10 @@ module ActiveSupport
124
132
  end
125
133
  end
126
134
 
127
- ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
128
- names.each do |name|
129
- owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
130
- batch <<
131
- "def #{name}" <<
132
- "instance.#{name}" <<
133
- "end"
134
- end
135
- owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
136
- batch <<
137
- "def #{name}=(value)" <<
138
- "instance.#{name} = value" <<
139
- "end"
140
- end
141
- end
142
- end
135
+ Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
136
+ Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
137
+
138
+ self.defaults = defaults.merge(names.index_with { default })
143
139
  end
144
140
 
145
141
  # Calls this callback before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
@@ -177,25 +173,29 @@ module ActiveSupport
177
173
  @current_instances_key ||= name.to_sym
178
174
  end
179
175
 
180
- def method_missing(name, *args, &block)
181
- # Caches the method definition as a singleton method of the receiver.
182
- #
183
- # By letting #delegate handle it, we avoid an enclosure that'll capture args.
184
- singleton_class.delegate name, to: :instance
185
-
186
- send(name, *args, &block)
176
+ def method_missing(name, ...)
177
+ instance.public_send(name, ...)
187
178
  end
188
- ruby2_keywords(:method_missing)
189
179
 
190
180
  def respond_to_missing?(name, _)
191
- super || instance.respond_to?(name)
181
+ instance.respond_to?(name) || super
182
+ end
183
+
184
+ def method_added(name)
185
+ super
186
+ return if name == :initialize
187
+ return unless public_method_defined?(name)
188
+ return if respond_to?(name, true)
189
+ Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
192
190
  end
193
191
  end
194
192
 
193
+ class_attribute :defaults, instance_writer: false, default: {}.freeze
194
+
195
195
  attr_accessor :attributes
196
196
 
197
197
  def initialize
198
- @attributes = {}
198
+ @attributes = resolve_defaults
199
199
  end
200
200
 
201
201
  # Expose one or more attributes within a block. Old values are returned after the block concludes.
@@ -208,28 +208,22 @@ module ActiveSupport
208
208
  # end
209
209
  # end
210
210
  # end
211
- def set(set_attributes)
212
- old_attributes = compute_attributes(set_attributes.keys)
213
- assign_attributes(set_attributes)
214
- yield
215
- ensure
216
- assign_attributes(old_attributes)
211
+ def set(attributes, &block)
212
+ with(**attributes, &block)
217
213
  end
218
214
 
219
215
  # Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
220
216
  def reset
221
217
  run_callbacks :reset do
222
- self.attributes = {}
218
+ self.attributes = resolve_defaults
223
219
  end
224
220
  end
225
221
 
226
222
  private
227
- def assign_attributes(new_attributes)
228
- new_attributes.each { |key, value| public_send("#{key}=", value) }
229
- end
230
-
231
- def compute_attributes(keys)
232
- keys.index_with { |key| public_send(key) }
223
+ def resolve_defaults
224
+ defaults.transform_values do |value|
225
+ Proc === value ? value.call : value.dup
226
+ end
233
227
  end
234
228
  end
235
229
  end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module ActiveSupport
6
+ # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
7
+ # option is not used.
8
+ class DelegationError < NoMethodError
9
+ class << self
10
+ def nil_target(method_name, target) # :nodoc:
11
+ new("#{method_name} delegated to #{target}, but #{target} is nil")
12
+ end
13
+ end
14
+ end
15
+
16
+ module Delegation # :nodoc:
17
+ RUBY_RESERVED_KEYWORDS = %w(__ENCODING__ __LINE__ __FILE__ alias and BEGIN begin break
18
+ case class def defined? do else elsif END end ensure false for if in module next nil
19
+ not or redo rescue retry return self super then true undef unless until when while yield)
20
+ RESERVED_METHOD_NAMES = (RUBY_RESERVED_KEYWORDS + %w(_ arg args block)).to_set.freeze
21
+
22
+ class << self
23
+ def generate(owner, methods, location: nil, to: nil, prefix: nil, allow_nil: nil, nilable: true, private: nil, as: nil, signature: nil)
24
+ unless to
25
+ raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
26
+ end
27
+
28
+ if prefix == true && /^[^a-z_]/.match?(to)
29
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
30
+ end
31
+
32
+ method_prefix = \
33
+ if prefix
34
+ "#{prefix == true ? to : prefix}_"
35
+ else
36
+ ""
37
+ end
38
+
39
+ location ||= caller_locations(1, 1).first
40
+ file, line = location.path, location.lineno
41
+
42
+ receiver = if to.is_a?(Module)
43
+ if to.name.nil?
44
+ raise ArgumentError, "Can't delegate to anonymous class or module: #{to}"
45
+ end
46
+
47
+ unless Inflector.safe_constantize(to.name).equal?(to)
48
+ raise ArgumentError, "Can't delegate to detached class or module: #{to.name}"
49
+ end
50
+
51
+ "::#{to.name}"
52
+ else
53
+ to.to_s
54
+ end
55
+ receiver = "self.#{receiver}" if RESERVED_METHOD_NAMES.include?(receiver)
56
+
57
+ explicit_receiver = false
58
+ receiver_class = if as
59
+ explicit_receiver = true
60
+ as
61
+ elsif to.is_a?(Module)
62
+ to.singleton_class
63
+ elsif receiver == "self.class"
64
+ nilable = false # self.class can't possibly be nil
65
+ owner.singleton_class
66
+ end
67
+
68
+ method_def = []
69
+ method_names = []
70
+
71
+ method_def << "self.private" if private
72
+
73
+ methods.each do |method|
74
+ method_name = prefix ? "#{method_prefix}#{method}" : method
75
+ method_names << method_name.to_sym
76
+
77
+ # Attribute writer methods only accept one argument. Makes sure []=
78
+ # methods still accept two arguments.
79
+ definition = \
80
+ if signature
81
+ signature
82
+ elsif /[^\]]=\z/.match?(method)
83
+ "arg"
84
+ else
85
+ method_object = if receiver_class
86
+ begin
87
+ receiver_class.public_instance_method(method)
88
+ rescue NameError
89
+ raise if explicit_receiver
90
+ # Do nothing. Fall back to `"..."`
91
+ end
92
+ end
93
+
94
+ if method_object
95
+ parameters = method_object.parameters
96
+
97
+ if parameters.map(&:first).intersect?([:opt, :rest, :keyreq, :key, :keyrest])
98
+ "..."
99
+ else
100
+ defn = parameters.filter_map { |type, arg| arg if type == :req }
101
+ defn << "&"
102
+ defn.join(", ")
103
+ end
104
+ else
105
+ "..."
106
+ end
107
+ end
108
+
109
+ # The following generated method calls the target exactly once, storing
110
+ # the returned value in a dummy variable.
111
+ #
112
+ # Reason is twofold: On one hand doing less calls is in general better.
113
+ # On the other hand it could be that the target has side-effects,
114
+ # whereas conceptually, from the user point of view, the delegator should
115
+ # be doing one call.
116
+ if nilable == false
117
+ method_def <<
118
+ "def #{method_name}(#{definition})" <<
119
+ " (#{receiver}).#{method}(#{definition})" <<
120
+ "end"
121
+ elsif allow_nil
122
+ method = method.to_s
123
+
124
+ method_def <<
125
+ "def #{method_name}(#{definition})" <<
126
+ " _ = #{receiver}" <<
127
+ " if !_.nil? || nil.respond_to?(:#{method})" <<
128
+ " _.#{method}(#{definition})" <<
129
+ " end" <<
130
+ "end"
131
+ else
132
+ method = method.to_s
133
+ method_name = method_name.to_s
134
+
135
+ method_def <<
136
+ "def #{method_name}(#{definition})" <<
137
+ " _ = #{receiver}" <<
138
+ " _.#{method}(#{definition})" <<
139
+ "rescue NoMethodError => e" <<
140
+ " if _.nil? && e.name == :#{method}" <<
141
+ " raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
142
+ " else" <<
143
+ " raise" <<
144
+ " end" <<
145
+ "end"
146
+ end
147
+ end
148
+ owner.module_eval(method_def.join(";"), file, line)
149
+ method_names
150
+ end
151
+
152
+ def generate_method_missing(owner, target, allow_nil: nil)
153
+ target = target.to_s
154
+ target = "self.#{target}" if RESERVED_METHOD_NAMES.include?(target) || target == "__target"
155
+
156
+ if allow_nil
157
+ owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
158
+ def respond_to_missing?(name, include_private = false)
159
+ # It may look like an oversight, but we deliberately do not pass
160
+ # +include_private+, because they do not get delegated.
161
+
162
+ return false if name == :marshal_dump || name == :_dump
163
+ #{target}.respond_to?(name) || super
164
+ end
165
+
166
+ def method_missing(method, ...)
167
+ __target = #{target}
168
+ if __target.nil? && !nil.respond_to?(method)
169
+ nil
170
+ elsif __target.respond_to?(method)
171
+ __target.public_send(method, ...)
172
+ else
173
+ super
174
+ end
175
+ end
176
+ RUBY
177
+ else
178
+ owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
179
+ def respond_to_missing?(name, include_private = false)
180
+ # It may look like an oversight, but we deliberately do not pass
181
+ # +include_private+, because they do not get delegated.
182
+
183
+ return false if name == :marshal_dump || name == :_dump
184
+ #{target}.respond_to?(name) || super
185
+ end
186
+
187
+ def method_missing(method, ...)
188
+ __target = #{target}
189
+ if __target.nil? && !nil.respond_to?(method)
190
+ raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')
191
+ elsif __target.respond_to?(method)
192
+ __target.public_send(method, ...)
193
+ else
194
+ super
195
+ end
196
+ end
197
+ RUBY
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -27,18 +27,6 @@ module ActiveSupport
27
27
  #
28
28
  # MyLib.eager_load!
29
29
  module Autoload
30
- def self.extended(base) # :nodoc:
31
- if RUBY_VERSION < "3"
32
- base.class_eval do
33
- @_autoloads = nil
34
- @_under_path = nil
35
- @_at_path = nil
36
- @_eager_autoload = false
37
- @_eagerloaded_constants = nil
38
- end
39
- end
40
- end
41
-
42
30
  def autoload(const_name, path = @_at_path)
43
31
  unless path
44
32
  full = [name, @_under_path, const_name.to_s].compact.join("::")
@@ -2,28 +2,6 @@
2
2
 
3
3
  module ActiveSupport
4
4
  class Deprecation
5
- # DeprecatedConstantAccessor transforms a constant into a deprecated one by
6
- # hooking +const_missing+.
7
- #
8
- # It takes the names of an old (deprecated) constant and of a new constant
9
- # (both in string form) and a deprecator.
10
- #
11
- # The deprecated constant now returns the same object as the new one rather
12
- # than a proxy object, so it can be used transparently in +rescue+ blocks
13
- # etc.
14
- #
15
- # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
16
- #
17
- # # (In a later update, the original implementation of `PLANETS` has been removed.)
18
- #
19
- # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
20
- # include ActiveSupport::Deprecation::DeprecatedConstantAccessor
21
- # deprecate_constant 'PLANETS', 'PLANETS_POST_2006', deprecator: ActiveSupport::Deprecation.new
22
- #
23
- # PLANETS.map { |planet| planet.capitalize }
24
- # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
25
- # (Backtrace information…)
26
- # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
27
5
  module DeprecatedConstantAccessor
28
6
  def self.included(base)
29
7
  require "active_support/inflector/methods"
@@ -39,11 +17,54 @@ module ActiveSupport
39
17
  super
40
18
  end
41
19
 
42
- def deprecate_constant(const_name, new_constant, message: nil, deprecator: nil)
43
- ActiveSupport.deprecator.warn("DeprecatedConstantAccessor.deprecate_constant without a deprecator is deprecated") unless deprecator
44
- deprecator ||= ActiveSupport::Deprecation._instance
20
+ # Provides a way to rename constants with a deprecation cycle in which
21
+ # both the old and new names work, but using the old one prints a
22
+ # deprecation message.
23
+ #
24
+ # In order to rename <tt>A::B</tt> to <tt>C::D</tt>, you need to delete the
25
+ # definition of <tt>A::B</tt> and declare the deprecation in +A+:
26
+ #
27
+ # require "active_support/deprecation"
28
+ #
29
+ # module A
30
+ # include ActiveSupport::Deprecation::DeprecatedConstantAccessor
31
+ #
32
+ # deprecate_constant "B", "C::D", deprecator: ActiveSupport::Deprecation.new
33
+ # end
34
+ #
35
+ # The first argument is a constant name (no colons). It is the name of
36
+ # the constant you want to deprecate in the enclosing class or module.
37
+ #
38
+ # The second argument is the constant path of the replacement. That
39
+ # has to be a full path even if the replacement is defined in the same
40
+ # namespace as the deprecated one was.
41
+ #
42
+ # In both cases, strings and symbols are supported.
43
+ #
44
+ # The +deprecator+ keyword argument is the object that will print the
45
+ # deprecation message, an instance of ActiveSupport::Deprecation.
46
+ #
47
+ # With that in place, references to <tt>A::B</tt> still work, they
48
+ # evaluate to <tt>C::D</tt> now, and trigger a deprecation warning:
49
+ #
50
+ # DEPRECATION WARNING: A::B is deprecated! Use C::D instead.
51
+ # (called from ...)
52
+ #
53
+ # The message can be customized with the optional +message+ keyword
54
+ # argument.
55
+ #
56
+ # For this to work, a +const_missing+ hook is installed. When client
57
+ # code references the deprecated constant, the callback prints the
58
+ # message and constantizes the replacement.
59
+ #
60
+ # Caveat: If the deprecated constant name is reachable in a different
61
+ # namespace and Ruby constant lookup finds it, the hook won't be
62
+ # called and the deprecation won't work as intended. This may happen,
63
+ # for example, if an ancestor of the enclosing namespace has a
64
+ # constant with the same name. This is an unsupported edge case.
65
+ def deprecate_constant(old_constant_name, new_constant_path, deprecator:, message: nil)
45
66
  class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
46
- class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator }
67
+ class_variable_get(:@@_deprecated_constants)[old_constant_name.to_s] = { new: new_constant_path, message: message, deprecator: deprecator }
47
68
  end
48
69
  end
49
70
  base.singleton_class.prepend extension
@@ -3,7 +3,7 @@
3
3
  module ActiveSupport
4
4
  class Deprecation
5
5
  class DeprecationProxy # :nodoc:
6
- def self.new(*args, &block)
6
+ def self.new(*args, **kwargs, &block)
7
7
  object = args.first
8
8
 
9
9
  return object unless object
@@ -36,11 +36,10 @@ module ActiveSupport
36
36
  # (Backtrace)
37
37
  # # => "#<Object:0x007fb9b34c34b0>"
38
38
  class DeprecatedObjectProxy < DeprecationProxy
39
- def initialize(object, message, deprecator = nil)
39
+ def initialize(object, message, deprecator)
40
40
  @object = object
41
41
  @message = message
42
- ActiveSupport.deprecator.warn("DeprecatedObjectProxy without a deprecator is deprecated") unless deprecator
43
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
42
+ @deprecator = deprecator
44
43
  end
45
44
 
46
45
  private
@@ -86,12 +85,11 @@ module ActiveSupport
86
85
  # example.request.to_s
87
86
  # # => "special_request"
88
87
  class DeprecatedInstanceVariableProxy < DeprecationProxy
89
- def initialize(instance, method, var = "@#{method}", deprecator = nil)
88
+ def initialize(instance, method, var = "@#{method}", deprecator:)
90
89
  @instance = instance
91
90
  @method = method
92
91
  @var = var
93
- ActiveSupport.deprecator.warn("DeprecatedInstanceVariableProxy without a deprecator is deprecated") unless deprecator
94
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
92
+ @deprecator = deprecator
95
93
  end
96
94
 
97
95
  private
@@ -127,13 +125,12 @@ module ActiveSupport
127
125
  super
128
126
  end
129
127
 
130
- def initialize(old_const, new_const, deprecator = nil, message: "#{old_const} is deprecated! Use #{new_const} instead.")
128
+ def initialize(old_const, new_const, deprecator, message: "#{old_const} is deprecated! Use #{new_const} instead.")
131
129
  Kernel.require "active_support/inflector/methods"
132
130
 
133
131
  @old_const = old_const
134
132
  @new_const = new_const
135
- ActiveSupport.deprecator.warn("DeprecatedConstantProxy without a deprecator is deprecated") unless deprecator
136
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
133
+ @deprecator = deprecator
137
134
  @message = message
138
135
  end
139
136
 
@@ -183,9 +180,9 @@ module ActiveSupport
183
180
  target.const_get(name)
184
181
  end
185
182
 
186
- def method_missing(called, *args, &block)
183
+ def method_missing(...)
187
184
  @deprecator.warn(@message, caller_locations)
188
- target.__send__(called, *args, &block)
185
+ target.__send__(...)
189
186
  end
190
187
  end
191
188
  end
@@ -151,7 +151,12 @@ module ActiveSupport
151
151
  end
152
152
 
153
153
  def _extract_callstack(callstack)
154
- warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
154
+ ActiveSupport.deprecator.warn(<<~MESSAGE)
155
+ Passing the result of `caller` to ActiveSupport::Deprecation#warn is deprecated and will be removed in Rails 8.0.
156
+
157
+ Please pass the result of `caller_locations` instead.
158
+ MESSAGE
159
+
155
160
  offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
156
161
 
157
162
  if offending_line
@@ -167,7 +172,7 @@ module ActiveSupport
167
172
  LIB_DIR = RbConfig::CONFIG["libdir"]
168
173
 
169
174
  def ignored_callstack?(path)
170
- path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
175
+ path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
171
176
  end
172
177
  end
173
178
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "singleton"
4
-
5
3
  module ActiveSupport
6
4
  # = Active Support \Deprecation
7
5
  #
@@ -41,7 +39,6 @@ module ActiveSupport
41
39
  # a circular require warning for active_support/deprecation.rb.
42
40
  #
43
41
  # So, we define the constant first, and load dependencies later.
44
- require "active_support/deprecation/instance_delegator"
45
42
  require "active_support/deprecation/behaviors"
46
43
  require "active_support/deprecation/reporting"
47
44
  require "active_support/deprecation/disallowed"
@@ -52,12 +49,18 @@ module ActiveSupport
52
49
  require "active_support/core_ext/module/deprecation"
53
50
  require "concurrent/atomic/thread_local_var"
54
51
 
55
- include InstanceDelegator
56
52
  include Behavior
57
53
  include Reporting
58
54
  include Disallowed
59
55
  include MethodWrapper
60
56
 
57
+ MUTEX = Mutex.new # :nodoc:
58
+ private_constant :MUTEX
59
+
60
+ def self._instance # :nodoc:
61
+ @_instance ||= MUTEX.synchronize { @_instance ||= new }
62
+ end
63
+
61
64
  # The version number in which the deprecated behavior will be removed, by default.
62
65
  attr_accessor :deprecation_horizon
63
66
 
@@ -65,7 +68,7 @@ module ActiveSupport
65
68
  # and the second is a library name.
66
69
  #
67
70
  # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
68
- def initialize(deprecation_horizon = "7.2", gem_name = "Rails")
71
+ def initialize(deprecation_horizon = "8.0", gem_name = "Rails")
69
72
  self.gem_name = gem_name
70
73
  self.deprecation_horizon = deprecation_horizon
71
74
  # By default, warnings are not silenced and debugging is off.
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "weakref"
4
- require "active_support/ruby_features"
5
4
 
6
5
  module ActiveSupport
7
6
  # = Active Support Descendants Tracker
@@ -95,96 +94,19 @@ module ActiveSupport
95
94
  end
96
95
  end
97
96
 
98
- if RubyFeatures::CLASS_SUBCLASSES
99
- class << self
100
- def subclasses(klass)
101
- klass.subclasses
102
- end
103
-
104
- def descendants(klass)
105
- klass.descendants
106
- end
107
- end
108
-
109
- def descendants
110
- subclasses = DescendantsTracker.reject!(self.subclasses)
111
- subclasses.concat(subclasses.flat_map(&:descendants))
112
- end
113
- else
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
117
- class DescendantsArray # :nodoc:
118
- include Enumerable
119
-
120
- def initialize
121
- @refs = []
122
- end
123
-
124
- def <<(klass)
125
- @refs << WeakRef.new(klass)
126
- end
127
-
128
- def each
129
- @refs.reject! do |ref|
130
- yield ref.__getobj__
131
- false
132
- rescue WeakRef::RefError
133
- true
134
- end
135
- self
136
- end
137
-
138
- def refs_size
139
- @refs.size
140
- end
141
-
142
- def cleanup!
143
- @refs.delete_if { |ref| !ref.weakref_alive? }
144
- end
145
-
146
- def reject!
147
- @refs.reject! do |ref|
148
- yield ref.__getobj__
149
- rescue WeakRef::RefError
150
- true
151
- end
152
- end
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)
97
+ class << self
98
+ def subclasses(klass)
99
+ klass.subclasses
177
100
  end
178
101
 
179
- def descendants
180
- DescendantsTracker.descendants(self)
102
+ def descendants(klass)
103
+ klass.descendants
181
104
  end
105
+ end
182
106
 
183
- private
184
- def inherited(base) # :nodoc:
185
- DescendantsTracker.store_inherited(self, base)
186
- super
187
- end
107
+ def descendants
108
+ subclasses = DescendantsTracker.reject!(self.subclasses)
109
+ subclasses.concat(subclasses.flat_map(&:descendants))
188
110
  end
189
111
  end
190
112
  end