activesupport 7.1.5.2 → 8.0.0
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 +64 -1234
- data/lib/active_support/array_inquirer.rb +1 -1
- data/lib/active_support/backtrace_cleaner.rb +10 -3
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +4 -4
- data/lib/active_support/cache/file_store.rb +27 -12
- data/lib/active_support/cache/mem_cache_store.rb +16 -74
- data/lib/active_support/cache/memory_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +21 -15
- data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
- data/lib/active_support/cache.rb +74 -77
- data/lib/active_support/callbacks.rb +75 -115
- data/lib/active_support/class_attribute.rb +26 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/core_ext/array/conversions.rb +0 -2
- data/lib/active_support/core_ext/benchmark.rb +6 -9
- data/lib/active_support/core_ext/class/attribute.rb +10 -19
- 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 +2 -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/enumerable.rb +8 -3
- data/lib/active_support/core_ext/erb/util.rb +5 -0
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +16 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -163
- data/lib/active_support/core_ext/module/deprecation.rb +1 -4
- data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
- data/lib/active_support/core_ext/object/json.rb +16 -10
- 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/thread/backtrace/location.rb +2 -7
- data/lib/active_support/core_ext/time/calculations.rb +32 -30
- data/lib/active_support/core_ext/time/compatibility.rb +24 -0
- data/lib/active_support/core_ext/time/conversions.rb +2 -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 +200 -0
- data/lib/active_support/dependencies/autoload.rb +0 -12
- data/lib/active_support/dependencies.rb +0 -1
- 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 +1 -15
- 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 +25 -16
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/encrypted_file.rb +1 -1
- data/lib/active_support/error_reporter.rb +65 -3
- data/lib/active_support/evented_file_update_checker.rb +0 -2
- 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 +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +21 -23
- data/lib/active_support/html_safe_translation.rb +3 -0
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/isolated_execution_state.rb +0 -2
- data/lib/active_support/json/encoding.rb +2 -2
- data/lib/active_support/log_subscriber.rb +0 -12
- data/lib/active_support/logger.rb +15 -2
- data/lib/active_support/logger_thread_safe_level.rb +0 -8
- data/lib/active_support/message_pack/extensions.rb +15 -2
- data/lib/active_support/message_verifier.rb +12 -0
- data/lib/active_support/multibyte/chars.rb +2 -2
- data/lib/active_support/notifications/fanout.rb +4 -8
- data/lib/active_support/notifications/instrumenter.rb +21 -18
- data/lib/active_support/notifications.rb +28 -27
- data/lib/active_support/number_helper/number_converter.rb +2 -2
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_options.rb +53 -15
- data/lib/active_support/railtie.rb +8 -11
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/subscriber.rb +1 -0
- data/lib/active_support/tagged_logging.rb +5 -1
- data/lib/active_support/test_case.rb +3 -1
- data/lib/active_support/testing/assertions.rb +79 -21
- 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 +19 -9
- data/lib/active_support/testing/method_call_assertions.rb +2 -16
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/strict_warnings.rb +8 -4
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +4 -3
- data/lib/active_support/time_with_zone.rb +29 -16
- data/lib/active_support/values/time_zone.rb +18 -16
- data/lib/active_support/xml_mini.rb +11 -2
- data/lib/active_support.rb +11 -3
- metadata +32 -22
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- data/lib/active_support/proxy_object.rb +0 -17
- data/lib/active_support/ruby_features.rb +0 -7
|
@@ -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
|
|
@@ -486,17 +491,21 @@ module ActiveSupport
|
|
|
486
491
|
if @parts.empty?
|
|
487
492
|
time.since(sign * value)
|
|
488
493
|
else
|
|
489
|
-
@parts.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
494
|
+
@parts.each do |type, number|
|
|
495
|
+
t = time
|
|
496
|
+
time =
|
|
497
|
+
if type == :seconds
|
|
498
|
+
t.since(sign * number)
|
|
499
|
+
elsif type == :minutes
|
|
500
|
+
t.since(sign * number * 60)
|
|
501
|
+
elsif type == :hours
|
|
502
|
+
t.since(sign * number * 3600)
|
|
503
|
+
else
|
|
504
|
+
t.advance(type => sign * number)
|
|
505
|
+
end
|
|
499
506
|
end
|
|
507
|
+
|
|
508
|
+
time
|
|
500
509
|
end
|
|
501
510
|
end
|
|
502
511
|
|
|
@@ -504,8 +513,8 @@ module ActiveSupport
|
|
|
504
513
|
value.respond_to?(method)
|
|
505
514
|
end
|
|
506
515
|
|
|
507
|
-
def method_missing(
|
|
508
|
-
value.public_send(
|
|
516
|
+
def method_missing(...)
|
|
517
|
+
value.public_send(...)
|
|
509
518
|
end
|
|
510
519
|
|
|
511
520
|
def raise_type_error(other)
|
|
@@ -43,6 +43,12 @@ module ActiveSupport
|
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
class InvalidKeyError < RuntimeError
|
|
47
|
+
def initialize(content_path, key)
|
|
48
|
+
super "Key '#{key}' is invalid, it must respond to '#to_sym' from configuration in '#{content_path}'."
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
46
52
|
delegate_missing_to :options
|
|
47
53
|
|
|
48
54
|
def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
|
|
@@ -61,7 +67,11 @@ module ActiveSupport
|
|
|
61
67
|
end
|
|
62
68
|
|
|
63
69
|
def validate! # :nodoc:
|
|
64
|
-
deserialize(read)
|
|
70
|
+
deserialize(read).each_key do |key|
|
|
71
|
+
key.to_sym
|
|
72
|
+
rescue NoMethodError
|
|
73
|
+
raise InvalidKeyError.new(content_path, key)
|
|
74
|
+
end
|
|
65
75
|
end
|
|
66
76
|
|
|
67
77
|
# Returns the decrypted content as a Hash with symbolized keys.
|
|
@@ -73,7 +83,7 @@ module ActiveSupport
|
|
|
73
83
|
# # => { some_secret: 123, some_namespace: { another_secret: 789 } }
|
|
74
84
|
#
|
|
75
85
|
def config
|
|
76
|
-
@config ||= deserialize(read)
|
|
86
|
+
@config ||= deep_symbolize_keys(deserialize(read))
|
|
77
87
|
end
|
|
78
88
|
|
|
79
89
|
def inspect # :nodoc:
|
|
@@ -81,6 +91,14 @@ module ActiveSupport
|
|
|
81
91
|
end
|
|
82
92
|
|
|
83
93
|
private
|
|
94
|
+
def deep_symbolize_keys(hash)
|
|
95
|
+
hash.deep_transform_keys do |key|
|
|
96
|
+
key.to_sym
|
|
97
|
+
rescue NoMethodError
|
|
98
|
+
raise InvalidKeyError.new(content_path, key)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
84
102
|
def deep_transform(hash)
|
|
85
103
|
return hash unless hash.is_a?(Hash)
|
|
86
104
|
|
|
@@ -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
|
+
|
|
148
|
+
if @debug_mode
|
|
149
|
+
ensure_backtrace(error)
|
|
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)
|
|
@@ -171,6 +209,7 @@ module ActiveSupport
|
|
|
171
209
|
#
|
|
172
210
|
def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
|
|
173
211
|
return if error.instance_variable_defined?(:@__rails_error_reported)
|
|
212
|
+
ensure_backtrace(error)
|
|
174
213
|
|
|
175
214
|
unless SEVERITIES.include?(severity)
|
|
176
215
|
raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
|
|
@@ -199,5 +238,28 @@ module ActiveSupport
|
|
|
199
238
|
|
|
200
239
|
nil
|
|
201
240
|
end
|
|
241
|
+
|
|
242
|
+
private
|
|
243
|
+
def ensure_backtrace(error)
|
|
244
|
+
return if error.frozen? # re-raising won't add a backtrace
|
|
245
|
+
return unless error.backtrace.nil?
|
|
246
|
+
|
|
247
|
+
begin
|
|
248
|
+
# We could use Exception#set_backtrace, but until Ruby 3.4
|
|
249
|
+
# it only support setting `Exception#backtrace` and not
|
|
250
|
+
# `Exception#backtrace_locations`. So raising the exception
|
|
251
|
+
# is a good way to build a real backtrace.
|
|
252
|
+
raise error
|
|
253
|
+
rescue error.class => error
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
count = 0
|
|
257
|
+
while error.backtrace_locations.first&.path == __FILE__
|
|
258
|
+
count += 1
|
|
259
|
+
error.backtrace_locations.shift
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
error.backtrace.shift(count)
|
|
263
|
+
end
|
|
202
264
|
end
|
|
203
265
|
end
|
|
@@ -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)
|
|
@@ -313,10 +313,6 @@ module ActiveSupport
|
|
|
313
313
|
end
|
|
314
314
|
alias_method :without, :except
|
|
315
315
|
|
|
316
|
-
def stringify_keys!; self end
|
|
317
|
-
def deep_stringify_keys!; self end
|
|
318
|
-
def stringify_keys; dup end
|
|
319
|
-
def deep_stringify_keys; dup end
|
|
320
316
|
undef :symbolize_keys!
|
|
321
317
|
undef :deep_symbolize_keys!
|
|
322
318
|
def symbolize_keys; to_hash.symbolize_keys! end
|
|
@@ -378,33 +374,24 @@ module ActiveSupport
|
|
|
378
374
|
|
|
379
375
|
# Convert to a regular hash with string keys.
|
|
380
376
|
def to_hash
|
|
381
|
-
|
|
382
|
-
|
|
377
|
+
copy = Hash[self]
|
|
378
|
+
copy.transform_values! { |v| convert_value_to_hash(v) }
|
|
379
|
+
set_defaults(copy)
|
|
380
|
+
copy
|
|
381
|
+
end
|
|
383
382
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
end
|
|
387
|
-
_new_hash
|
|
383
|
+
def to_proc
|
|
384
|
+
proc { |key| self[key] }
|
|
388
385
|
end
|
|
389
386
|
|
|
390
387
|
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
|
|
388
|
+
def convert_key(key)
|
|
389
|
+
Symbol === key ? key.name : key
|
|
399
390
|
end
|
|
400
391
|
|
|
401
392
|
def convert_value(value, conversion: nil)
|
|
402
393
|
if value.is_a? Hash
|
|
403
|
-
|
|
404
|
-
value.to_hash
|
|
405
|
-
else
|
|
406
|
-
value.nested_under_indifferent_access
|
|
407
|
-
end
|
|
394
|
+
value.nested_under_indifferent_access
|
|
408
395
|
elsif value.is_a?(Array)
|
|
409
396
|
if conversion != :assignment || value.frozen?
|
|
410
397
|
value = value.dup
|
|
@@ -415,6 +402,17 @@ module ActiveSupport
|
|
|
415
402
|
end
|
|
416
403
|
end
|
|
417
404
|
|
|
405
|
+
def convert_value_to_hash(value)
|
|
406
|
+
if value.is_a? Hash
|
|
407
|
+
value.to_hash
|
|
408
|
+
elsif value.is_a?(Array)
|
|
409
|
+
value.map { |e| convert_value_to_hash(e) }
|
|
410
|
+
else
|
|
411
|
+
value
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
|
|
418
416
|
def set_defaults(target)
|
|
419
417
|
if default_proc
|
|
420
418
|
target.default_proc = default_proc.dup
|
|
@@ -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
|
|
@@ -14,15 +14,18 @@ module I18n
|
|
|
14
14
|
|
|
15
15
|
config.eager_load_namespaces << I18n
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
config.
|
|
17
|
+
# Make sure i18n is ready before eager loading, in case any eager loaded
|
|
18
|
+
# code needs it.
|
|
19
|
+
config.before_eager_load do |app|
|
|
20
20
|
I18n::Railtie.initialize_i18n(app)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
|
|
23
|
+
# i18n initialization needs to run after application initialization, since
|
|
24
|
+
# initializers may configure i18n.
|
|
25
|
+
#
|
|
26
|
+
# If the application eager loaded, this was done on before_eager_load. The
|
|
27
|
+
# hook is still OK, though, because initialize_i18n is idempotent.
|
|
28
|
+
config.after_initialize do |app|
|
|
26
29
|
I18n::Railtie.initialize_i18n(app)
|
|
27
30
|
end
|
|
28
31
|
|
|
@@ -49,7 +52,8 @@ module I18n
|
|
|
49
52
|
when :load_path
|
|
50
53
|
I18n.load_path += value
|
|
51
54
|
when :raise_on_missing_translations
|
|
52
|
-
|
|
55
|
+
strict = value == :strict
|
|
56
|
+
setup_raise_on_missing_translations_config(app, strict)
|
|
53
57
|
else
|
|
54
58
|
I18n.public_send("#{setting}=", value)
|
|
55
59
|
end
|
|
@@ -62,8 +66,9 @@ module I18n
|
|
|
62
66
|
|
|
63
67
|
if app.config.reloading_enabled?
|
|
64
68
|
directories = watched_dirs_with_extensions(reloadable_paths)
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
root_load_paths = I18n.load_path.select { |path| path.to_s.start_with?(Rails.root.to_s) }
|
|
70
|
+
reloader = app.config.file_watcher.new(root_load_paths, directories) do
|
|
71
|
+
I18n.load_path.delete_if { |path| path.to_s.start_with?(Rails.root.to_s) && !File.exist?(path) }
|
|
67
72
|
I18n.load_path |= reloadable_paths.flat_map(&:existent)
|
|
68
73
|
end
|
|
69
74
|
|
|
@@ -71,17 +76,20 @@ module I18n
|
|
|
71
76
|
app.reloader.to_run do
|
|
72
77
|
reloader.execute_if_updated { require_unload_lock! }
|
|
73
78
|
end
|
|
74
|
-
reloader.execute
|
|
75
79
|
end
|
|
76
80
|
|
|
77
81
|
@i18n_inited = true
|
|
78
82
|
end
|
|
79
83
|
|
|
80
|
-
def self.setup_raise_on_missing_translations_config(app)
|
|
84
|
+
def self.setup_raise_on_missing_translations_config(app, strict)
|
|
81
85
|
ActiveSupport.on_load(:action_view) do
|
|
82
86
|
ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
|
|
83
87
|
end
|
|
84
88
|
|
|
89
|
+
ActiveSupport.on_load(:active_model_translation) do
|
|
90
|
+
ActiveModel::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations if strict
|
|
91
|
+
end
|
|
92
|
+
|
|
85
93
|
if app.config.i18n.raise_on_missing_translations &&
|
|
86
94
|
I18n.exception_handler.is_a?(I18n::ExceptionHandler) # Only override the i18n gem's default exception handler.
|
|
87
95
|
|
|
@@ -36,14 +36,14 @@ module ActiveSupport
|
|
|
36
36
|
# Encode the given object into a JSON string
|
|
37
37
|
def encode(value)
|
|
38
38
|
unless options.empty?
|
|
39
|
-
value = value.as_json(options.dup)
|
|
39
|
+
value = value.as_json(options.dup.freeze)
|
|
40
40
|
end
|
|
41
41
|
json = stringify(jsonify(value))
|
|
42
42
|
|
|
43
43
|
# Rails does more escaping than the JSON gem natively does (we
|
|
44
44
|
# escape \u2028 and \u2029 and optionally >, <, & to work around
|
|
45
45
|
# certain browser problems).
|
|
46
|
-
if Encoding.escape_html_entities_in_json
|
|
46
|
+
if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
|
|
47
47
|
json.gsub!(">", '\u003e')
|
|
48
48
|
json.gsub!("<", '\u003c')
|
|
49
49
|
json.gsub!("&", '\u0026')
|
|
@@ -62,10 +62,6 @@ module ActiveSupport
|
|
|
62
62
|
# that all logs are flushed, and it is called in Rails::Rack::Logger after a
|
|
63
63
|
# request finishes.
|
|
64
64
|
class LogSubscriber < Subscriber
|
|
65
|
-
# Embed in a String to clear all previous ANSI sequences.
|
|
66
|
-
CLEAR = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[0m", "CLEAR is deprecated! Use MODES[:clear] instead.", ActiveSupport.deprecator)
|
|
67
|
-
BOLD = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[1m", "BOLD is deprecated! Use MODES[:bold] instead.", ActiveSupport.deprecator)
|
|
68
|
-
|
|
69
65
|
# ANSI sequence modes
|
|
70
66
|
MODES = {
|
|
71
67
|
clear: 0,
|
|
@@ -182,14 +178,6 @@ module ActiveSupport
|
|
|
182
178
|
end
|
|
183
179
|
|
|
184
180
|
def mode_from(options)
|
|
185
|
-
if options.is_a?(TrueClass) || options.is_a?(FalseClass)
|
|
186
|
-
ActiveSupport.deprecator.warn(<<~MSG.squish)
|
|
187
|
-
Bolding log text with a positional boolean is deprecated and will be removed
|
|
188
|
-
in Rails 7.2. Use an option hash instead (eg. `color("my text", :red, bold: true)`).
|
|
189
|
-
MSG
|
|
190
|
-
options = { bold: options }
|
|
191
|
-
end
|
|
192
|
-
|
|
193
181
|
modes = MODES.values_at(*options.compact_blank.keys)
|
|
194
182
|
|
|
195
183
|
"\e[#{modes.join(";")}m" if modes.any?
|
|
@@ -13,6 +13,10 @@ module ActiveSupport
|
|
|
13
13
|
# logger = Logger.new(STDOUT)
|
|
14
14
|
# ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
|
|
15
15
|
# # => true
|
|
16
|
+
#
|
|
17
|
+
# logger = Logger.new('/var/log/rails.log')
|
|
18
|
+
# ActiveSupport::Logger.logger_outputs_to?(logger, '/var/log/rails.log')
|
|
19
|
+
# # => true
|
|
16
20
|
def self.logger_outputs_to?(logger, *sources)
|
|
17
21
|
loggers = if logger.is_a?(BroadcastLogger)
|
|
18
22
|
logger.broadcasts
|
|
@@ -21,9 +25,9 @@ module ActiveSupport
|
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
logdevs = loggers.map { |logger| logger.instance_variable_get(:@logdev) }
|
|
24
|
-
logger_sources = logdevs.filter_map { |logdev| logdev.
|
|
28
|
+
logger_sources = logdevs.filter_map { |logdev| logdev.try(:filename) || logdev.try(:dev) }
|
|
25
29
|
|
|
26
|
-
(sources
|
|
30
|
+
normalize_sources(sources).intersect?(normalize_sources(logger_sources))
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def initialize(*args, **kwargs)
|
|
@@ -38,5 +42,14 @@ module ActiveSupport
|
|
|
38
42
|
"#{String === msg ? msg : msg.inspect}\n"
|
|
39
43
|
end
|
|
40
44
|
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
def self.normalize_sources(sources)
|
|
48
|
+
sources.map do |source|
|
|
49
|
+
source = source.path if source.respond_to?(:path)
|
|
50
|
+
source = File.realpath(source) if source.is_a?(String) && File.exist?(source)
|
|
51
|
+
source
|
|
52
|
+
end
|
|
53
|
+
end
|
|
41
54
|
end
|
|
42
55
|
end
|
|
@@ -7,14 +7,6 @@ module ActiveSupport
|
|
|
7
7
|
module LoggerThreadSafeLevel # :nodoc:
|
|
8
8
|
extend ActiveSupport::Concern
|
|
9
9
|
|
|
10
|
-
Logger::Severity.constants.each do |severity|
|
|
11
|
-
class_eval(<<-EOT, __FILE__, __LINE__ + 1)
|
|
12
|
-
def #{severity.downcase}? # def debug?
|
|
13
|
-
Logger::#{severity} >= level # DEBUG >= level
|
|
14
|
-
end # end
|
|
15
|
-
EOT
|
|
16
|
-
end
|
|
17
|
-
|
|
18
10
|
def local_level
|
|
19
11
|
IsolatedExecutionState[local_level_key]
|
|
20
12
|
end
|
|
@@ -86,8 +86,9 @@ module ActiveSupport
|
|
|
86
86
|
unpacker: URI.method(:parse)
|
|
87
87
|
|
|
88
88
|
registry.register_type 14, IPAddr,
|
|
89
|
-
packer: :
|
|
90
|
-
unpacker: :
|
|
89
|
+
packer: method(:write_ipaddr),
|
|
90
|
+
unpacker: method(:read_ipaddr),
|
|
91
|
+
recursive: true
|
|
91
92
|
|
|
92
93
|
registry.register_type 15, Pathname,
|
|
93
94
|
packer: :to_s,
|
|
@@ -221,6 +222,18 @@ module ActiveSupport
|
|
|
221
222
|
Set.new(unpacker.read)
|
|
222
223
|
end
|
|
223
224
|
|
|
225
|
+
def write_ipaddr(ipaddr, packer)
|
|
226
|
+
if ipaddr.prefix < 32 || (ipaddr.ipv6? && ipaddr.prefix < 128)
|
|
227
|
+
packer.write("#{ipaddr}/#{ipaddr.prefix}")
|
|
228
|
+
else
|
|
229
|
+
packer.write(ipaddr.to_s)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def read_ipaddr(unpacker)
|
|
234
|
+
IPAddr.new(unpacker.read)
|
|
235
|
+
end
|
|
236
|
+
|
|
224
237
|
def write_hash_with_indifferent_access(hwia, packer)
|
|
225
238
|
packer.write(hwia.to_h)
|
|
226
239
|
end
|
|
@@ -30,6 +30,18 @@ module ActiveSupport
|
|
|
30
30
|
# self.current_user = User.find(id)
|
|
31
31
|
# end
|
|
32
32
|
#
|
|
33
|
+
# === Signing is not encryption
|
|
34
|
+
#
|
|
35
|
+
# The signed messages are not encrypted. The payload is merely encoded (Base64 by default) and can be decoded by
|
|
36
|
+
# anyone. The signature is just assuring that the message wasn't tampered with. For example:
|
|
37
|
+
#
|
|
38
|
+
# message = Rails.application.message_verifier('my_purpose').generate('never put secrets here')
|
|
39
|
+
# # => "BAhJIhtuZXZlciBwdXQgc2VjcmV0cyBoZXJlBjoGRVQ=--a0c1c0827919da5e949e989c971249355735e140"
|
|
40
|
+
# Base64.decode64(message.split("--").first) # no key needed
|
|
41
|
+
# # => 'never put secrets here'
|
|
42
|
+
#
|
|
43
|
+
# If you also need to encrypt the contents, you must use ActiveSupport::MessageEncryptor instead.
|
|
44
|
+
#
|
|
33
45
|
# === Confine messages to a specific purpose
|
|
34
46
|
#
|
|
35
47
|
# It's not recommended to use the same verifier for different purposes in your application.
|