activesupport 7.1.3.4 → 7.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +123 -1084
- data/lib/active_support/array_inquirer.rb +1 -1
- data/lib/active_support/backtrace_cleaner.rb +15 -3
- data/lib/active_support/broadcast_logger.rb +5 -4
- data/lib/active_support/cache/file_store.rb +15 -10
- data/lib/active_support/cache/mem_cache_store.rb +16 -74
- data/lib/active_support/cache/memory_store.rb +2 -1
- data/lib/active_support/cache/redis_cache_store.rb +16 -13
- data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
- data/lib/active_support/cache.rb +61 -68
- data/lib/active_support/callbacks.rb +74 -113
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/core_ext/array/conversions.rb +0 -2
- data/lib/active_support/core_ext/class/subclasses.rb +15 -35
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +0 -2
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
- data/lib/active_support/core_ext/digest/uuid.rb +6 -0
- data/lib/active_support/core_ext/erb/util.rb +5 -0
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -148
- data/lib/active_support/core_ext/module/deprecation.rb +1 -4
- data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/duplicable.rb +24 -15
- data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
- data/lib/active_support/core_ext/object/json.rb +1 -1
- data/lib/active_support/core_ext/object/with.rb +5 -3
- data/lib/active_support/core_ext/pathname/blank.rb +4 -0
- data/lib/active_support/core_ext/range/overlap.rb +1 -1
- data/lib/active_support/core_ext/securerandom.rb +8 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +0 -7
- data/lib/active_support/core_ext/time/calculations.rb +18 -28
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +0 -2
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +34 -40
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +0 -12
- data/lib/active_support/deprecation/constant_accessor.rb +47 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
- data/lib/active_support/deprecation/reporting.rb +7 -2
- data/lib/active_support/deprecation.rb +8 -5
- data/lib/active_support/descendants_tracker.rb +9 -87
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -2
- data/lib/active_support/duration.rb +11 -6
- data/lib/active_support/error_reporter.rb +41 -3
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +0 -1
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/fork_tracker.rb +2 -38
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +6 -8
- data/lib/active_support/html_safe_translation.rb +7 -4
- data/lib/active_support/json/encoding.rb +1 -1
- data/lib/active_support/log_subscriber.rb +1 -12
- data/lib/active_support/logger.rb +15 -2
- data/lib/active_support/logger_thread_safe_level.rb +0 -8
- data/lib/active_support/message_pack/extensions.rb +15 -2
- data/lib/active_support/message_verifier.rb +12 -0
- data/lib/active_support/messages/codec.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +2 -2
- data/lib/active_support/notifications/fanout.rb +4 -7
- data/lib/active_support/notifications/instrumenter.rb +32 -21
- data/lib/active_support/notifications.rb +28 -27
- data/lib/active_support/number_helper/number_converter.rb +2 -2
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_options.rb +53 -15
- data/lib/active_support/proxy_object.rb +8 -5
- data/lib/active_support/railtie.rb +4 -11
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/subscriber.rb +1 -0
- data/lib/active_support/syntax_error_proxy.rb +1 -11
- data/lib/active_support/tagged_logging.rb +4 -1
- data/lib/active_support/test_case.rb +3 -1
- data/lib/active_support/testing/assertions.rb +4 -4
- data/lib/active_support/testing/constant_stubbing.rb +30 -8
- data/lib/active_support/testing/deprecation.rb +5 -12
- data/lib/active_support/testing/isolation.rb +18 -8
- data/lib/active_support/testing/method_call_assertions.rb +2 -16
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/strict_warnings.rb +5 -4
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +3 -3
- data/lib/active_support/time_with_zone.rb +7 -3
- data/lib/active_support/values/time_zone.rb +10 -1
- data/lib/active_support/xml_mini.rb +11 -2
- data/lib/active_support.rb +3 -2
- metadata +35 -15
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- 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
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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,
|
181
|
-
|
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
|
-
|
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(
|
212
|
-
|
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
|
228
|
-
|
229
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
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)[
|
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
|
39
|
+
def initialize(object, message, deprecator)
|
40
40
|
@object = object
|
41
41
|
@message = message
|
42
|
-
|
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
|
88
|
+
def initialize(instance, method, var = "@#{method}", deprecator:)
|
90
89
|
@instance = instance
|
91
90
|
@method = method
|
92
91
|
@var = var
|
93
|
-
|
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
|
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
|
-
|
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(
|
183
|
+
def method_missing(...)
|
187
184
|
@deprecator.warn(@message, caller_locations)
|
188
|
-
target.__send__(
|
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
|
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 = "
|
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
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
102
|
+
def descendants(klass)
|
103
|
+
klass.descendants
|
181
104
|
end
|
105
|
+
end
|
182
106
|
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|