activesupport 6.1.4.1 → 7.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +201 -489
- data/MIT-LICENSE +1 -1
- data/lib/active_support/actionable_error.rb +1 -1
- data/lib/active_support/array_inquirer.rb +0 -2
- data/lib/active_support/benchmarkable.rb +2 -2
- data/lib/active_support/cache/file_store.rb +15 -9
- data/lib/active_support/cache/mem_cache_store.rb +127 -32
- data/lib/active_support/cache/memory_store.rb +23 -15
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +42 -67
- data/lib/active_support/cache/strategy/local_cache.rb +35 -61
- data/lib/active_support/cache.rb +189 -45
- data/lib/active_support/callbacks.rb +180 -81
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +5 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +6 -3
- data/lib/active_support/configuration_file.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +9 -7
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/subclasses.rb +4 -2
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +4 -4
- data/lib/active_support/core_ext/date/conversions.rb +3 -3
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +64 -12
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
- data/lib/active_support/core_ext/module/delegation.rb +2 -8
- data/lib/active_support/core_ext/name_error.rb +2 -8
- data/lib/active_support/core_ext/numeric/conversions.rb +79 -76
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +11 -0
- data/lib/active_support/core_ext/object/json.rb +29 -24
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with_options.rb +20 -1
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +0 -25
- data/lib/active_support/core_ext/range/conversions.rb +8 -8
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -25
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +60 -36
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
- data/lib/active_support/core_ext/time/calculations.rb +7 -6
- data/lib/active_support/core_ext/time/conversions.rb +4 -3
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +4 -19
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -27
- data/lib/active_support/core_ext.rb +1 -0
- data/lib/active_support/current_attributes.rb +31 -14
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -788
- data/lib/active_support/deprecation/behaviors.rb +4 -1
- data/lib/active_support/deprecation/method_wrappers.rb +3 -3
- data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/descendants_tracker.rb +177 -68
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +9 -1
- data/lib/active_support/duration.rb +77 -48
- data/lib/active_support/encrypted_configuration.rb +11 -1
- data/lib/active_support/encrypted_file.rb +1 -1
- data/lib/active_support/environment_inquirer.rb +1 -1
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +1 -1
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +30 -4
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/fork_tracker.rb +19 -12
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +3 -1
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +1 -1
- data/lib/active_support/inflector/inflections.rb +23 -7
- data/lib/active_support/inflector/methods.rb +24 -48
- data/lib/active_support/isolated_execution_state.rb +56 -0
- data/lib/active_support/json/encoding.rb +3 -3
- data/lib/active_support/key_generator.rb +18 -1
- data/lib/active_support/locale/en.yml +1 -1
- data/lib/active_support/log_subscriber.rb +13 -3
- data/lib/active_support/logger_thread_safe_level.rb +4 -13
- data/lib/active_support/message_encryptor.rb +8 -3
- data/lib/active_support/message_verifier.rb +4 -4
- data/lib/active_support/messages/metadata.rb +2 -2
- data/lib/active_support/multibyte/chars.rb +10 -11
- data/lib/active_support/multibyte/unicode.rb +0 -12
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +91 -65
- data/lib/active_support/notifications/instrumenter.rb +32 -15
- data/lib/active_support/notifications.rb +15 -21
- data/lib/active_support/number_helper/number_converter.rb +1 -3
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
- data/lib/active_support/number_helper/rounding_helper.rb +1 -5
- data/lib/active_support/number_helper.rb +0 -2
- data/lib/active_support/option_merger.rb +8 -16
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/parameter_filter.rb +5 -0
- data/lib/active_support/per_thread_registry.rb +5 -0
- data/lib/active_support/railtie.rb +69 -19
- data/lib/active_support/rescuable.rb +2 -2
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +1 -1
- data/lib/active_support/string_inquirer.rb +0 -2
- data/lib/active_support/subscriber.rb +7 -18
- data/lib/active_support/tagged_logging.rb +2 -2
- data/lib/active_support/test_case.rb +9 -21
- data/lib/active_support/testing/assertions.rb +35 -5
- data/lib/active_support/testing/deprecation.rb +52 -1
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +5 -5
- data/lib/active_support/testing/parallelization/server.rb +4 -0
- data/lib/active_support/testing/parallelization/worker.rb +3 -0
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +3 -5
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +13 -2
- data/lib/active_support/time_with_zone.rb +53 -12
- data/lib/active_support/values/time_zone.rb +30 -9
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +4 -4
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +5 -4
- data/lib/active_support.rb +17 -1
- metadata +27 -24
- data/lib/active_support/core_ext/marshal.rb +0 -26
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -11,7 +11,7 @@ module ActiveSupport
|
|
11
11
|
#
|
12
12
|
# 1.month.ago # equivalent to Time.now.advance(months: -1)
|
13
13
|
class Duration
|
14
|
-
class Scalar < Numeric
|
14
|
+
class Scalar < Numeric # :nodoc:
|
15
15
|
attr_reader :value
|
16
16
|
delegate :to_i, :to_f, :to_s, to: :value
|
17
17
|
|
@@ -39,11 +39,11 @@ module ActiveSupport
|
|
39
39
|
|
40
40
|
def +(other)
|
41
41
|
if Duration === other
|
42
|
-
seconds = value + other.
|
43
|
-
new_parts = other.
|
42
|
+
seconds = value + other._parts.fetch(:seconds, 0)
|
43
|
+
new_parts = other._parts.merge(seconds: seconds)
|
44
44
|
new_value = value + other.value
|
45
45
|
|
46
|
-
Duration.new(new_value, new_parts)
|
46
|
+
Duration.new(new_value, new_parts, other.variable?)
|
47
47
|
else
|
48
48
|
calculate(:+, other)
|
49
49
|
end
|
@@ -51,12 +51,12 @@ module ActiveSupport
|
|
51
51
|
|
52
52
|
def -(other)
|
53
53
|
if Duration === other
|
54
|
-
seconds = value - other.
|
55
|
-
new_parts = other.
|
54
|
+
seconds = value - other._parts.fetch(:seconds, 0)
|
55
|
+
new_parts = other._parts.transform_values(&:-@)
|
56
56
|
new_parts = new_parts.merge(seconds: seconds)
|
57
57
|
new_value = value - other.value
|
58
58
|
|
59
|
-
Duration.new(new_value, new_parts)
|
59
|
+
Duration.new(new_value, new_parts, other.variable?)
|
60
60
|
else
|
61
61
|
calculate(:-, other)
|
62
62
|
end
|
@@ -64,10 +64,10 @@ module ActiveSupport
|
|
64
64
|
|
65
65
|
def *(other)
|
66
66
|
if Duration === other
|
67
|
-
new_parts = other.
|
67
|
+
new_parts = other._parts.transform_values { |other_value| value * other_value }
|
68
68
|
new_value = value * other.value
|
69
69
|
|
70
|
-
Duration.new(new_value, new_parts)
|
70
|
+
Duration.new(new_value, new_parts, other.variable?)
|
71
71
|
else
|
72
72
|
calculate(:*, other)
|
73
73
|
end
|
@@ -89,6 +89,10 @@ module ActiveSupport
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
def variable? # :nodoc:
|
93
|
+
false
|
94
|
+
end
|
95
|
+
|
92
96
|
private
|
93
97
|
def calculate(op, other)
|
94
98
|
if Scalar === other
|
@@ -123,8 +127,9 @@ module ActiveSupport
|
|
123
127
|
}.freeze
|
124
128
|
|
125
129
|
PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
|
130
|
+
VARIABLE_PARTS = [:years, :months, :weeks, :days].freeze
|
126
131
|
|
127
|
-
|
132
|
+
attr_reader :value
|
128
133
|
|
129
134
|
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
|
130
135
|
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
|
@@ -140,38 +145,38 @@ module ActiveSupport
|
|
140
145
|
new(calculate_total_seconds(parts), parts)
|
141
146
|
end
|
142
147
|
|
143
|
-
def ===(other)
|
148
|
+
def ===(other) # :nodoc:
|
144
149
|
other.is_a?(Duration)
|
145
150
|
rescue ::NoMethodError
|
146
151
|
false
|
147
152
|
end
|
148
153
|
|
149
|
-
def seconds(value)
|
150
|
-
new(value, seconds: value)
|
154
|
+
def seconds(value) # :nodoc:
|
155
|
+
new(value, { seconds: value }, false)
|
151
156
|
end
|
152
157
|
|
153
|
-
def minutes(value)
|
154
|
-
new(value * SECONDS_PER_MINUTE, minutes: value)
|
158
|
+
def minutes(value) # :nodoc:
|
159
|
+
new(value * SECONDS_PER_MINUTE, { minutes: value }, false)
|
155
160
|
end
|
156
161
|
|
157
|
-
def hours(value)
|
158
|
-
new(value * SECONDS_PER_HOUR, hours: value)
|
162
|
+
def hours(value) # :nodoc:
|
163
|
+
new(value * SECONDS_PER_HOUR, { hours: value }, false)
|
159
164
|
end
|
160
165
|
|
161
|
-
def days(value)
|
162
|
-
new(value * SECONDS_PER_DAY, days: value)
|
166
|
+
def days(value) # :nodoc:
|
167
|
+
new(value * SECONDS_PER_DAY, { days: value }, true)
|
163
168
|
end
|
164
169
|
|
165
|
-
def weeks(value)
|
166
|
-
new(value * SECONDS_PER_WEEK, weeks: value)
|
170
|
+
def weeks(value) # :nodoc:
|
171
|
+
new(value * SECONDS_PER_WEEK, { weeks: value }, true)
|
167
172
|
end
|
168
173
|
|
169
|
-
def months(value)
|
170
|
-
new(value * SECONDS_PER_MONTH, months: value)
|
174
|
+
def months(value) # :nodoc:
|
175
|
+
new(value * SECONDS_PER_MONTH, { months: value }, true)
|
171
176
|
end
|
172
177
|
|
173
|
-
def years(value)
|
174
|
-
new(value * SECONDS_PER_YEAR, years: value)
|
178
|
+
def years(value) # :nodoc:
|
179
|
+
new(value * SECONDS_PER_YEAR, { years: value }, true)
|
175
180
|
end
|
176
181
|
|
177
182
|
# Creates a new Duration from a seconds value that is converted
|
@@ -187,18 +192,23 @@ module ActiveSupport
|
|
187
192
|
|
188
193
|
parts = {}
|
189
194
|
remainder = value.round(9)
|
195
|
+
variable = false
|
190
196
|
|
191
197
|
PARTS.each do |part|
|
192
198
|
unless part == :seconds
|
193
199
|
part_in_seconds = PARTS_IN_SECONDS[part]
|
194
200
|
parts[part] = remainder.div(part_in_seconds)
|
195
201
|
remainder %= part_in_seconds
|
202
|
+
|
203
|
+
unless parts[part].zero?
|
204
|
+
variable ||= VARIABLE_PARTS.include?(part)
|
205
|
+
end
|
196
206
|
end
|
197
207
|
end unless value == 0
|
198
208
|
|
199
209
|
parts[:seconds] = remainder
|
200
210
|
|
201
|
-
new(value, parts)
|
211
|
+
new(value, parts, variable)
|
202
212
|
end
|
203
213
|
|
204
214
|
private
|
@@ -209,12 +219,23 @@ module ActiveSupport
|
|
209
219
|
end
|
210
220
|
end
|
211
221
|
|
212
|
-
def initialize(value, parts)
|
222
|
+
def initialize(value, parts, variable = nil) # :nodoc:
|
213
223
|
@value, @parts = value, parts
|
214
224
|
@parts.reject! { |k, v| v.zero? } unless value == 0
|
225
|
+
@parts.freeze
|
226
|
+
@variable = variable
|
227
|
+
|
228
|
+
if @variable.nil?
|
229
|
+
@variable = @parts.any? { |part, _| VARIABLE_PARTS.include?(part) }
|
230
|
+
end
|
215
231
|
end
|
216
232
|
|
217
|
-
|
233
|
+
# Returns a copy of the parts hash that defines the duration
|
234
|
+
def parts
|
235
|
+
@parts.dup
|
236
|
+
end
|
237
|
+
|
238
|
+
def coerce(other) # :nodoc:
|
218
239
|
case other
|
219
240
|
when Scalar
|
220
241
|
[other, self]
|
@@ -239,13 +260,13 @@ module ActiveSupport
|
|
239
260
|
# are treated as seconds.
|
240
261
|
def +(other)
|
241
262
|
if Duration === other
|
242
|
-
parts = @parts.merge(other.
|
263
|
+
parts = @parts.merge(other._parts) do |_key, value, other_value|
|
243
264
|
value + other_value
|
244
265
|
end
|
245
|
-
Duration.new(value + other.value, parts)
|
266
|
+
Duration.new(value + other.value, parts, @variable || other.variable?)
|
246
267
|
else
|
247
268
|
seconds = @parts.fetch(:seconds, 0) + other
|
248
|
-
Duration.new(value + other, @parts.merge(seconds: seconds))
|
269
|
+
Duration.new(value + other, @parts.merge(seconds: seconds), @variable)
|
249
270
|
end
|
250
271
|
end
|
251
272
|
|
@@ -258,9 +279,9 @@ module ActiveSupport
|
|
258
279
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
259
280
|
def *(other)
|
260
281
|
if Scalar === other || Duration === other
|
261
|
-
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
282
|
+
Duration.new(value * other.value, @parts.transform_values { |number| number * other.value }, @variable || other.variable?)
|
262
283
|
elsif Numeric === other
|
263
|
-
Duration.new(value * other, parts.transform_values { |number| number * other })
|
284
|
+
Duration.new(value * other, @parts.transform_values { |number| number * other }, @variable)
|
264
285
|
else
|
265
286
|
raise_type_error(other)
|
266
287
|
end
|
@@ -269,11 +290,11 @@ module ActiveSupport
|
|
269
290
|
# Divides this Duration by a Numeric and returns a new Duration.
|
270
291
|
def /(other)
|
271
292
|
if Scalar === other
|
272
|
-
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
293
|
+
Duration.new(value / other.value, @parts.transform_values { |number| number / other.value }, @variable)
|
273
294
|
elsif Duration === other
|
274
295
|
value / other.value
|
275
296
|
elsif Numeric === other
|
276
|
-
Duration.new(value / other, parts.transform_values { |number| number / other })
|
297
|
+
Duration.new(value / other, @parts.transform_values { |number| number / other }, @variable)
|
277
298
|
else
|
278
299
|
raise_type_error(other)
|
279
300
|
end
|
@@ -291,15 +312,15 @@ module ActiveSupport
|
|
291
312
|
end
|
292
313
|
end
|
293
314
|
|
294
|
-
def -@
|
295
|
-
Duration.new(-value, parts.transform_values(&:-@))
|
315
|
+
def -@ # :nodoc:
|
316
|
+
Duration.new(-value, @parts.transform_values(&:-@), @variable)
|
296
317
|
end
|
297
318
|
|
298
|
-
def +@
|
319
|
+
def +@ # :nodoc:
|
299
320
|
self
|
300
321
|
end
|
301
322
|
|
302
|
-
def is_a?(klass)
|
323
|
+
def is_a?(klass) # :nodoc:
|
303
324
|
Duration == klass || value.is_a?(klass)
|
304
325
|
end
|
305
326
|
alias :kind_of? :is_a?
|
@@ -419,24 +440,24 @@ module ActiveSupport
|
|
419
440
|
alias :until :ago
|
420
441
|
alias :before :ago
|
421
442
|
|
422
|
-
def inspect
|
423
|
-
return "#{value} seconds" if parts.empty?
|
443
|
+
def inspect # :nodoc:
|
444
|
+
return "#{value} seconds" if @parts.empty?
|
424
445
|
|
425
|
-
parts.
|
446
|
+
@parts.
|
426
447
|
sort_by { |unit, _ | PARTS.index(unit) }.
|
427
448
|
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
428
|
-
to_sentence(locale:
|
449
|
+
to_sentence(locale: false)
|
429
450
|
end
|
430
451
|
|
431
|
-
def as_json(options = nil)
|
452
|
+
def as_json(options = nil) # :nodoc:
|
432
453
|
to_i
|
433
454
|
end
|
434
455
|
|
435
|
-
def init_with(coder)
|
456
|
+
def init_with(coder) # :nodoc:
|
436
457
|
initialize(coder["value"], coder["parts"])
|
437
458
|
end
|
438
459
|
|
439
|
-
def encode_with(coder)
|
460
|
+
def encode_with(coder) # :nodoc:
|
440
461
|
coder.map = { "value" => @value, "parts" => @parts }
|
441
462
|
end
|
442
463
|
|
@@ -446,16 +467,24 @@ module ActiveSupport
|
|
446
467
|
ISO8601Serializer.new(self, precision: precision).serialize
|
447
468
|
end
|
448
469
|
|
470
|
+
def variable? # :nodoc:
|
471
|
+
@variable
|
472
|
+
end
|
473
|
+
|
474
|
+
def _parts # :nodoc:
|
475
|
+
@parts
|
476
|
+
end
|
477
|
+
|
449
478
|
private
|
450
479
|
def sum(sign, time = ::Time.current)
|
451
480
|
unless time.acts_like?(:time) || time.acts_like?(:date)
|
452
481
|
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
453
482
|
end
|
454
483
|
|
455
|
-
if parts.empty?
|
484
|
+
if @parts.empty?
|
456
485
|
time.since(sign * value)
|
457
486
|
else
|
458
|
-
parts.inject(time) do |t, (type, number)|
|
487
|
+
@parts.inject(time) do |t, (type, number)|
|
459
488
|
if type == :seconds
|
460
489
|
t.since(sign * number)
|
461
490
|
elsif type == :minutes
|
@@ -34,8 +34,18 @@ module ActiveSupport
|
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
|
+
def deep_transform(hash)
|
38
|
+
return hash unless hash.is_a?(Hash)
|
39
|
+
|
40
|
+
h = ActiveSupport::InheritableOptions.new
|
41
|
+
hash.each do |k, v|
|
42
|
+
h[k] = deep_transform(v)
|
43
|
+
end
|
44
|
+
h
|
45
|
+
end
|
46
|
+
|
37
47
|
def options
|
38
|
-
@options ||= ActiveSupport::InheritableOptions.new(config)
|
48
|
+
@options ||= ActiveSupport::InheritableOptions.new(deep_transform(config))
|
39
49
|
end
|
40
50
|
|
41
51
|
def deserialize(config)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/string_inquirer"
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
|
-
class EnvironmentInquirer < StringInquirer
|
6
|
+
class EnvironmentInquirer < StringInquirer # :nodoc:
|
7
7
|
DEFAULT_ENVIRONMENTS = ["development", "test", "production"]
|
8
8
|
def initialize(env)
|
9
9
|
super(env)
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
# +ActiveSupport::ErrorReporter+ is a common interface for error reporting services.
|
5
|
+
#
|
6
|
+
# To rescue and report any unhandled error, you can use the +handle+ method:
|
7
|
+
#
|
8
|
+
# Rails.error.handle do
|
9
|
+
# do_something!
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# If an error is raised, it will be reported and swallowed.
|
13
|
+
#
|
14
|
+
# Alternatively if you want to report the error but not swallow it, you can use +record+
|
15
|
+
#
|
16
|
+
# Rails.error.record do
|
17
|
+
# do_something!
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Both methods can be restricted to only handle a specific exception class
|
21
|
+
#
|
22
|
+
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
|
23
|
+
#
|
24
|
+
# You can also pass some extra context information that may be used by the error subscribers:
|
25
|
+
#
|
26
|
+
# Rails.error.handle(context: { section: "admin" }) do
|
27
|
+
# # ...
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Additionally a +severity+ can be passed along to communicate how important the error report is.
|
31
|
+
# +severity+ can be one of +:error+, +:warning+ or +:info+. Handled errors default to the +:warning+
|
32
|
+
# severity, and unhandled ones to +error+.
|
33
|
+
#
|
34
|
+
# Both +handle+ and +record+ pass through the return value from the block. In the case of +handle+
|
35
|
+
# rescuing an error, a fallback can be provided. The fallback must be a callable whose result will
|
36
|
+
# be returned when the block raises and is handled:
|
37
|
+
#
|
38
|
+
# user = Rails.error.handle(fallback: -> { User.anonymous }) do
|
39
|
+
# User.find_by(params)
|
40
|
+
# end
|
41
|
+
class ErrorReporter
|
42
|
+
SEVERITIES = %i(error warning info)
|
43
|
+
|
44
|
+
attr_accessor :logger
|
45
|
+
|
46
|
+
def initialize(*subscribers, logger: nil)
|
47
|
+
@subscribers = subscribers.flatten
|
48
|
+
@logger = logger
|
49
|
+
end
|
50
|
+
|
51
|
+
# Report any unhandled exception, and swallow it.
|
52
|
+
#
|
53
|
+
# Rails.error.handle do
|
54
|
+
# 1 + '1'
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil)
|
58
|
+
yield
|
59
|
+
rescue error_class => error
|
60
|
+
report(error, handled: true, severity: severity, context: context)
|
61
|
+
fallback.call if fallback
|
62
|
+
end
|
63
|
+
|
64
|
+
def record(error_class = StandardError, severity: :error, context: {})
|
65
|
+
yield
|
66
|
+
rescue error_class => error
|
67
|
+
report(error, handled: false, severity: severity, context: context)
|
68
|
+
raise
|
69
|
+
end
|
70
|
+
|
71
|
+
# Register a new error subscriber. The subscriber must respond to
|
72
|
+
#
|
73
|
+
# report(Exception, handled: Boolean, context: Hash)
|
74
|
+
#
|
75
|
+
# The +report+ method +should+ never raise an error.
|
76
|
+
def subscribe(subscriber)
|
77
|
+
unless subscriber.respond_to?(:report)
|
78
|
+
raise ArgumentError, "Error subscribers must respond to #report"
|
79
|
+
end
|
80
|
+
@subscribers << subscriber
|
81
|
+
end
|
82
|
+
|
83
|
+
# Update the execution context that is accessible to error subscribers
|
84
|
+
#
|
85
|
+
# Rails.error.set_context(section: "checkout", user_id: @user.id)
|
86
|
+
#
|
87
|
+
# See +ActiveSupport::ExecutionContext.set+
|
88
|
+
def set_context(...)
|
89
|
+
ActiveSupport::ExecutionContext.set(...)
|
90
|
+
end
|
91
|
+
|
92
|
+
# When the block based +handle+ and +record+ methods are not suitable, you can directly use +report+
|
93
|
+
#
|
94
|
+
# Rails.error.report(error, handled: true)
|
95
|
+
def report(error, handled:, severity: handled ? :warning : :error, context: {})
|
96
|
+
unless SEVERITIES.include?(severity)
|
97
|
+
raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
|
98
|
+
end
|
99
|
+
|
100
|
+
full_context = ActiveSupport::ExecutionContext.to_h.merge(context)
|
101
|
+
@subscribers.each do |subscriber|
|
102
|
+
subscriber.report(error, handled: handled, severity: severity, context: full_context)
|
103
|
+
rescue => subscriber_error
|
104
|
+
if logger
|
105
|
+
logger.fatal(
|
106
|
+
"Error subscriber raised an error: #{subscriber_error.message} (#{subscriber_error.class})\n" +
|
107
|
+
subscriber_error.backtrace.join("\n")
|
108
|
+
)
|
109
|
+
else
|
110
|
+
raise
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -34,7 +34,7 @@ module ActiveSupport
|
|
34
34
|
# checker.execute_if_updated
|
35
35
|
# # => "changed"
|
36
36
|
#
|
37
|
-
class EventedFileUpdateChecker
|
37
|
+
class EventedFileUpdateChecker # :nodoc: all
|
38
38
|
def initialize(files, dirs = {}, &block)
|
39
39
|
unless block
|
40
40
|
raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
module ExecutionContext # :nodoc:
|
5
|
+
@after_change_callbacks = []
|
6
|
+
class << self
|
7
|
+
def after_change(&block)
|
8
|
+
@after_change_callbacks << block
|
9
|
+
end
|
10
|
+
|
11
|
+
# Updates the execution context. If a block is given, it resets the provided keys to their
|
12
|
+
# previous value once the block exits.
|
13
|
+
def set(**options)
|
14
|
+
options.symbolize_keys!
|
15
|
+
keys = options.keys
|
16
|
+
|
17
|
+
store = self.store
|
18
|
+
|
19
|
+
previous_context = keys.zip(store.values_at(*keys)).to_h
|
20
|
+
|
21
|
+
store.merge!(options)
|
22
|
+
@after_change_callbacks.each(&:call)
|
23
|
+
|
24
|
+
if block_given?
|
25
|
+
begin
|
26
|
+
yield
|
27
|
+
ensure
|
28
|
+
store.merge!(previous_context)
|
29
|
+
@after_change_callbacks.each(&:call)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def []=(key, value)
|
35
|
+
store[key.to_sym] = value
|
36
|
+
@after_change_callbacks.each(&:call)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_h
|
40
|
+
store.dup
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear
|
44
|
+
store.clear
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def store
|
49
|
+
IsolatedExecutionState[:active_support_execution_context] ||= {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/error_reporter"
|
3
4
|
require "active_support/callbacks"
|
4
5
|
require "concurrent/hash"
|
5
6
|
|
@@ -86,15 +87,32 @@ module ActiveSupport
|
|
86
87
|
instance = run!
|
87
88
|
begin
|
88
89
|
yield
|
90
|
+
rescue => error
|
91
|
+
error_reporter.report(error, handled: false)
|
92
|
+
raise
|
89
93
|
ensure
|
90
94
|
instance.complete!
|
91
95
|
end
|
92
96
|
end
|
93
97
|
|
98
|
+
def self.perform # :nodoc:
|
99
|
+
instance = new
|
100
|
+
instance.run
|
101
|
+
begin
|
102
|
+
yield
|
103
|
+
ensure
|
104
|
+
instance.complete
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
94
108
|
class << self # :nodoc:
|
95
109
|
attr_accessor :active
|
96
110
|
end
|
97
111
|
|
112
|
+
def self.error_reporter
|
113
|
+
@error_reporter ||= ActiveSupport::ErrorReporter.new
|
114
|
+
end
|
115
|
+
|
98
116
|
def self.inherited(other) # :nodoc:
|
99
117
|
super
|
100
118
|
other.active = Concurrent::Hash.new
|
@@ -103,11 +121,15 @@ module ActiveSupport
|
|
103
121
|
self.active = Concurrent::Hash.new
|
104
122
|
|
105
123
|
def self.active? # :nodoc:
|
106
|
-
@active[
|
124
|
+
@active[IsolatedExecutionState.unique_id]
|
107
125
|
end
|
108
126
|
|
109
127
|
def run! # :nodoc:
|
110
|
-
self.class.active[
|
128
|
+
self.class.active[IsolatedExecutionState.unique_id] = true
|
129
|
+
run
|
130
|
+
end
|
131
|
+
|
132
|
+
def run # :nodoc:
|
111
133
|
run_callbacks(:run)
|
112
134
|
end
|
113
135
|
|
@@ -116,9 +138,13 @@ module ActiveSupport
|
|
116
138
|
#
|
117
139
|
# Where possible, prefer +wrap+.
|
118
140
|
def complete!
|
119
|
-
|
141
|
+
complete
|
120
142
|
ensure
|
121
|
-
self.class.active.delete
|
143
|
+
self.class.active.delete(IsolatedExecutionState.unique_id)
|
144
|
+
end
|
145
|
+
|
146
|
+
def complete # :nodoc:
|
147
|
+
run_callbacks(:complete)
|
122
148
|
end
|
123
149
|
|
124
150
|
private
|
@@ -2,8 +2,18 @@
|
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
module ForkTracker # :nodoc:
|
5
|
+
module ModernCoreExt
|
6
|
+
def _fork
|
7
|
+
pid = super
|
8
|
+
if pid == 0
|
9
|
+
ForkTracker.check!
|
10
|
+
end
|
11
|
+
pid
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
5
15
|
module CoreExt
|
6
|
-
def fork(
|
16
|
+
def fork(...)
|
7
17
|
if block_given?
|
8
18
|
super do
|
9
19
|
ForkTracker.check!
|
@@ -16,17 +26,11 @@ module ActiveSupport
|
|
16
26
|
pid
|
17
27
|
end
|
18
28
|
end
|
19
|
-
ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
|
20
29
|
end
|
21
30
|
|
22
31
|
module CoreExtPrivate
|
23
32
|
include CoreExt
|
24
|
-
|
25
|
-
private
|
26
|
-
def fork(*)
|
27
|
-
super
|
28
|
-
end
|
29
|
-
ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
|
33
|
+
private :fork
|
30
34
|
end
|
31
35
|
|
32
36
|
@pid = Process.pid
|
@@ -34,15 +38,18 @@ module ActiveSupport
|
|
34
38
|
|
35
39
|
class << self
|
36
40
|
def check!
|
37
|
-
|
41
|
+
new_pid = Process.pid
|
42
|
+
if @pid != new_pid
|
38
43
|
@callbacks.each(&:call)
|
39
|
-
@pid =
|
44
|
+
@pid = new_pid
|
40
45
|
end
|
41
46
|
end
|
42
47
|
|
43
48
|
def hook!
|
44
|
-
if Process.respond_to?(:
|
45
|
-
::
|
49
|
+
if Process.respond_to?(:_fork) # Ruby 3.1+
|
50
|
+
::Process.singleton_class.prepend(ModernCoreExt)
|
51
|
+
elsif Process.respond_to?(:fork)
|
52
|
+
::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
|
46
53
|
::Kernel.prepend(CoreExtPrivate)
|
47
54
|
::Kernel.singleton_class.prepend(CoreExt)
|
48
55
|
::Process.singleton_class.prepend(CoreExt)
|