activesupport 7.2.3.1 → 8.1.4.1
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 +584 -234
- data/lib/active_support/backtrace_cleaner.rb +72 -1
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/cache/file_store.rb +13 -2
- data/lib/active_support/cache/mem_cache_store.rb +17 -14
- data/lib/active_support/cache/memory_store.rb +9 -3
- data/lib/active_support/cache/redis_cache_store.rb +51 -34
- data/lib/active_support/cache/strategy/local_cache.rb +17 -8
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +86 -18
- data/lib/active_support/callbacks.rb +25 -11
- 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 -16
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +24 -18
- 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/calculations.rb +1 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
- 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 +8 -3
- data/lib/active_support/core_ext/erb/util.rb +3 -3
- 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.rb +11 -11
- data/lib/active_support/core_ext/numeric.rb +3 -3
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
- 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.rb +13 -13
- data/lib/active_support/core_ext/pathname.rb +2 -2
- data/lib/active_support/core_ext/range/compare_range.rb +2 -14
- data/lib/active_support/core_ext/range/overlap.rb +3 -3
- data/lib/active_support/core_ext/range/sole.rb +2 -0
- data/lib/active_support/core_ext/range.rb +4 -5
- data/lib/active_support/core_ext/string/filters.rb +2 -0
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/multibyte.rb +10 -1
- data/lib/active_support/core_ext/string/output_safety.rb +27 -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 +11 -2
- data/lib/active_support/core_ext/time/compatibility.rb +2 -27
- 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 +13 -10
- 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 +20 -16
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/error_reporter.rb +76 -2
- 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/file_update_checker.rb +7 -5
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +44 -24
- 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 +6 -22
- data/lib/active_support/inflector/transliterate.rb +18 -10
- data/lib/active_support/isolated_execution_state.rb +17 -17
- data/lib/active_support/json/decoding.rb +9 -3
- data/lib/active_support/json/encoding.rb +149 -19
- 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 +52 -0
- data/lib/active_support/message_pack/extensions.rb +23 -1
- data/lib/active_support/message_verifiers.rb +52 -0
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +5 -0
- data/lib/active_support/messages/serializer_with_fallback.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +8 -1
- 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/notifications.rb +2 -2
- data/lib/active_support/number_helper/number_converter.rb +4 -1
- data/lib/active_support/number_helper/number_to_currency_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -4
- data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -7
- data/lib/active_support/number_helper/rounding_helper.rb +1 -1
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/ordered_options.rb +2 -2
- data/lib/active_support/railtie.rb +32 -9
- data/lib/active_support/security_utils.rb +4 -5
- 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 +9 -1
- 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/worker.rb +7 -1
- data/lib/active_support/testing/parallelization.rb +17 -0
- 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 +22 -13
- data/lib/active_support/xml_mini.rb +1 -2
- data/lib/active_support.rb +21 -9
- metadata +33 -17
- data/lib/active_support/core_ext/range/each.rb +0 -24
- data/lib/active_support/proxy_object.rb +0 -20
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "set"
|
|
4
|
-
|
|
5
3
|
module ActiveSupport
|
|
6
4
|
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
|
|
7
5
|
# option is not used.
|
|
@@ -136,11 +134,11 @@ module ActiveSupport
|
|
|
136
134
|
"def #{method_name}(#{definition})" <<
|
|
137
135
|
" _ = #{receiver}" <<
|
|
138
136
|
" _.#{method}(#{definition})" <<
|
|
139
|
-
"rescue NoMethodError => e" <<
|
|
137
|
+
"rescue ::NoMethodError => e" <<
|
|
140
138
|
" if _.nil? && e.name == :#{method}" <<
|
|
141
|
-
" raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
|
|
139
|
+
" ::Kernel.raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
|
|
142
140
|
" else" <<
|
|
143
|
-
" raise" <<
|
|
141
|
+
" ::Kernel.raise" <<
|
|
144
142
|
" end" <<
|
|
145
143
|
"end"
|
|
146
144
|
end
|
|
@@ -153,49 +151,32 @@ module ActiveSupport
|
|
|
153
151
|
target = target.to_s
|
|
154
152
|
target = "self.#{target}" if RESERVED_METHOD_NAMES.include?(target) || target == "__target"
|
|
155
153
|
|
|
156
|
-
if allow_nil
|
|
157
|
-
|
|
158
|
-
def respond_to_missing?(name, include_private = false)
|
|
159
|
-
# It may look like an oversight, but we deliberately do not pass
|
|
160
|
-
# +include_private+, because they do not get delegated.
|
|
161
|
-
|
|
162
|
-
return false if name == :marshal_dump || name == :_dump
|
|
163
|
-
#{target}.respond_to?(name) || super
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def method_missing(method, ...)
|
|
167
|
-
__target = #{target}
|
|
168
|
-
if __target.nil? && !nil.respond_to?(method)
|
|
169
|
-
nil
|
|
170
|
-
elsif __target.respond_to?(method)
|
|
171
|
-
__target.public_send(method, ...)
|
|
172
|
-
else
|
|
173
|
-
super
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
RUBY
|
|
154
|
+
nil_behavior = if allow_nil
|
|
155
|
+
"nil"
|
|
177
156
|
else
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# It may look like an oversight, but we deliberately do not pass
|
|
181
|
-
# +include_private+, because they do not get delegated.
|
|
157
|
+
"::Kernel.raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')"
|
|
158
|
+
end
|
|
182
159
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
160
|
+
owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
161
|
+
def respond_to_missing?(name, include_private = false)
|
|
162
|
+
# It may look like an oversight, but we deliberately do not pass
|
|
163
|
+
# +include_private+, because they do not get delegated.
|
|
186
164
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
165
|
+
return false if name == :marshal_dump || name == :_dump
|
|
166
|
+
#{target}.respond_to?(name) || super
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def method_missing(method, ...)
|
|
170
|
+
__target = #{target}
|
|
171
|
+
if __target.nil? && !nil.respond_to?(method)
|
|
172
|
+
#{nil_behavior}
|
|
173
|
+
elsif __target.respond_to?(method)
|
|
174
|
+
__target.public_send(method, ...)
|
|
175
|
+
else
|
|
176
|
+
super
|
|
196
177
|
end
|
|
197
|
-
|
|
198
|
-
|
|
178
|
+
end
|
|
179
|
+
RUBY
|
|
199
180
|
end
|
|
200
181
|
end
|
|
201
182
|
end
|
|
@@ -10,19 +10,24 @@ module ActiveSupport # :nodoc:
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def loading(&block)
|
|
13
|
-
|
|
13
|
+
ActiveSupport.deprecator.warn(
|
|
14
|
+
"ActiveSupport::Dependencies::Interlock#loading is deprecated and " \
|
|
15
|
+
"will be removed in Rails 9.0. The loading interlock is no longer " \
|
|
16
|
+
"used since Rails switched to Zeitwerk for autoloading."
|
|
17
|
+
)
|
|
18
|
+
yield if block
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
def unloading(&block)
|
|
17
|
-
@lock.exclusive(purpose: :unload, compatible: [:
|
|
22
|
+
@lock.exclusive(purpose: :unload, compatible: [:unload], after_compatible: [:unload], &block)
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
def start_unloading
|
|
21
|
-
@lock.start_exclusive(purpose: :unload, compatible: [:
|
|
26
|
+
@lock.start_exclusive(purpose: :unload, compatible: [:unload])
|
|
22
27
|
end
|
|
23
28
|
|
|
24
29
|
def done_unloading
|
|
25
|
-
@lock.stop_exclusive(compatible: [:
|
|
30
|
+
@lock.stop_exclusive(compatible: [:unload])
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
def start_running
|
|
@@ -38,7 +43,8 @@ module ActiveSupport # :nodoc:
|
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
def permit_concurrent_loads(&block)
|
|
41
|
-
|
|
46
|
+
# Soft deprecated: no deprecation warning for now, but this is a no-op.
|
|
47
|
+
yield if block
|
|
42
48
|
end
|
|
43
49
|
|
|
44
50
|
def raw_state(&block) # :nodoc:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "set"
|
|
4
3
|
require "active_support/dependencies/interlock"
|
|
5
4
|
|
|
6
5
|
module ActiveSupport # :nodoc:
|
|
@@ -22,7 +21,12 @@ module ActiveSupport # :nodoc:
|
|
|
22
21
|
# preventing any other thread from being inside a #run_interlock
|
|
23
22
|
# block at the same time.
|
|
24
23
|
def self.load_interlock(&block)
|
|
25
|
-
|
|
24
|
+
ActiveSupport.deprecator.warn(
|
|
25
|
+
"ActiveSupport::Dependencies.load_interlock is deprecated and " \
|
|
26
|
+
"will be removed in Rails 9.0. The loading interlock is no longer " \
|
|
27
|
+
"used since Rails switched to Zeitwerk for autoloading."
|
|
28
|
+
)
|
|
29
|
+
yield if block
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
# Execute the supplied block while holding an exclusive lock,
|
|
@@ -139,7 +139,6 @@ module ActiveSupport
|
|
|
139
139
|
|
|
140
140
|
def extract_callstack(callstack)
|
|
141
141
|
return [] if callstack.empty?
|
|
142
|
-
return _extract_callstack(callstack) if callstack.first.is_a? String
|
|
143
142
|
|
|
144
143
|
offending_line = callstack.find { |frame|
|
|
145
144
|
# Code generated with `eval` doesn't have an `absolute_path`, e.g. templates.
|
|
@@ -150,26 +149,10 @@ module ActiveSupport
|
|
|
150
149
|
[offending_line.path, offending_line.lineno, offending_line.label]
|
|
151
150
|
end
|
|
152
151
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
Please pass the result of `caller_locations` instead.
|
|
158
|
-
MESSAGE
|
|
159
|
-
|
|
160
|
-
offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
|
|
161
|
-
|
|
162
|
-
if offending_line
|
|
163
|
-
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
|
|
164
|
-
md.captures
|
|
165
|
-
else
|
|
166
|
-
offending_line
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
|
|
172
|
-
LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
|
|
152
|
+
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
|
|
153
|
+
private_constant :RAILS_GEM_ROOT
|
|
154
|
+
LIB_DIR = RbConfig::CONFIG["libdir"]
|
|
155
|
+
private_constant :LIB_DIR
|
|
173
156
|
|
|
174
157
|
def ignored_callstack?(path)
|
|
175
158
|
path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
|
|
@@ -68,7 +68,7 @@ module ActiveSupport
|
|
|
68
68
|
# and the second is a library name.
|
|
69
69
|
#
|
|
70
70
|
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
|
|
71
|
-
def initialize(deprecation_horizon = "8.
|
|
71
|
+
def initialize(deprecation_horizon = "8.2", gem_name = "Rails")
|
|
72
72
|
self.gem_name = gem_name
|
|
73
73
|
self.deprecation_horizon = deprecation_horizon
|
|
74
74
|
# By default, warnings are not silenced and debugging is off.
|
|
@@ -383,42 +383,42 @@ module ActiveSupport
|
|
|
383
383
|
#
|
|
384
384
|
# 1.day.in_minutes # => 1440.0
|
|
385
385
|
def in_minutes
|
|
386
|
-
|
|
386
|
+
value / SECONDS_PER_MINUTE.to_f
|
|
387
387
|
end
|
|
388
388
|
|
|
389
389
|
# Returns the amount of hours a duration covers as a float
|
|
390
390
|
#
|
|
391
391
|
# 1.day.in_hours # => 24.0
|
|
392
392
|
def in_hours
|
|
393
|
-
|
|
393
|
+
value / SECONDS_PER_HOUR.to_f
|
|
394
394
|
end
|
|
395
395
|
|
|
396
396
|
# Returns the amount of days a duration covers as a float
|
|
397
397
|
#
|
|
398
398
|
# 12.hours.in_days # => 0.5
|
|
399
399
|
def in_days
|
|
400
|
-
|
|
400
|
+
value / SECONDS_PER_DAY.to_f
|
|
401
401
|
end
|
|
402
402
|
|
|
403
403
|
# Returns the amount of weeks a duration covers as a float
|
|
404
404
|
#
|
|
405
405
|
# 2.months.in_weeks # => 8.696
|
|
406
406
|
def in_weeks
|
|
407
|
-
|
|
407
|
+
value / SECONDS_PER_WEEK.to_f
|
|
408
408
|
end
|
|
409
409
|
|
|
410
410
|
# Returns the amount of months a duration covers as a float
|
|
411
411
|
#
|
|
412
412
|
# 9.weeks.in_months # => 2.07
|
|
413
413
|
def in_months
|
|
414
|
-
|
|
414
|
+
value / SECONDS_PER_MONTH.to_f
|
|
415
415
|
end
|
|
416
416
|
|
|
417
417
|
# Returns the amount of years a duration covers as a float
|
|
418
418
|
#
|
|
419
419
|
# 30.days.in_years # => 0.082
|
|
420
420
|
def in_years
|
|
421
|
-
|
|
421
|
+
value / SECONDS_PER_YEAR.to_f
|
|
422
422
|
end
|
|
423
423
|
|
|
424
424
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
|
@@ -491,17 +491,21 @@ module ActiveSupport
|
|
|
491
491
|
if @parts.empty?
|
|
492
492
|
time.since(sign * value)
|
|
493
493
|
else
|
|
494
|
-
@parts.
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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
|
|
504
506
|
end
|
|
507
|
+
|
|
508
|
+
time
|
|
505
509
|
end
|
|
506
510
|
end
|
|
507
511
|
|
|
@@ -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 }
|
|
@@ -240,5 +273,46 @@ module ActiveSupport
|
|
|
240
273
|
|
|
241
274
|
nil
|
|
242
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
|
|
243
317
|
end
|
|
244
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
|