activesupport 7.2.2.1 → 8.1.3
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 +422 -145
- data/README.rdoc +1 -1
- data/lib/active_support/backtrace_cleaner.rb +73 -2
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +61 -74
- data/lib/active_support/cache/file_store.rb +14 -4
- data/lib/active_support/cache/mem_cache_store.rb +30 -29
- data/lib/active_support/cache/memory_store.rb +11 -5
- data/lib/active_support/cache/null_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +43 -34
- data/lib/active_support/cache/strategy/local_cache.rb +72 -27
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +88 -20
- data/lib/active_support/callbacks.rb +28 -13
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +34 -0
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/conversions.rb +3 -3
- data/lib/active_support/core_ext/array.rb +7 -7
- data/lib/active_support/core_ext/benchmark.rb +0 -15
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +26 -20
- data/lib/active_support/core_ext/class.rb +2 -2
- data/lib/active_support/core_ext/date/conversions.rb +2 -0
- data/lib/active_support/core_ext/date.rb +5 -5
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
- data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
- data/lib/active_support/core_ext/date_time.rb +5 -5
- data/lib/active_support/core_ext/digest.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +25 -8
- data/lib/active_support/core_ext/erb/util.rb +5 -5
- data/lib/active_support/core_ext/file.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/hash.rb +8 -8
- data/lib/active_support/core_ext/integer.rb +3 -3
- data/lib/active_support/core_ext/kernel.rb +3 -3
- data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
- data/lib/active_support/core_ext/module/introspection.rb +3 -0
- data/lib/active_support/core_ext/module.rb +11 -11
- data/lib/active_support/core_ext/numeric.rb +3 -3
- data/lib/active_support/core_ext/object/json.rb +24 -11
- data/lib/active_support/core_ext/object/to_query.rb +7 -1
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/object.rb +13 -13
- data/lib/active_support/core_ext/pathname.rb +2 -2
- data/lib/active_support/core_ext/range/overlap.rb +3 -3
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +4 -4
- data/lib/active_support/core_ext/securerandom.rb +24 -8
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/multibyte.rb +12 -3
- data/lib/active_support/core_ext/string/output_safety.rb +29 -13
- data/lib/active_support/core_ext/string.rb +13 -13
- data/lib/active_support/core_ext/symbol.rb +1 -1
- data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
- data/lib/active_support/core_ext/time/calculations.rb +7 -2
- data/lib/active_support/core_ext/time/compatibility.rb +2 -19
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/time.rb +5 -5
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +27 -17
- data/lib/active_support/delegation.rb +25 -44
- data/lib/active_support/dependencies/interlock.rb +11 -5
- data/lib/active_support/dependencies.rb +6 -2
- data/lib/active_support/deprecation/reporting.rb +4 -21
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/duration.rb +14 -10
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/error_reporter.rb +81 -4
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +5 -2
- data/lib/active_support/execution_context.rb +75 -7
- data/lib/active_support/execution_wrapper.rb +1 -1
- data/lib/active_support/file_update_checker.rb +8 -6
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +61 -38
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/inflector/inflections.rb +34 -16
- data/lib/active_support/inflector/methods.rb +3 -3
- data/lib/active_support/inflector/transliterate.rb +6 -8
- data/lib/active_support/isolated_execution_state.rb +17 -17
- data/lib/active_support/json/decoding.rb +6 -4
- data/lib/active_support/json/encoding.rb +159 -21
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber.rb +2 -6
- data/lib/active_support/logger_thread_safe_level.rb +6 -3
- data/lib/active_support/message_encryptors.rb +54 -2
- data/lib/active_support/message_pack/extensions.rb +6 -1
- data/lib/active_support/message_verifier.rb +9 -0
- data/lib/active_support/message_verifiers.rb +57 -3
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +10 -0
- data/lib/active_support/multibyte/chars.rb +12 -2
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/notifications/fanout.rb +64 -43
- data/lib/active_support/notifications/instrumenter.rb +1 -1
- data/lib/active_support/number_helper/number_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +17 -2
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/railtie.rb +32 -9
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +0 -5
- data/lib/active_support/syntax_error_proxy.rb +7 -0
- data/lib/active_support/tagged_logging.rb +5 -0
- data/lib/active_support/test_case.rb +67 -6
- data/lib/active_support/testing/assertions.rb +118 -27
- data/lib/active_support/testing/autorun.rb +5 -0
- data/lib/active_support/testing/error_reporter_assertions.rb +17 -0
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/isolation.rb +0 -2
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +15 -2
- data/lib/active_support/testing/parallelization/worker.rb +9 -3
- data/lib/active_support/testing/parallelization.rb +25 -1
- data/lib/active_support/testing/tests_without_assertions.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +9 -4
- data/lib/active_support/time_with_zone.rb +36 -23
- data/lib/active_support/values/time_zone.rb +19 -10
- data/lib/active_support/xml_mini.rb +3 -2
- data/lib/active_support.rb +21 -9
- metadata +35 -16
- data/lib/active_support/core_ext/range/each.rb +0 -24
- data/lib/active_support/proxy_object.rb +0 -20
- data/lib/active_support/testing/strict_warnings.rb +0 -43
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
class Editor
|
|
7
|
+
@editors = {}
|
|
8
|
+
@current = false
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
# Registers a URL pattern for opening file in a given editor.
|
|
12
|
+
# This allows Rails to generate clickable links to control known editors.
|
|
13
|
+
#
|
|
14
|
+
# Example:
|
|
15
|
+
#
|
|
16
|
+
# ActiveSupport::Editor.register("myeditor", "myeditor://%s:%d")
|
|
17
|
+
def register(name, url_pattern, aliases: [])
|
|
18
|
+
editor = new(url_pattern)
|
|
19
|
+
@editors[name] = editor
|
|
20
|
+
aliases.each do |a|
|
|
21
|
+
@editors[a] = editor
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns the current editor pattern if it is known.
|
|
26
|
+
# First check for the `RAILS_EDITOR` environment variable, and if it's
|
|
27
|
+
# missing, check for the `EDITOR` environment variable.
|
|
28
|
+
def current
|
|
29
|
+
if @current == false
|
|
30
|
+
@current = if editor_name = ENV["RAILS_EDITOR"] || ENV["EDITOR"]
|
|
31
|
+
@editors[editor_name]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
@current
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# :nodoc:
|
|
38
|
+
|
|
39
|
+
def find(name)
|
|
40
|
+
@editors[name]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def reset
|
|
44
|
+
@current = false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def initialize(url_pattern)
|
|
49
|
+
@url_pattern = url_pattern
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def url_for(path, line)
|
|
53
|
+
sprintf(@url_pattern, path, line)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
register "atom", "atom://core/open/file?filename=%s&line=%d"
|
|
57
|
+
register "cursor", "cursor://file/%s:%f"
|
|
58
|
+
register "emacs", "emacs://open?url=file://%s&line=%d", aliases: ["emacsclient"]
|
|
59
|
+
register "idea", "idea://open?file=%s&line=%d"
|
|
60
|
+
register "macvim", "mvim://open?url=file://%s&line=%d", aliases: ["mvim"]
|
|
61
|
+
register "nova", "nova://open?path=%s&line=%d"
|
|
62
|
+
register "rubymine", "x-mine://open?file=%s&line=%d"
|
|
63
|
+
register "sublime", "subl://open?url=file://%s&line=%d", aliases: ["subl"]
|
|
64
|
+
register "textmate", "txmt://open?url=file://%s&line=%d", aliases: ["mate"]
|
|
65
|
+
register "vscode", "vscode://file/%s:%d", aliases: ["code"]
|
|
66
|
+
register "vscodium", "vscodium://file/%s:%d", aliases: ["codium"]
|
|
67
|
+
register "windsurf", "windsurf://file/%s:%d"
|
|
68
|
+
register "zed", "zed://file/%s:%d"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -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
|
|
|
@@ -36,6 +36,7 @@ module ActiveSupport
|
|
|
36
36
|
@subscribers = subscribers.flatten
|
|
37
37
|
@logger = logger
|
|
38
38
|
@debug_mode = false
|
|
39
|
+
@context_middlewares = ErrorContextMiddlewareStack.new
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
# Evaluates the given block, reporting and swallowing any unhandled error.
|
|
@@ -144,9 +145,9 @@ module ActiveSupport
|
|
|
144
145
|
#
|
|
145
146
|
def unexpected(error, severity: :warning, context: {}, source: DEFAULT_SOURCE)
|
|
146
147
|
error = RuntimeError.new(error) if error.is_a?(String)
|
|
147
|
-
error.set_backtrace(caller(1)) if error.backtrace.nil?
|
|
148
148
|
|
|
149
149
|
if @debug_mode
|
|
150
|
+
ensure_backtrace(error)
|
|
150
151
|
raise UnexpectedError, "#{error.class.name}: #{error.message}", error.backtrace, cause: error
|
|
151
152
|
else
|
|
152
153
|
report(error, handled: true, severity: severity, context: context, source: source)
|
|
@@ -202,19 +203,51 @@ module ActiveSupport
|
|
|
202
203
|
ActiveSupport::ExecutionContext.set(...)
|
|
203
204
|
end
|
|
204
205
|
|
|
206
|
+
# Add a middleware to modify the error context before it is sent to subscribers.
|
|
207
|
+
#
|
|
208
|
+
# Middleware is added to a stack of callables run on an error's execution context
|
|
209
|
+
# before passing to subscribers. Allows creation of entries in error context that
|
|
210
|
+
# are shared by all subscribers.
|
|
211
|
+
#
|
|
212
|
+
# A context middleware receives the same parameters as #report.
|
|
213
|
+
# It must return a hash - the middleware stack returns the hash after it has
|
|
214
|
+
# run through all middlewares. A middleware can mutate or replace the hash.
|
|
215
|
+
#
|
|
216
|
+
# Rails.error.add_middleware(-> (error, context) { context.merge({ foo: :bar }) })
|
|
217
|
+
#
|
|
218
|
+
def add_middleware(middleware)
|
|
219
|
+
@context_middlewares.use(middleware)
|
|
220
|
+
end
|
|
221
|
+
|
|
205
222
|
# Report an error directly to subscribers. You can use this method when the
|
|
206
223
|
# block-based #handle and #record methods are not suitable.
|
|
207
224
|
#
|
|
208
225
|
# Rails.error.report(error)
|
|
209
226
|
#
|
|
227
|
+
# The +error+ argument must be an instance of Exception.
|
|
228
|
+
#
|
|
229
|
+
# Rails.error.report(Exception.new("Something went wrong"))
|
|
230
|
+
#
|
|
231
|
+
# Otherwise you can use #unexpected to report an error which does accept a
|
|
232
|
+
# string argument.
|
|
210
233
|
def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
|
|
211
234
|
return if error.instance_variable_defined?(:@__rails_error_reported)
|
|
235
|
+
raise ArgumentError, "Reported error must be an Exception, got: #{error.inspect}" unless error.is_a?(Exception)
|
|
236
|
+
|
|
237
|
+
ensure_backtrace(error)
|
|
212
238
|
|
|
213
239
|
unless SEVERITIES.include?(severity)
|
|
214
240
|
raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
|
|
215
241
|
end
|
|
216
242
|
|
|
217
|
-
full_context =
|
|
243
|
+
full_context = @context_middlewares.execute(
|
|
244
|
+
error,
|
|
245
|
+
context: ActiveSupport::ExecutionContext.to_h.merge(context || {}),
|
|
246
|
+
handled:,
|
|
247
|
+
severity:,
|
|
248
|
+
source:
|
|
249
|
+
)
|
|
250
|
+
|
|
218
251
|
disabled_subscribers = ActiveSupport::IsolatedExecutionState[self]
|
|
219
252
|
@subscribers.each do |subscriber|
|
|
220
253
|
unless disabled_subscribers&.any? { |s| s === subscriber }
|
|
@@ -231,11 +264,55 @@ module ActiveSupport
|
|
|
231
264
|
end
|
|
232
265
|
end
|
|
233
266
|
|
|
234
|
-
|
|
235
|
-
error.
|
|
267
|
+
while error
|
|
268
|
+
unless error.frozen?
|
|
269
|
+
error.instance_variable_set(:@__rails_error_reported, true)
|
|
270
|
+
end
|
|
271
|
+
error = error.cause
|
|
236
272
|
end
|
|
237
273
|
|
|
238
274
|
nil
|
|
239
275
|
end
|
|
276
|
+
|
|
277
|
+
private
|
|
278
|
+
def ensure_backtrace(error)
|
|
279
|
+
return if error.frozen? # re-raising won't add a backtrace or set the cause
|
|
280
|
+
return unless error.backtrace.nil?
|
|
281
|
+
|
|
282
|
+
begin
|
|
283
|
+
# As of Ruby 3.4, we could use Exception#set_backtrace to set the backtrace,
|
|
284
|
+
# but there's nothing like Exception#set_cause. Raising+rescuing is the only way to set the cause.
|
|
285
|
+
raise error
|
|
286
|
+
rescue error.class => error
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
count = 0
|
|
290
|
+
while error.backtrace_locations.first&.path == __FILE__
|
|
291
|
+
count += 1
|
|
292
|
+
error.backtrace_locations.shift
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
error.backtrace.shift(count)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
class ErrorContextMiddlewareStack # :nodoc:
|
|
299
|
+
def initialize
|
|
300
|
+
@stack = []
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Add a middleware to the error context stack.
|
|
304
|
+
def use(middleware)
|
|
305
|
+
unless middleware.respond_to?(:call)
|
|
306
|
+
raise ArgumentError, "Error context middleware must respond to #call"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
@stack << middleware
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Run all middlewares in the stack
|
|
313
|
+
def execute(error, handled:, severity:, context:, source:)
|
|
314
|
+
@stack.inject(context) { |c, middleware| middleware.call(error, context: c, handled:, severity:, source:) }
|
|
315
|
+
end
|
|
316
|
+
end
|
|
240
317
|
end
|
|
241
318
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport::EventReporter::TestHelper # :nodoc:
|
|
4
|
+
class EventSubscriber # :nodoc:
|
|
5
|
+
attr_reader :events
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@events = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def emit(event)
|
|
12
|
+
@events << event
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def event_matcher(name:, payload: nil, tags: {}, context: {}, source_location: nil)
|
|
17
|
+
->(event) {
|
|
18
|
+
return false unless event[:name] == name
|
|
19
|
+
return false unless event[:payload] == payload
|
|
20
|
+
return false unless event[:tags] == tags
|
|
21
|
+
return false unless event[:context] == context
|
|
22
|
+
|
|
23
|
+
[:filepath, :lineno, :label].each do |key|
|
|
24
|
+
if source_location && source_location[key]
|
|
25
|
+
return false unless event[:source_location][key] == source_location[key]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
true
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|