activesupport 7.1.5.1 → 7.2.0.beta1

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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +113 -1178
  3. data/lib/active_support/array_inquirer.rb +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +10 -3
  5. data/lib/active_support/broadcast_logger.rb +18 -19
  6. data/lib/active_support/cache/file_store.rb +15 -10
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -74
  8. data/lib/active_support/cache/memory_store.rb +2 -1
  9. data/lib/active_support/cache/redis_cache_store.rb +16 -13
  10. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  11. data/lib/active_support/cache.rb +59 -67
  12. data/lib/active_support/callbacks.rb +74 -113
  13. data/lib/active_support/code_generator.rb +10 -15
  14. data/lib/active_support/core_ext/array/conversions.rb +0 -2
  15. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  16. data/lib/active_support/core_ext/date/blank.rb +4 -0
  17. data/lib/active_support/core_ext/date/conversions.rb +0 -2
  18. data/lib/active_support/core_ext/date_and_time/compatibility.rb +12 -9
  19. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  20. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  21. data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
  22. data/lib/active_support/core_ext/erb/util.rb +5 -0
  23. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  24. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  25. data/lib/active_support/core_ext/module/delegation.rb +20 -163
  26. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  27. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  28. data/lib/active_support/core_ext/object/blank.rb +45 -1
  29. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  30. data/lib/active_support/core_ext/object/json.rb +4 -6
  31. data/lib/active_support/core_ext/object/with.rb +5 -3
  32. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  33. data/lib/active_support/core_ext/range/overlap.rb +1 -1
  34. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  35. data/lib/active_support/core_ext/string/filters.rb +1 -1
  36. data/lib/active_support/core_ext/string/output_safety.rb +0 -7
  37. data/lib/active_support/core_ext/time/calculations.rb +12 -27
  38. data/lib/active_support/core_ext/time/compatibility.rb +2 -3
  39. data/lib/active_support/core_ext/time/conversions.rb +0 -2
  40. data/lib/active_support/core_ext.rb +0 -1
  41. data/lib/active_support/current_attributes.rb +33 -40
  42. data/lib/active_support/delegation.rb +188 -0
  43. data/lib/active_support/dependencies/autoload.rb +0 -12
  44. data/lib/active_support/deprecation/constant_accessor.rb +1 -3
  45. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  46. data/lib/active_support/deprecation/reporting.rb +9 -4
  47. data/lib/active_support/deprecation.rb +8 -5
  48. data/lib/active_support/descendants_tracker.rb +9 -87
  49. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  50. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  51. data/lib/active_support/duration.rb +11 -6
  52. data/lib/active_support/error_reporter.rb +41 -3
  53. data/lib/active_support/evented_file_update_checker.rb +0 -1
  54. data/lib/active_support/execution_wrapper.rb +0 -1
  55. data/lib/active_support/file_update_checker.rb +1 -1
  56. data/lib/active_support/fork_tracker.rb +2 -38
  57. data/lib/active_support/gem_version.rb +3 -3
  58. data/lib/active_support/hash_with_indifferent_access.rb +6 -8
  59. data/lib/active_support/html_safe_translation.rb +3 -0
  60. data/lib/active_support/log_subscriber.rb +0 -12
  61. data/lib/active_support/logger.rb +15 -2
  62. data/lib/active_support/message_pack/extensions.rb +15 -2
  63. data/lib/active_support/multibyte/chars.rb +2 -2
  64. data/lib/active_support/notifications/fanout.rb +4 -7
  65. data/lib/active_support/notifications/instrumenter.rb +21 -18
  66. data/lib/active_support/notifications.rb +28 -27
  67. data/lib/active_support/number_helper/number_converter.rb +2 -2
  68. data/lib/active_support/option_merger.rb +2 -2
  69. data/lib/active_support/ordered_options.rb +53 -15
  70. data/lib/active_support/proxy_object.rb +8 -5
  71. data/lib/active_support/railtie.rb +4 -11
  72. data/lib/active_support/string_inquirer.rb +1 -1
  73. data/lib/active_support/syntax_error_proxy.rb +11 -1
  74. data/lib/active_support/test_case.rb +3 -1
  75. data/lib/active_support/testing/assertions.rb +4 -4
  76. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  77. data/lib/active_support/testing/deprecation.rb +5 -12
  78. data/lib/active_support/testing/isolation.rb +18 -8
  79. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  80. data/lib/active_support/testing/strict_warnings.rb +5 -4
  81. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  82. data/lib/active_support/time_with_zone.rb +9 -10
  83. data/lib/active_support/values/time_zone.rb +1 -1
  84. data/lib/active_support/xml_mini.rb +11 -2
  85. data/lib/active_support.rb +7 -8
  86. metadata +20 -70
  87. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  88. data/lib/active_support/ruby_features.rb +0 -7
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/callbacks"
4
+ require "active_support/core_ext/object/with"
4
5
  require "active_support/core_ext/enumerable"
5
6
  require "active_support/core_ext/module/delegation"
6
7
 
@@ -101,7 +102,14 @@ module ActiveSupport
101
102
  end
102
103
 
103
104
  # Declares one or more attributes that will be given both class and instance accessor methods.
104
- def attribute(*names)
105
+ #
106
+ # ==== Options
107
+ #
108
+ # * <tt>:default</tt> - The default value for the attributes. If the value
109
+ # is a proc or lambda, it will be called whenever an instance is
110
+ # constructed. Otherwise, the value will be duplicated with +#dup+.
111
+ # Default values are re-assigned when the attributes are reset.
112
+ def attribute(*names, default: nil)
105
113
  invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
106
114
  if invalid_attribute_names.any?
107
115
  raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
@@ -124,22 +132,10 @@ module ActiveSupport
124
132
  end
125
133
  end
126
134
 
127
- ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
128
- names.each do |name|
129
- owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
130
- batch <<
131
- "def #{name}" <<
132
- "instance.#{name}" <<
133
- "end"
134
- end
135
- owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
136
- batch <<
137
- "def #{name}=(value)" <<
138
- "instance.#{name} = value" <<
139
- "end"
140
- end
141
- end
142
- end
135
+ Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
136
+ Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
137
+
138
+ self.defaults = defaults.merge(names.index_with { default })
143
139
  end
144
140
 
145
141
  # Calls this callback before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
@@ -177,25 +173,28 @@ module ActiveSupport
177
173
  @current_instances_key ||= name.to_sym
178
174
  end
179
175
 
180
- def method_missing(name, *args, &block)
181
- # Caches the method definition as a singleton method of the receiver.
182
- #
183
- # By letting #delegate handle it, we avoid an enclosure that'll capture args.
184
- singleton_class.delegate name, to: :instance
185
-
186
- send(name, *args, &block)
176
+ def method_missing(name, ...)
177
+ instance.public_send(name, ...)
187
178
  end
188
- ruby2_keywords(:method_missing)
189
179
 
190
180
  def respond_to_missing?(name, _)
191
- super || instance.respond_to?(name)
181
+ instance.respond_to?(name) || super
182
+ end
183
+
184
+ def method_added(name)
185
+ 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(set_attributes)
212
- old_attributes = compute_attributes(set_attributes.keys)
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 assign_attributes(new_attributes)
228
- new_attributes.each { |key, value| public_send("#{key}=", value) }
229
- end
230
-
231
- def compute_attributes(keys)
232
- keys.index_with { |key| public_send(key) }
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,188 @@
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)
155
+
156
+ owner.module_eval <<-RUBY, __FILE__, __LINE__ + 1
157
+ def respond_to_missing?(name, include_private = false)
158
+ # It may look like an oversight, but we deliberately do not pass
159
+ # +include_private+, because they do not get delegated.
160
+
161
+ return false if name == :marshal_dump || name == :_dump
162
+ #{target}.respond_to?(name) || super
163
+ end
164
+
165
+ def method_missing(method, ...)
166
+ if #{target}.respond_to?(method)
167
+ #{target}.public_send(method, ...)
168
+ else
169
+ begin
170
+ super
171
+ rescue NoMethodError
172
+ if #{target}.nil?
173
+ if #{allow_nil == true}
174
+ nil
175
+ else
176
+ raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')
177
+ end
178
+ else
179
+ raise
180
+ end
181
+ end
182
+ end
183
+ end
184
+ RUBY
185
+ end
186
+ end
187
+ end
188
+ 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("::")
@@ -39,9 +39,7 @@ module ActiveSupport
39
39
  super
40
40
  end
41
41
 
42
- def deprecate_constant(const_name, new_constant, message: nil, deprecator: nil)
43
- ActiveSupport.deprecator.warn("DeprecatedConstantAccessor.deprecate_constant without a deprecator is deprecated") unless deprecator
44
- deprecator ||= ActiveSupport::Deprecation._instance
42
+ def deprecate_constant(const_name, new_constant, deprecator:, message: nil)
45
43
  class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
46
44
  class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator }
47
45
  end
@@ -3,7 +3,7 @@
3
3
  module ActiveSupport
4
4
  class Deprecation
5
5
  class DeprecationProxy # :nodoc:
6
- def self.new(*args, &block)
6
+ def self.new(*args, **kwargs, &block)
7
7
  object = args.first
8
8
 
9
9
  return object unless object
@@ -36,11 +36,10 @@ module ActiveSupport
36
36
  # (Backtrace)
37
37
  # # => "#<Object:0x007fb9b34c34b0>"
38
38
  class DeprecatedObjectProxy < DeprecationProxy
39
- def initialize(object, message, deprecator = nil)
39
+ def initialize(object, message, deprecator)
40
40
  @object = object
41
41
  @message = message
42
- ActiveSupport.deprecator.warn("DeprecatedObjectProxy without a deprecator is deprecated") unless deprecator
43
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
42
+ @deprecator = deprecator
44
43
  end
45
44
 
46
45
  private
@@ -86,12 +85,11 @@ module ActiveSupport
86
85
  # example.request.to_s
87
86
  # # => "special_request"
88
87
  class DeprecatedInstanceVariableProxy < DeprecationProxy
89
- def initialize(instance, method, var = "@#{method}", deprecator = nil)
88
+ def initialize(instance, method, var = "@#{method}", deprecator:)
90
89
  @instance = instance
91
90
  @method = method
92
91
  @var = var
93
- ActiveSupport.deprecator.warn("DeprecatedInstanceVariableProxy without a deprecator is deprecated") unless deprecator
94
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
92
+ @deprecator = deprecator
95
93
  end
96
94
 
97
95
  private
@@ -127,13 +125,12 @@ module ActiveSupport
127
125
  super
128
126
  end
129
127
 
130
- def initialize(old_const, new_const, deprecator = nil, message: "#{old_const} is deprecated! Use #{new_const} instead.")
128
+ def initialize(old_const, new_const, deprecator, message: "#{old_const} is deprecated! Use #{new_const} instead.")
131
129
  Kernel.require "active_support/inflector/methods"
132
130
 
133
131
  @old_const = old_const
134
132
  @new_const = new_const
135
- ActiveSupport.deprecator.warn("DeprecatedConstantProxy without a deprecator is deprecated") unless deprecator
136
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
133
+ @deprecator = deprecator
137
134
  @message = message
138
135
  end
139
136
 
@@ -183,9 +180,9 @@ module ActiveSupport
183
180
  target.const_get(name)
184
181
  end
185
182
 
186
- def method_missing(called, *args, &block)
183
+ def method_missing(...)
187
184
  @deprecator.warn(@message, caller_locations)
188
- target.__send__(called, *args, &block)
185
+ target.__send__(...)
189
186
  end
190
187
  end
191
188
  end
@@ -151,7 +151,12 @@ module ActiveSupport
151
151
  end
152
152
 
153
153
  def _extract_callstack(callstack)
154
- warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
154
+ ActiveSupport.deprecator.warn(<<~MESSAGE)
155
+ Passing the result of `caller` to ActiveSupport::Deprecation#warn is deprecated and will be removed in Rails 7.3.
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
@@ -163,11 +168,11 @@ module ActiveSupport
163
168
  end
164
169
  end
165
170
 
166
- RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
167
- LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
171
+ RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
172
+ LIB_DIR = RbConfig::CONFIG["libdir"]
168
173
 
169
174
  def ignored_callstack?(path)
170
- path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
175
+ path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
171
176
  end
172
177
  end
173
178
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "singleton"
4
-
5
3
  module ActiveSupport
6
4
  # = Active Support \Deprecation
7
5
  #
@@ -41,7 +39,6 @@ module ActiveSupport
41
39
  # a circular require warning for active_support/deprecation.rb.
42
40
  #
43
41
  # So, we define the constant first, and load dependencies later.
44
- require "active_support/deprecation/instance_delegator"
45
42
  require "active_support/deprecation/behaviors"
46
43
  require "active_support/deprecation/reporting"
47
44
  require "active_support/deprecation/disallowed"
@@ -52,12 +49,18 @@ module ActiveSupport
52
49
  require "active_support/core_ext/module/deprecation"
53
50
  require "concurrent/atomic/thread_local_var"
54
51
 
55
- include InstanceDelegator
56
52
  include Behavior
57
53
  include Reporting
58
54
  include Disallowed
59
55
  include MethodWrapper
60
56
 
57
+ MUTEX = Mutex.new # :nodoc:
58
+ private_constant :MUTEX
59
+
60
+ def self._instance # :nodoc:
61
+ @_instance ||= MUTEX.synchronize { @_instance ||= new }
62
+ end
63
+
61
64
  # The version number in which the deprecated behavior will be removed, by default.
62
65
  attr_accessor :deprecation_horizon
63
66
 
@@ -65,7 +68,7 @@ module ActiveSupport
65
68
  # and the second is a library name.
66
69
  #
67
70
  # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
68
- def initialize(deprecation_horizon = "7.2", gem_name = "Rails")
71
+ def initialize(deprecation_horizon = "7.3", gem_name = "Rails")
69
72
  self.gem_name = gem_name
70
73
  self.deprecation_horizon = deprecation_horizon
71
74
  # By default, warnings are not silenced and debugging is off.
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "weakref"
4
- require "active_support/ruby_features"
5
4
 
6
5
  module ActiveSupport
7
6
  # = Active Support Descendants Tracker
@@ -95,96 +94,19 @@ module ActiveSupport
95
94
  end
96
95
  end
97
96
 
98
- if RubyFeatures::CLASS_SUBCLASSES
99
- class << self
100
- def subclasses(klass)
101
- klass.subclasses
102
- end
103
-
104
- def descendants(klass)
105
- klass.descendants
106
- end
107
- end
108
-
109
- def descendants
110
- subclasses = DescendantsTracker.reject!(self.subclasses)
111
- subclasses.concat(subclasses.flat_map(&:descendants))
112
- end
113
- else
114
- # DescendantsArray is an array that contains weak references to classes.
115
- # Note: DescendantsArray is redundant with WeakSet, however WeakSet when used
116
- # on Ruby 2.7 or 3.0 can trigger a Ruby crash: https://bugs.ruby-lang.org/issues/18928
117
- class DescendantsArray # :nodoc:
118
- include Enumerable
119
-
120
- def initialize
121
- @refs = []
122
- end
123
-
124
- def <<(klass)
125
- @refs << WeakRef.new(klass)
126
- end
127
-
128
- def each
129
- @refs.reject! do |ref|
130
- yield ref.__getobj__
131
- false
132
- rescue WeakRef::RefError
133
- true
134
- end
135
- self
136
- end
137
-
138
- def refs_size
139
- @refs.size
140
- end
141
-
142
- def cleanup!
143
- @refs.delete_if { |ref| !ref.weakref_alive? }
144
- end
145
-
146
- def reject!
147
- @refs.reject! do |ref|
148
- yield ref.__getobj__
149
- rescue WeakRef::RefError
150
- true
151
- end
152
- end
153
- end
154
-
155
- @direct_descendants = {}
156
-
157
- class << self
158
- def subclasses(klass)
159
- descendants = @direct_descendants[klass]
160
- descendants ? DescendantsTracker.reject!(descendants.to_a) : []
161
- end
162
-
163
- def descendants(klass)
164
- subclasses = self.subclasses(klass)
165
- subclasses.concat(subclasses.flat_map { |k| descendants(k) })
166
- end
167
-
168
- # This is the only method that is not thread safe, but is only ever called
169
- # during the eager loading phase.
170
- def store_inherited(klass, descendant) # :nodoc:
171
- (@direct_descendants[klass] ||= DescendantsArray.new) << descendant
172
- end
173
- end
174
-
175
- def subclasses
176
- DescendantsTracker.subclasses(self)
97
+ class << self
98
+ def subclasses(klass)
99
+ klass.subclasses
177
100
  end
178
101
 
179
- def descendants
180
- DescendantsTracker.descendants(self)
102
+ def descendants(klass)
103
+ klass.descendants
181
104
  end
105
+ end
182
106
 
183
- private
184
- def inherited(base) # :nodoc:
185
- DescendantsTracker.store_inherited(self, base)
186
- super
187
- end
107
+ def descendants
108
+ subclasses = DescendantsTracker.reject!(self.subclasses)
109
+ subclasses.concat(subclasses.flat_map(&:descendants))
188
110
  end
189
111
  end
190
112
  end
@@ -102,12 +102,12 @@ module ActiveSupport
102
102
  raise_parsing_error("is empty duration") if parts.empty?
103
103
 
104
104
  # Mixing any of Y, M, D with W is invalid.
105
- if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
105
+ if parts.key?(:weeks) && parts.keys.intersect?(DATE_COMPONENTS)
106
106
  raise_parsing_error("mixing weeks with other date parts not allowed")
107
107
  end
108
108
 
109
109
  # Specifying an empty T part is invalid.
110
- if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
110
+ if mode == :time && !parts.keys.intersect?(TIME_COMPONENTS)
111
111
  raise_parsing_error("time part marker is present but time part is empty")
112
112
  end
113
113
 
@@ -35,7 +35,6 @@ module ActiveSupport
35
35
  # Return pair of duration's parts and whole duration sign.
36
36
  # Parts are summarized (as they can become repetitive due to addition, etc).
37
37
  # Zero parts are removed as not significant.
38
- # If all parts are negative it will negate all of them and return minus as a sign.
39
38
  def normalize
40
39
  parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
41
40
  p[k] += v unless v.zero?
@@ -50,7 +49,7 @@ module ActiveSupport
50
49
  end
51
50
 
52
51
  def week_mixed_with_date?(parts)
53
- parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
52
+ parts.key?(:weeks) && parts.keys.intersect?(DATE_COMPONENTS)
54
53
  end
55
54
 
56
55
  def format_seconds(seconds)
@@ -14,7 +14,7 @@ module ActiveSupport
14
14
  class Duration
15
15
  class Scalar < Numeric # :nodoc:
16
16
  attr_reader :value
17
- delegate :to_i, :to_f, :to_s, to: :value
17
+ delegate :to_i, :to_f, :to_s, to: :@value
18
18
 
19
19
  def initialize(value)
20
20
  @value = value
@@ -221,6 +221,8 @@ module ActiveSupport
221
221
  end
222
222
  end
223
223
 
224
+ Delegation.generate(self, [:to_f, :positive?, :negative?, :zero?, :abs], to: :@value, as: Integer, nilable: false)
225
+
224
226
  def initialize(value, parts, variable = nil) # :nodoc:
225
227
  @value, @parts = value, parts
226
228
  @parts.reject! { |k, v| v.zero? } unless value == 0
@@ -232,7 +234,10 @@ module ActiveSupport
232
234
  end
233
235
  end
234
236
 
235
- # Returns a copy of the parts hash that defines the duration
237
+ # Returns a copy of the parts hash that defines the duration.
238
+ #
239
+ # 5.minutes.parts # => {:minutes=>5}
240
+ # 3.years.parts # => {:years=>3}
236
241
  def parts
237
242
  @parts.dup
238
243
  end
@@ -366,8 +371,8 @@ module ActiveSupport
366
371
  # 1.year.to_i # => 31556952
367
372
  #
368
373
  # In such cases, Ruby's core
369
- # Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
370
- # Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
374
+ # Date[https://docs.ruby-lang.org/en/master/Date.html] and
375
+ # Time[https://docs.ruby-lang.org/en/master/Time.html] should be used for precision
371
376
  # date and time arithmetic.
372
377
  def to_i
373
378
  @value.to_i
@@ -504,8 +509,8 @@ module ActiveSupport
504
509
  value.respond_to?(method)
505
510
  end
506
511
 
507
- def method_missing(method, *args, &block)
508
- value.public_send(method, *args, &block)
512
+ def method_missing(...)
513
+ value.public_send(...)
509
514
  end
510
515
 
511
516
  def raise_type_error(other)