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
@@ -2,28 +2,6 @@
|
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
class Deprecation
|
5
|
-
# DeprecatedConstantAccessor transforms a constant into a deprecated one by
|
6
|
-
# hooking +const_missing+.
|
7
|
-
#
|
8
|
-
# It takes the names of an old (deprecated) constant and of a new constant
|
9
|
-
# (both in string form) and a deprecator.
|
10
|
-
#
|
11
|
-
# The deprecated constant now returns the same object as the new one rather
|
12
|
-
# than a proxy object, so it can be used transparently in +rescue+ blocks
|
13
|
-
# etc.
|
14
|
-
#
|
15
|
-
# PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
|
16
|
-
#
|
17
|
-
# # (In a later update, the original implementation of `PLANETS` has been removed.)
|
18
|
-
#
|
19
|
-
# PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
|
20
|
-
# include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
21
|
-
# deprecate_constant 'PLANETS', 'PLANETS_POST_2006', deprecator: ActiveSupport::Deprecation.new
|
22
|
-
#
|
23
|
-
# PLANETS.map { |planet| planet.capitalize }
|
24
|
-
# # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
|
25
|
-
# (Backtrace information…)
|
26
|
-
# ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
|
27
5
|
module DeprecatedConstantAccessor
|
28
6
|
def self.included(base)
|
29
7
|
require "active_support/inflector/methods"
|
@@ -39,11 +17,54 @@ module ActiveSupport
|
|
39
17
|
super
|
40
18
|
end
|
41
19
|
|
42
|
-
|
43
|
-
|
44
|
-
|
20
|
+
# Provides a way to rename constants with a deprecation cycle in which
|
21
|
+
# both the old and new names work, but using the old one prints a
|
22
|
+
# deprecation message.
|
23
|
+
#
|
24
|
+
# In order to rename <tt>A::B</tt> to <tt>C::D</tt>, you need to delete the
|
25
|
+
# definition of <tt>A::B</tt> and declare the deprecation in +A+:
|
26
|
+
#
|
27
|
+
# require "active_support/deprecation"
|
28
|
+
#
|
29
|
+
# module A
|
30
|
+
# include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
31
|
+
#
|
32
|
+
# deprecate_constant "B", "C::D", deprecator: ActiveSupport::Deprecation.new
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# The first argument is a constant name (no colons). It is the name of
|
36
|
+
# the constant you want to deprecate in the enclosing class or module.
|
37
|
+
#
|
38
|
+
# The second argument is the constant path of the replacement. That
|
39
|
+
# has to be a full path even if the replacement is defined in the same
|
40
|
+
# namespace as the deprecated one was.
|
41
|
+
#
|
42
|
+
# In both cases, strings and symbols are supported.
|
43
|
+
#
|
44
|
+
# The +deprecator+ keyword argument is the object that will print the
|
45
|
+
# deprecation message, an instance of ActiveSupport::Deprecation.
|
46
|
+
#
|
47
|
+
# With that in place, references to <tt>A::B</tt> still work, they
|
48
|
+
# evaluate to <tt>C::D</tt> now, and trigger a deprecation warning:
|
49
|
+
#
|
50
|
+
# DEPRECATION WARNING: A::B is deprecated! Use C::D instead.
|
51
|
+
# (called from ...)
|
52
|
+
#
|
53
|
+
# The message can be customized with the optional +message+ keyword
|
54
|
+
# argument.
|
55
|
+
#
|
56
|
+
# For this to work, a +const_missing+ hook is installed. When client
|
57
|
+
# code references the deprecated constant, the callback prints the
|
58
|
+
# message and constantizes the replacement.
|
59
|
+
#
|
60
|
+
# Caveat: If the deprecated constant name is reachable in a different
|
61
|
+
# namespace and Ruby constant lookup finds it, the hook won't be
|
62
|
+
# called and the deprecation won't work as intended. This may happen,
|
63
|
+
# for example, if an ancestor of the enclosing namespace has a
|
64
|
+
# constant with the same name. This is an unsupported edge case.
|
65
|
+
def deprecate_constant(old_constant_name, new_constant_path, deprecator:, message: nil)
|
45
66
|
class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
|
46
|
-
class_variable_get(:@@_deprecated_constants)[
|
67
|
+
class_variable_get(:@@_deprecated_constants)[old_constant_name.to_s] = { new: new_constant_path, message: message, deprecator: deprecator }
|
47
68
|
end
|
48
69
|
end
|
49
70
|
base.singleton_class.prepend extension
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveSupport
|
4
4
|
class Deprecation
|
5
5
|
class DeprecationProxy # :nodoc:
|
6
|
-
def self.new(*args, &block)
|
6
|
+
def self.new(*args, **kwargs, &block)
|
7
7
|
object = args.first
|
8
8
|
|
9
9
|
return object unless object
|
@@ -36,11 +36,10 @@ module ActiveSupport
|
|
36
36
|
# (Backtrace)
|
37
37
|
# # => "#<Object:0x007fb9b34c34b0>"
|
38
38
|
class DeprecatedObjectProxy < DeprecationProxy
|
39
|
-
def initialize(object, message, deprecator
|
39
|
+
def initialize(object, message, deprecator)
|
40
40
|
@object = object
|
41
41
|
@message = message
|
42
|
-
|
43
|
-
@deprecator = deprecator || ActiveSupport::Deprecation._instance
|
42
|
+
@deprecator = deprecator
|
44
43
|
end
|
45
44
|
|
46
45
|
private
|
@@ -86,12 +85,11 @@ module ActiveSupport
|
|
86
85
|
# example.request.to_s
|
87
86
|
# # => "special_request"
|
88
87
|
class DeprecatedInstanceVariableProxy < DeprecationProxy
|
89
|
-
def initialize(instance, method, var = "@#{method}", deprecator
|
88
|
+
def initialize(instance, method, var = "@#{method}", deprecator:)
|
90
89
|
@instance = instance
|
91
90
|
@method = method
|
92
91
|
@var = var
|
93
|
-
|
94
|
-
@deprecator = deprecator || ActiveSupport::Deprecation._instance
|
92
|
+
@deprecator = deprecator
|
95
93
|
end
|
96
94
|
|
97
95
|
private
|
@@ -127,13 +125,12 @@ module ActiveSupport
|
|
127
125
|
super
|
128
126
|
end
|
129
127
|
|
130
|
-
def initialize(old_const, new_const, deprecator
|
128
|
+
def initialize(old_const, new_const, deprecator, message: "#{old_const} is deprecated! Use #{new_const} instead.")
|
131
129
|
Kernel.require "active_support/inflector/methods"
|
132
130
|
|
133
131
|
@old_const = old_const
|
134
132
|
@new_const = new_const
|
135
|
-
|
136
|
-
@deprecator = deprecator || ActiveSupport::Deprecation._instance
|
133
|
+
@deprecator = deprecator
|
137
134
|
@message = message
|
138
135
|
end
|
139
136
|
|
@@ -183,9 +180,9 @@ module ActiveSupport
|
|
183
180
|
target.const_get(name)
|
184
181
|
end
|
185
182
|
|
186
|
-
def method_missing(
|
183
|
+
def method_missing(...)
|
187
184
|
@deprecator.warn(@message, caller_locations)
|
188
|
-
target.__send__(
|
185
|
+
target.__send__(...)
|
189
186
|
end
|
190
187
|
end
|
191
188
|
end
|
@@ -151,7 +151,12 @@ module ActiveSupport
|
|
151
151
|
end
|
152
152
|
|
153
153
|
def _extract_callstack(callstack)
|
154
|
-
warn
|
154
|
+
ActiveSupport.deprecator.warn(<<~MESSAGE)
|
155
|
+
Passing the result of `caller` to ActiveSupport::Deprecation#warn is deprecated and will be removed in Rails 8.0.
|
156
|
+
|
157
|
+
Please pass the result of `caller_locations` instead.
|
158
|
+
MESSAGE
|
159
|
+
|
155
160
|
offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
|
156
161
|
|
157
162
|
if offending_line
|
@@ -163,11 +168,11 @@ module ActiveSupport
|
|
163
168
|
end
|
164
169
|
end
|
165
170
|
|
166
|
-
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
|
167
|
-
LIB_DIR = RbConfig::CONFIG["libdir"]
|
171
|
+
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
|
172
|
+
LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
|
168
173
|
|
169
174
|
def ignored_callstack?(path)
|
170
|
-
path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
|
175
|
+
path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
|
171
176
|
end
|
172
177
|
end
|
173
178
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "singleton"
|
4
|
-
|
5
3
|
module ActiveSupport
|
6
4
|
# = Active Support \Deprecation
|
7
5
|
#
|
@@ -41,7 +39,6 @@ module ActiveSupport
|
|
41
39
|
# a circular require warning for active_support/deprecation.rb.
|
42
40
|
#
|
43
41
|
# So, we define the constant first, and load dependencies later.
|
44
|
-
require "active_support/deprecation/instance_delegator"
|
45
42
|
require "active_support/deprecation/behaviors"
|
46
43
|
require "active_support/deprecation/reporting"
|
47
44
|
require "active_support/deprecation/disallowed"
|
@@ -52,12 +49,18 @@ module ActiveSupport
|
|
52
49
|
require "active_support/core_ext/module/deprecation"
|
53
50
|
require "concurrent/atomic/thread_local_var"
|
54
51
|
|
55
|
-
include InstanceDelegator
|
56
52
|
include Behavior
|
57
53
|
include Reporting
|
58
54
|
include Disallowed
|
59
55
|
include MethodWrapper
|
60
56
|
|
57
|
+
MUTEX = Mutex.new # :nodoc:
|
58
|
+
private_constant :MUTEX
|
59
|
+
|
60
|
+
def self._instance # :nodoc:
|
61
|
+
@_instance ||= MUTEX.synchronize { @_instance ||= new }
|
62
|
+
end
|
63
|
+
|
61
64
|
# The version number in which the deprecated behavior will be removed, by default.
|
62
65
|
attr_accessor :deprecation_horizon
|
63
66
|
|
@@ -65,7 +68,7 @@ module ActiveSupport
|
|
65
68
|
# and the second is a library name.
|
66
69
|
#
|
67
70
|
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
|
68
|
-
def initialize(deprecation_horizon = "
|
71
|
+
def initialize(deprecation_horizon = "8.0", gem_name = "Rails")
|
69
72
|
self.gem_name = gem_name
|
70
73
|
self.deprecation_horizon = deprecation_horizon
|
71
74
|
# By default, warnings are not silenced and debugging is off.
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "weakref"
|
4
|
-
require "active_support/ruby_features"
|
5
4
|
|
6
5
|
module ActiveSupport
|
7
6
|
# = Active Support Descendants Tracker
|
@@ -95,96 +94,19 @@ module ActiveSupport
|
|
95
94
|
end
|
96
95
|
end
|
97
96
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
klass.subclasses
|
102
|
-
end
|
103
|
-
|
104
|
-
def descendants(klass)
|
105
|
-
klass.descendants
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def descendants
|
110
|
-
subclasses = DescendantsTracker.reject!(self.subclasses)
|
111
|
-
subclasses.concat(subclasses.flat_map(&:descendants))
|
112
|
-
end
|
113
|
-
else
|
114
|
-
# DescendantsArray is an array that contains weak references to classes.
|
115
|
-
# Note: DescendantsArray is redundant with WeakSet, however WeakSet when used
|
116
|
-
# on Ruby 2.7 or 3.0 can trigger a Ruby crash: https://bugs.ruby-lang.org/issues/18928
|
117
|
-
class DescendantsArray # :nodoc:
|
118
|
-
include Enumerable
|
119
|
-
|
120
|
-
def initialize
|
121
|
-
@refs = []
|
122
|
-
end
|
123
|
-
|
124
|
-
def <<(klass)
|
125
|
-
@refs << WeakRef.new(klass)
|
126
|
-
end
|
127
|
-
|
128
|
-
def each
|
129
|
-
@refs.reject! do |ref|
|
130
|
-
yield ref.__getobj__
|
131
|
-
false
|
132
|
-
rescue WeakRef::RefError
|
133
|
-
true
|
134
|
-
end
|
135
|
-
self
|
136
|
-
end
|
137
|
-
|
138
|
-
def refs_size
|
139
|
-
@refs.size
|
140
|
-
end
|
141
|
-
|
142
|
-
def cleanup!
|
143
|
-
@refs.delete_if { |ref| !ref.weakref_alive? }
|
144
|
-
end
|
145
|
-
|
146
|
-
def reject!
|
147
|
-
@refs.reject! do |ref|
|
148
|
-
yield ref.__getobj__
|
149
|
-
rescue WeakRef::RefError
|
150
|
-
true
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
@direct_descendants = {}
|
156
|
-
|
157
|
-
class << self
|
158
|
-
def subclasses(klass)
|
159
|
-
descendants = @direct_descendants[klass]
|
160
|
-
descendants ? DescendantsTracker.reject!(descendants.to_a) : []
|
161
|
-
end
|
162
|
-
|
163
|
-
def descendants(klass)
|
164
|
-
subclasses = self.subclasses(klass)
|
165
|
-
subclasses.concat(subclasses.flat_map { |k| descendants(k) })
|
166
|
-
end
|
167
|
-
|
168
|
-
# This is the only method that is not thread safe, but is only ever called
|
169
|
-
# during the eager loading phase.
|
170
|
-
def store_inherited(klass, descendant) # :nodoc:
|
171
|
-
(@direct_descendants[klass] ||= DescendantsArray.new) << descendant
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def subclasses
|
176
|
-
DescendantsTracker.subclasses(self)
|
97
|
+
class << self
|
98
|
+
def subclasses(klass)
|
99
|
+
klass.subclasses
|
177
100
|
end
|
178
101
|
|
179
|
-
def descendants
|
180
|
-
|
102
|
+
def descendants(klass)
|
103
|
+
klass.descendants
|
181
104
|
end
|
105
|
+
end
|
182
106
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
super
|
187
|
-
end
|
107
|
+
def descendants
|
108
|
+
subclasses = DescendantsTracker.reject!(self.subclasses)
|
109
|
+
subclasses.concat(subclasses.flat_map(&:descendants))
|
188
110
|
end
|
189
111
|
end
|
190
112
|
end
|
@@ -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) &&
|
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 &&
|
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) &&
|
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:
|
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-
|
370
|
-
# Time[https://ruby-
|
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(
|
508
|
-
value.public_send(
|
512
|
+
def method_missing(...)
|
513
|
+
value.public_send(...)
|
509
514
|
end
|
510
515
|
|
511
516
|
def raise_type_error(other)
|
@@ -26,12 +26,16 @@ module ActiveSupport
|
|
26
26
|
class ErrorReporter
|
27
27
|
SEVERITIES = %i(error warning info)
|
28
28
|
DEFAULT_SOURCE = "application"
|
29
|
+
DEFAULT_RESCUE = [StandardError].freeze
|
29
30
|
|
30
|
-
attr_accessor :logger
|
31
|
+
attr_accessor :logger, :debug_mode
|
32
|
+
|
33
|
+
UnexpectedError = Class.new(Exception)
|
31
34
|
|
32
35
|
def initialize(*subscribers, logger: nil)
|
33
36
|
@subscribers = subscribers.flatten
|
34
37
|
@logger = logger
|
38
|
+
@debug_mode = false
|
35
39
|
end
|
36
40
|
|
37
41
|
# Evaluates the given block, reporting and swallowing any unhandled error.
|
@@ -72,7 +76,7 @@ module ActiveSupport
|
|
72
76
|
# source of the error. Subscribers can use this value to ignore certain
|
73
77
|
# errors. Defaults to <tt>"application"</tt>.
|
74
78
|
def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
|
75
|
-
error_classes =
|
79
|
+
error_classes = DEFAULT_RESCUE if error_classes.empty?
|
76
80
|
yield
|
77
81
|
rescue *error_classes => error
|
78
82
|
report(error, handled: true, severity: severity, context: context, source: source)
|
@@ -108,13 +112,47 @@ module ActiveSupport
|
|
108
112
|
# source of the error. Subscribers can use this value to ignore certain
|
109
113
|
# errors. Defaults to <tt>"application"</tt>.
|
110
114
|
def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
|
111
|
-
error_classes =
|
115
|
+
error_classes = DEFAULT_RESCUE if error_classes.empty?
|
112
116
|
yield
|
113
117
|
rescue *error_classes => error
|
114
118
|
report(error, handled: false, severity: severity, context: context, source: source)
|
115
119
|
raise
|
116
120
|
end
|
117
121
|
|
122
|
+
# Either report the given error when in production, or raise it when in development or test.
|
123
|
+
#
|
124
|
+
# When called in production, after the error is reported, this method will return
|
125
|
+
# nil and execution will continue.
|
126
|
+
#
|
127
|
+
# When called in development, the original error is wrapped in a different error class to ensure
|
128
|
+
# it's not being rescued higher in the stack and will be surfaced to the developer.
|
129
|
+
#
|
130
|
+
# This method is intended for reporting violated assertions about preconditions, or similar
|
131
|
+
# cases that can and should be gracefully handled in production, but that aren't supposed to happen.
|
132
|
+
#
|
133
|
+
# The error can be either an exception instance or a String.
|
134
|
+
#
|
135
|
+
# example:
|
136
|
+
#
|
137
|
+
# def edit
|
138
|
+
# if published?
|
139
|
+
# Rails.error.unexpected("[BUG] Attempting to edit a published article, that shouldn't be possible")
|
140
|
+
# return false
|
141
|
+
# end
|
142
|
+
# # ...
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
def unexpected(error, severity: :warning, context: {}, source: DEFAULT_SOURCE)
|
146
|
+
error = RuntimeError.new(error) if error.is_a?(String)
|
147
|
+
error.set_backtrace(caller(1)) if error.backtrace.nil?
|
148
|
+
|
149
|
+
if @debug_mode
|
150
|
+
raise UnexpectedError, "#{error.class.name}: #{error.message}", error.backtrace, cause: error
|
151
|
+
else
|
152
|
+
report(error, handled: true, severity: severity, context: context, source: source)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
118
156
|
# Register a new error subscriber. The subscriber must respond to
|
119
157
|
#
|
120
158
|
# report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
module ForkTracker # :nodoc:
|
5
|
-
module
|
5
|
+
module CoreExt
|
6
6
|
def _fork
|
7
7
|
pid = super
|
8
8
|
if pid == 0
|
@@ -12,27 +12,6 @@ module ActiveSupport
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
module CoreExt
|
16
|
-
def fork(...)
|
17
|
-
if block_given?
|
18
|
-
super do
|
19
|
-
ForkTracker.check!
|
20
|
-
yield
|
21
|
-
end
|
22
|
-
else
|
23
|
-
unless pid = super
|
24
|
-
ForkTracker.check!
|
25
|
-
end
|
26
|
-
pid
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
module CoreExtPrivate
|
32
|
-
include CoreExt
|
33
|
-
private :fork
|
34
|
-
end
|
35
|
-
|
36
15
|
@pid = Process.pid
|
37
16
|
@callbacks = []
|
38
17
|
|
@@ -45,23 +24,8 @@ module ActiveSupport
|
|
45
24
|
end
|
46
25
|
end
|
47
26
|
|
48
|
-
if Process.respond_to?(:_fork) # Ruby 3.1+
|
49
|
-
def check!
|
50
|
-
# We trust the `_fork` callback
|
51
|
-
end
|
52
|
-
else
|
53
|
-
alias_method :check!, :after_fork_callback
|
54
|
-
end
|
55
|
-
|
56
27
|
def hook!
|
57
|
-
|
58
|
-
::Process.singleton_class.prepend(ModernCoreExt)
|
59
|
-
elsif Process.respond_to?(:fork)
|
60
|
-
::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
|
61
|
-
::Kernel.prepend(CoreExtPrivate)
|
62
|
-
::Kernel.singleton_class.prepend(CoreExt)
|
63
|
-
::Process.singleton_class.prepend(CoreExt)
|
64
|
-
end
|
28
|
+
::Process.singleton_class.prepend(CoreExt)
|
65
29
|
end
|
66
30
|
|
67
31
|
def after_fork(&block)
|
@@ -387,15 +387,13 @@ module ActiveSupport
|
|
387
387
|
_new_hash
|
388
388
|
end
|
389
389
|
|
390
|
+
def to_proc
|
391
|
+
proc { |key| self[key] }
|
392
|
+
end
|
393
|
+
|
390
394
|
private
|
391
|
-
|
392
|
-
|
393
|
-
key.kind_of?(Symbol) ? key.name : key
|
394
|
-
end
|
395
|
-
else
|
396
|
-
def convert_key(key)
|
397
|
-
key.kind_of?(Symbol) ? key.to_s : key
|
398
|
-
end
|
395
|
+
def convert_key(key)
|
396
|
+
Symbol === key ? key.name : key
|
399
397
|
end
|
400
398
|
|
401
399
|
def convert_value(value, conversion: nil)
|
@@ -9,11 +9,14 @@ module ActiveSupport
|
|
9
9
|
html_safe_options = html_escape_translation_options(options)
|
10
10
|
|
11
11
|
exception = false
|
12
|
+
|
12
13
|
exception_handler = ->(*args) do
|
13
14
|
exception = true
|
14
15
|
I18n.exception_handler.call(*args)
|
15
16
|
end
|
17
|
+
|
16
18
|
translation = I18n.translate(key, **html_safe_options, exception_handler: exception_handler)
|
19
|
+
|
17
20
|
if exception
|
18
21
|
translation
|
19
22
|
else
|
@@ -24,11 +27,11 @@ module ActiveSupport
|
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
30
|
+
def html_safe_translation_key?(key)
|
31
|
+
/(?:_|\b)html\z/.match?(key)
|
32
|
+
end
|
31
33
|
|
34
|
+
private
|
32
35
|
def html_escape_translation_options(options)
|
33
36
|
options.each do |name, value|
|
34
37
|
unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
|