activesupport 7.1.3.2 → 7.2.2.2
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 +134 -1054
- 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 +19 -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 +62 -69
- 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 +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 +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 +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/setup_and_teardown.rb +2 -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 +16 -7
- data/lib/active_support/xml_mini.rb +11 -2
- data/lib/active_support.rb +3 -2
- metadata +49 -18
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
@@ -46,7 +46,7 @@ module ActiveSupport
|
|
46
46
|
end
|
47
47
|
|
48
48
|
[Enumerable, Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].reverse_each do |klass|
|
49
|
-
klass.
|
49
|
+
klass.include(ActiveSupport::ToJsonWithActiveSupportEncoder)
|
50
50
|
end
|
51
51
|
|
52
52
|
class Module
|
@@ -233,9 +233,11 @@ class Pathname # :nodoc:
|
|
233
233
|
end
|
234
234
|
end
|
235
235
|
|
236
|
-
|
237
|
-
|
238
|
-
|
236
|
+
unless IPAddr.method_defined?(:as_json, false)
|
237
|
+
class IPAddr # :nodoc:
|
238
|
+
def as_json(options = nil)
|
239
|
+
to_s
|
240
|
+
end
|
239
241
|
end
|
240
242
|
end
|
241
243
|
|
@@ -4,11 +4,13 @@ class Object
|
|
4
4
|
# Set and restore public attributes around a block.
|
5
5
|
#
|
6
6
|
# client.timeout # => 5
|
7
|
-
# client.with(timeout: 1) do
|
8
|
-
#
|
7
|
+
# client.with(timeout: 1) do |c|
|
8
|
+
# c.timeout # => 1
|
9
9
|
# end
|
10
10
|
# client.timeout # => 5
|
11
11
|
#
|
12
|
+
# The receiver is yielded to the provided block.
|
13
|
+
#
|
12
14
|
# This method is a shorthand for the common begin/ensure pattern:
|
13
15
|
#
|
14
16
|
# old_value = object.attribute
|
@@ -28,7 +30,7 @@ class Object
|
|
28
30
|
old_values[key] = public_send(key)
|
29
31
|
public_send("#{key}=", value)
|
30
32
|
end
|
31
|
-
yield
|
33
|
+
yield self
|
32
34
|
ensure
|
33
35
|
old_values.each do |key, old_value|
|
34
36
|
public_send("#{key}=", old_value)
|
@@ -4,7 +4,7 @@ class Range
|
|
4
4
|
# Compare two ranges and see if they overlap each other
|
5
5
|
# (1..5).overlap?(4..6) # => true
|
6
6
|
# (1..5).overlap?(7..9) # => false
|
7
|
-
unless Range.method_defined?(:overlap?)
|
7
|
+
unless Range.method_defined?(:overlap?) # Ruby 3.3+
|
8
8
|
def overlap?(other)
|
9
9
|
raise TypeError unless other.is_a? Range
|
10
10
|
|
@@ -16,18 +16,8 @@ module SecureRandom
|
|
16
16
|
#
|
17
17
|
# p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
|
18
18
|
# p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
|
19
|
-
|
20
|
-
|
21
|
-
SecureRandom.alphanumeric(n, chars: BASE58_ALPHABET)
|
22
|
-
end
|
23
|
-
else
|
24
|
-
def self.base58(n = 16)
|
25
|
-
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
|
26
|
-
idx = byte % 64
|
27
|
-
idx = SecureRandom.random_number(58) if idx >= 58
|
28
|
-
BASE58_ALPHABET[idx]
|
29
|
-
end.join
|
30
|
-
end
|
19
|
+
def self.base58(n = 16)
|
20
|
+
SecureRandom.alphanumeric(n, chars: BASE58_ALPHABET)
|
31
21
|
end
|
32
22
|
|
33
23
|
# SecureRandom.base36 generates a random base36 string in lowercase.
|
@@ -41,17 +31,11 @@ module SecureRandom
|
|
41
31
|
#
|
42
32
|
# p SecureRandom.base36 # => "4kugl2pdqmscqtje"
|
43
33
|
# p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7"
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
|
51
|
-
idx = byte % 64
|
52
|
-
idx = SecureRandom.random_number(36) if idx >= 36
|
53
|
-
BASE36_ALPHABET[idx]
|
54
|
-
end.join
|
55
|
-
end
|
34
|
+
def self.base36(n = 16)
|
35
|
+
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
|
36
|
+
idx = byte % 64
|
37
|
+
idx = SecureRandom.random_number(36) if idx >= 36
|
38
|
+
BASE36_ALPHABET[idx]
|
39
|
+
end.join
|
56
40
|
end
|
57
41
|
end
|
@@ -22,7 +22,7 @@ class String
|
|
22
22
|
def to_time(form = :local)
|
23
23
|
parts = Date._parse(self, false)
|
24
24
|
used_keys = %i(year mon mday hour min sec sec_fraction offset)
|
25
|
-
return if
|
25
|
+
return if !parts.keys.intersect?(used_keys)
|
26
26
|
|
27
27
|
now = Time.now
|
28
28
|
time = Time.new(
|
@@ -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("::")
|