activesupport 7.1.0 → 7.2.0
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 +122 -943
- 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 +33 -4
- data/lib/active_support/cache/entry.rb +7 -1
- data/lib/active_support/cache/file_store.rb +16 -11
- data/lib/active_support/cache/mem_cache_store.rb +32 -82
- data/lib/active_support/cache/memory_store.rb +17 -14
- data/lib/active_support/cache/redis_cache_store.rb +54 -36
- data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
- data/lib/active_support/cache/strategy/local_cache.rb +9 -6
- data/lib/active_support/cache.rb +98 -84
- 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 +1 -3
- 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/concerning.rb +6 -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 +2 -2
- data/lib/active_support/core_ext/object/with.rb +5 -3
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- 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/indent.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 +33 -40
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +0 -12
- data/lib/active_support/deprecation/behaviors.rb +18 -16
- 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 +13 -5
- 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 +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +6 -8
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/inflector/methods.rb +2 -2
- data/lib/active_support/log_subscriber.rb +9 -14
- data/lib/active_support/logger.rb +15 -2
- data/lib/active_support/logger_thread_safe_level.rb +0 -8
- data/lib/active_support/message_encryptors.rb +1 -0
- data/lib/active_support/message_pack/extensions.rb +15 -2
- data/lib/active_support/message_verifier.rb +12 -0
- data/lib/active_support/message_verifiers.rb +1 -0
- data/lib/active_support/messages/codec.rb +1 -1
- data/lib/active_support/messages/metadata.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +2 -2
- data/lib/active_support/notifications/fanout.rb +29 -26
- data/lib/active_support/notifications/instrumenter.rb +43 -32
- data/lib/active_support/notifications.rb +28 -27
- data/lib/active_support/number_helper/number_converter.rb +5 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
- data/lib/active_support/number_helper.rb +379 -318
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_options.rb +55 -17
- 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/syntax_error_proxy.rb +12 -1
- 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 +5 -5
- 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 +6 -4
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +8 -4
- 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
|
@@ -13,4 +13,20 @@ class Time
|
|
|
13
13
|
def to_time
|
|
14
14
|
preserve_timezone ? self : getlocal
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
def preserve_timezone # :nodoc:
|
|
18
|
+
active_support_local_zone == zone || super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
@@active_support_local_tz = nil
|
|
23
|
+
|
|
24
|
+
def active_support_local_zone
|
|
25
|
+
@@active_support_local_zone = nil if @@active_support_local_tz != ENV["TZ"]
|
|
26
|
+
@@active_support_local_zone ||=
|
|
27
|
+
begin
|
|
28
|
+
@@active_support_local_tz = ENV["TZ"]
|
|
29
|
+
Time.new.zone
|
|
30
|
+
end
|
|
31
|
+
end
|
|
16
32
|
end
|
|
@@ -58,8 +58,6 @@ class Time
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
alias_method :to_formatted_s, :to_fs
|
|
61
|
-
alias_method :to_default_s, :to_s
|
|
62
|
-
deprecate to_default_s: :to_s, deprecator: ActiveSupport.deprecator
|
|
63
61
|
|
|
64
62
|
# Returns a formatted string of the offset from UTC, or an alternative
|
|
65
63
|
# string if the time zone is already UTC.
|
|
@@ -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,28 @@ 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
|
+
return if name == :initialize
|
|
186
|
+
return unless public_method_defined?(name)
|
|
187
|
+
return if respond_to?(name, true)
|
|
188
|
+
Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
|
|
192
189
|
end
|
|
193
190
|
end
|
|
194
191
|
|
|
192
|
+
class_attribute :defaults, instance_writer: false, default: {}.freeze
|
|
193
|
+
|
|
195
194
|
attr_accessor :attributes
|
|
196
195
|
|
|
197
196
|
def initialize
|
|
198
|
-
@attributes =
|
|
197
|
+
@attributes = resolve_defaults
|
|
199
198
|
end
|
|
200
199
|
|
|
201
200
|
# Expose one or more attributes within a block. Old values are returned after the block concludes.
|
|
@@ -208,28 +207,22 @@ module ActiveSupport
|
|
|
208
207
|
# end
|
|
209
208
|
# end
|
|
210
209
|
# end
|
|
211
|
-
def set(
|
|
212
|
-
|
|
213
|
-
assign_attributes(set_attributes)
|
|
214
|
-
yield
|
|
215
|
-
ensure
|
|
216
|
-
assign_attributes(old_attributes)
|
|
210
|
+
def set(attributes, &block)
|
|
211
|
+
with(**attributes, &block)
|
|
217
212
|
end
|
|
218
213
|
|
|
219
214
|
# Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
|
|
220
215
|
def reset
|
|
221
216
|
run_callbacks :reset do
|
|
222
|
-
self.attributes =
|
|
217
|
+
self.attributes = resolve_defaults
|
|
223
218
|
end
|
|
224
219
|
end
|
|
225
220
|
|
|
226
221
|
private
|
|
227
|
-
def
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def compute_attributes(keys)
|
|
232
|
-
keys.index_with { |key| public_send(key) }
|
|
222
|
+
def resolve_defaults
|
|
223
|
+
defaults.transform_values do |value|
|
|
224
|
+
Proc === value ? value.call : value.dup
|
|
225
|
+
end
|
|
233
226
|
end
|
|
234
227
|
end
|
|
235
228
|
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("::")
|
|
@@ -57,15 +57,15 @@ module ActiveSupport
|
|
|
57
57
|
# You can create a custom behavior or set any from the +DEFAULT_BEHAVIORS+
|
|
58
58
|
# constant. Available behaviors are:
|
|
59
59
|
#
|
|
60
|
-
# [
|
|
61
|
-
# [
|
|
62
|
-
# [
|
|
63
|
-
# [
|
|
64
|
-
# [
|
|
65
|
-
# [
|
|
60
|
+
# [+:raise+] Raise ActiveSupport::DeprecationException.
|
|
61
|
+
# [+:stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
|
|
62
|
+
# [+:log+] Log all deprecation warnings to +Rails.logger+.
|
|
63
|
+
# [+:notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
|
|
64
|
+
# [+:report+] Use ActiveSupport::ErrorReporter to report deprecations.
|
|
65
|
+
# [+:silence+] Do nothing. On \Rails, set <tt>config.active_support.report_deprecations = false</tt> to disable all behaviors.
|
|
66
66
|
#
|
|
67
67
|
# Setting behaviors only affects deprecations that happen after boot time.
|
|
68
|
-
# For more information you can read the documentation of the
|
|
68
|
+
# For more information you can read the documentation of the #behavior= method.
|
|
69
69
|
module Behavior
|
|
70
70
|
# Whether to print a backtrace along with the warning.
|
|
71
71
|
attr_accessor :debug
|
|
@@ -85,12 +85,12 @@ module ActiveSupport
|
|
|
85
85
|
#
|
|
86
86
|
# Available behaviors:
|
|
87
87
|
#
|
|
88
|
-
# [
|
|
89
|
-
# [
|
|
90
|
-
# [
|
|
91
|
-
# [
|
|
92
|
-
# [
|
|
93
|
-
# [
|
|
88
|
+
# [+:raise+] Raise ActiveSupport::DeprecationException.
|
|
89
|
+
# [+:stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
|
|
90
|
+
# [+:log+] Log all deprecation warnings to +Rails.logger+.
|
|
91
|
+
# [+:notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
|
|
92
|
+
# [+:report+] Use ActiveSupport::ErrorReporter to report deprecations.
|
|
93
|
+
# [+:silence+] Do nothing.
|
|
94
94
|
#
|
|
95
95
|
# Setting behaviors only affects deprecations that happen after boot time.
|
|
96
96
|
# Deprecation warnings raised by gems are not affected by this setting
|
|
@@ -104,15 +104,17 @@ module ActiveSupport
|
|
|
104
104
|
# # custom stuff
|
|
105
105
|
# }
|
|
106
106
|
#
|
|
107
|
-
# If you are using \Rails, you can set
|
|
108
|
-
#
|
|
107
|
+
# If you are using \Rails, you can set
|
|
108
|
+
# <tt>config.active_support.report_deprecations = false</tt> to disable
|
|
109
|
+
# all deprecation behaviors. This is similar to the +:silence+ option but
|
|
110
|
+
# more performant.
|
|
109
111
|
def behavior=(behavior)
|
|
110
112
|
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
|
|
111
113
|
end
|
|
112
114
|
|
|
113
115
|
# Sets the behavior for disallowed deprecations (those configured by
|
|
114
116
|
# ActiveSupport::Deprecation#disallowed_warnings=) to the specified
|
|
115
|
-
# value. As with
|
|
117
|
+
# value. As with #behavior=, this can be a single value, array, or an
|
|
116
118
|
# object that responds to +call+.
|
|
117
119
|
def disallowed_behavior=(behavior)
|
|
118
120
|
@disallowed_behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
|
|
@@ -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
|
|
@@ -142,15 +142,22 @@ module ActiveSupport
|
|
|
142
142
|
return _extract_callstack(callstack) if callstack.first.is_a? String
|
|
143
143
|
|
|
144
144
|
offending_line = callstack.find { |frame|
|
|
145
|
-
|
|
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)
|
|
146
148
|
} || callstack.first
|
|
147
149
|
|
|
148
150
|
[offending_line.path, offending_line.lineno, offending_line.label]
|
|
149
151
|
end
|
|
150
152
|
|
|
151
153
|
def _extract_callstack(callstack)
|
|
152
|
-
warn
|
|
153
|
-
|
|
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
|
+
|
|
160
|
+
offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
|
|
154
161
|
|
|
155
162
|
if offending_line
|
|
156
163
|
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
|
|
@@ -162,9 +169,10 @@ module ActiveSupport
|
|
|
162
169
|
end
|
|
163
170
|
|
|
164
171
|
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
|
|
172
|
+
LIB_DIR = RbConfig::CONFIG["libdir"]
|
|
165
173
|
|
|
166
|
-
def ignored_callstack(path)
|
|
167
|
-
path.start_with?(RAILS_GEM_ROOT) || path.
|
|
174
|
+
def ignored_callstack?(path)
|
|
175
|
+
path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
|
|
168
176
|
end
|
|
169
177
|
end
|
|
170
178
|
end
|