activesupport 7.1.4.1 → 7.2.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 +127 -1129
- data/lib/active_support/array_inquirer.rb +1 -1
- data/lib/active_support/backtrace_cleaner.rb +10 -3
- data/lib/active_support/broadcast_logger.rb +18 -18
- 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 +60 -68
- data/lib/active_support/callbacks.rb +74 -113
- 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 -163
- 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/instance_variables.rb +11 -19
- data/lib/active_support/core_ext/object/json.rb +6 -4
- 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/multibyte.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/time/zones.rb +1 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +38 -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 +9 -4
- 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/encrypted_file.rb +1 -1
- 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 +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +6 -8
- data/lib/active_support/html_safe_translation.rb +3 -0
- data/lib/active_support/log_subscriber.rb +0 -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/multibyte/chars.rb +2 -2
- data/lib/active_support/notifications/fanout.rb +4 -7
- data/lib/active_support/notifications/instrumenter.rb +21 -18
- 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/tagged_logging.rb +0 -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 +20 -8
- data/lib/active_support/testing/method_call_assertions.rb +2 -16
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/strict_warnings.rb +8 -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 +8 -4
- data/lib/active_support/values/time_zone.rb +7 -7
- data/lib/active_support/xml_mini.rb +11 -2
- data/lib/active_support.rb +2 -1
- metadata +49 -15
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
@@ -109,7 +109,7 @@ class String
|
|
109
109
|
when omission.bytesize == truncate_to
|
110
110
|
omission.dup
|
111
111
|
else
|
112
|
-
self.class.new.tap do |cut|
|
112
|
+
self.class.new.force_encoding(encoding).tap do |cut|
|
113
113
|
cut_at = truncate_to - omission.bytesize
|
114
114
|
|
115
115
|
each_grapheme_cluster do |grapheme|
|
@@ -19,7 +19,7 @@ class String
|
|
19
19
|
# >> "lj".upcase
|
20
20
|
# => "LJ"
|
21
21
|
#
|
22
|
-
# == Method chaining
|
22
|
+
# == \Method chaining
|
23
23
|
#
|
24
24
|
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
|
25
25
|
# method chaining on the result of any of these methods.
|
@@ -77,13 +77,6 @@ module ActiveSupport # :nodoc:
|
|
77
77
|
@html_safe = other.html_safe?
|
78
78
|
end
|
79
79
|
|
80
|
-
def clone_empty # :nodoc:
|
81
|
-
ActiveSupport.deprecator.warn <<~EOM
|
82
|
-
ActiveSupport::SafeBuffer#clone_empty is deprecated and will be removed in Rails 7.2.
|
83
|
-
EOM
|
84
|
-
self[0, 0]
|
85
|
-
end
|
86
|
-
|
87
80
|
def concat(value)
|
88
81
|
unless value.nil?
|
89
82
|
super(implicit_html_escape_interpolated_argument(value))
|
@@ -42,20 +42,20 @@ class Time
|
|
42
42
|
|
43
43
|
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
|
44
44
|
# instances can be used when called with a single argument
|
45
|
-
def at_with_coercion(*args
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
at_without_coercion(time_or_number.to_f).getlocal
|
45
|
+
def at_with_coercion(time_or_number, *args)
|
46
|
+
if args.empty?
|
47
|
+
if time_or_number.is_a?(ActiveSupport::TimeWithZone)
|
48
|
+
at_without_coercion(time_or_number.to_r).getlocal
|
49
|
+
elsif time_or_number.is_a?(DateTime)
|
50
|
+
at_without_coercion(time_or_number.to_f).getlocal
|
51
|
+
else
|
52
|
+
at_without_coercion(time_or_number)
|
53
|
+
end
|
55
54
|
else
|
56
|
-
at_without_coercion(time_or_number)
|
55
|
+
at_without_coercion(time_or_number, *args)
|
57
56
|
end
|
58
57
|
end
|
58
|
+
ruby2_keywords :at_with_coercion
|
59
59
|
alias_method :at_without_coercion, :at
|
60
60
|
alias_method :at, :at_with_coercion
|
61
61
|
|
@@ -108,21 +108,6 @@ class Time
|
|
108
108
|
subsec
|
109
109
|
end
|
110
110
|
|
111
|
-
unless Time.method_defined?(:floor)
|
112
|
-
def floor(precision = 0)
|
113
|
-
change(nsec: 0) + subsec.floor(precision)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Restricted Ruby version due to a bug in `Time#ceil`
|
118
|
-
# See https://bugs.ruby-lang.org/issues/17025 for more details
|
119
|
-
if RUBY_VERSION <= "2.8"
|
120
|
-
remove_possible_method :ceil
|
121
|
-
def ceil(precision = 0)
|
122
|
-
change(nsec: 0) + subsec.ceil(precision)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
111
|
# Returns a new Time where one or more of the elements have been changed according
|
127
112
|
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
|
128
113
|
# <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
|
@@ -159,7 +144,7 @@ class Time
|
|
159
144
|
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset)
|
160
145
|
elsif utc?
|
161
146
|
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec)
|
162
|
-
elsif zone
|
147
|
+
elsif zone.respond_to?(:utc_to_local)
|
163
148
|
new_time = ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone)
|
164
149
|
|
165
150
|
# When there are two occurrences of a nominal time due to DST ending,
|
@@ -334,7 +319,12 @@ class Time
|
|
334
319
|
if other.class == Time
|
335
320
|
compare_without_coercion(other)
|
336
321
|
elsif other.is_a?(Time)
|
337
|
-
|
322
|
+
# also avoid ActiveSupport::TimeWithZone#to_time before Rails 8.0
|
323
|
+
if other.respond_to?(:comparable_time)
|
324
|
+
compare_without_coercion(other.comparable_time)
|
325
|
+
else
|
326
|
+
compare_without_coercion(other.to_time)
|
327
|
+
end
|
338
328
|
else
|
339
329
|
to_datetime <=> other
|
340
330
|
end
|
@@ -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.
|
@@ -20,7 +20,7 @@ class Time
|
|
20
20
|
# This method accepts any of the following:
|
21
21
|
#
|
22
22
|
# * A \Rails TimeZone object.
|
23
|
-
# * An identifier for a \Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
|
23
|
+
# * An identifier for a \Rails TimeZone object (e.g., "Eastern \Time (US & Canada)", <tt>-5.hours</tt>).
|
24
24
|
# * A +TZInfo::Timezone+ object.
|
25
25
|
# * An identifier for a +TZInfo::Timezone+ object (e.g., "America/New_York").
|
26
26
|
#
|
@@ -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
|
|
@@ -94,6 +95,8 @@ module ActiveSupport
|
|
94
95
|
|
95
96
|
INVALID_ATTRIBUTE_NAMES = [:set, :reset, :resets, :instance, :before_reset, :after_reset, :reset_all, :clear_all] # :nodoc:
|
96
97
|
|
98
|
+
NOT_SET = Object.new.freeze # :nodoc:
|
99
|
+
|
97
100
|
class << self
|
98
101
|
# Returns singleton instance for this class in this thread. If none exists, one is created.
|
99
102
|
def instance
|
@@ -101,7 +104,14 @@ module ActiveSupport
|
|
101
104
|
end
|
102
105
|
|
103
106
|
# Declares one or more attributes that will be given both class and instance accessor methods.
|
104
|
-
|
107
|
+
#
|
108
|
+
# ==== Options
|
109
|
+
#
|
110
|
+
# * <tt>:default</tt> - The default value for the attributes. If the value
|
111
|
+
# is a proc or lambda, it will be called whenever an instance is
|
112
|
+
# constructed. Otherwise, the value will be duplicated with +#dup+.
|
113
|
+
# Default values are re-assigned when the attributes are reset.
|
114
|
+
def attribute(*names, default: NOT_SET)
|
105
115
|
invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
|
106
116
|
if invalid_attribute_names.any?
|
107
117
|
raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
|
@@ -124,22 +134,10 @@ module ActiveSupport
|
|
124
134
|
end
|
125
135
|
end
|
126
136
|
|
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
|
137
|
+
Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
|
138
|
+
Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
|
139
|
+
|
140
|
+
self.defaults = defaults.merge(names.index_with { default })
|
143
141
|
end
|
144
142
|
|
145
143
|
# Calls this callback before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
|
@@ -177,25 +175,29 @@ module ActiveSupport
|
|
177
175
|
@current_instances_key ||= name.to_sym
|
178
176
|
end
|
179
177
|
|
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)
|
178
|
+
def method_missing(name, ...)
|
179
|
+
instance.public_send(name, ...)
|
187
180
|
end
|
188
|
-
ruby2_keywords(:method_missing)
|
189
181
|
|
190
182
|
def respond_to_missing?(name, _)
|
191
|
-
|
183
|
+
instance.respond_to?(name) || super
|
184
|
+
end
|
185
|
+
|
186
|
+
def method_added(name)
|
187
|
+
super
|
188
|
+
return if name == :initialize
|
189
|
+
return unless public_method_defined?(name)
|
190
|
+
return if respond_to?(name, true)
|
191
|
+
Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
195
|
+
class_attribute :defaults, instance_writer: false, default: {}.freeze
|
196
|
+
|
195
197
|
attr_accessor :attributes
|
196
198
|
|
197
199
|
def initialize
|
198
|
-
@attributes =
|
200
|
+
@attributes = resolve_defaults
|
199
201
|
end
|
200
202
|
|
201
203
|
# Expose one or more attributes within a block. Old values are returned after the block concludes.
|
@@ -208,28 +210,24 @@ module ActiveSupport
|
|
208
210
|
# end
|
209
211
|
# end
|
210
212
|
# end
|
211
|
-
def set(
|
212
|
-
|
213
|
-
assign_attributes(set_attributes)
|
214
|
-
yield
|
215
|
-
ensure
|
216
|
-
assign_attributes(old_attributes)
|
213
|
+
def set(attributes, &block)
|
214
|
+
with(**attributes, &block)
|
217
215
|
end
|
218
216
|
|
219
217
|
# Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
|
220
218
|
def reset
|
221
219
|
run_callbacks :reset do
|
222
|
-
self.attributes =
|
220
|
+
self.attributes = resolve_defaults
|
223
221
|
end
|
224
222
|
end
|
225
223
|
|
226
224
|
private
|
227
|
-
def
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
225
|
+
def resolve_defaults
|
226
|
+
defaults.each_with_object({}) do |(key, value), result|
|
227
|
+
if value != NOT_SET
|
228
|
+
result[key] = Proc === value ? value.call : value.dup
|
229
|
+
end
|
230
|
+
end
|
233
231
|
end
|
234
232
|
end
|
235
233
|
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
|