activesupport 7.0.0.alpha2 → 7.0.0.rc1
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 +80 -0
- data/lib/active_support/cache/mem_cache_store.rb +9 -5
- data/lib/active_support/cache/memory_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +3 -8
- data/lib/active_support/cache/strategy/local_cache.rb +6 -12
- data/lib/active_support/callbacks.rb +145 -50
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/core_ext/array/conversions.rb +3 -1
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/class/subclasses.rb +4 -2
- data/lib/active_support/core_ext/date/calculations.rb +2 -2
- 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/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 +26 -1
- 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/numeric/conversions.rb +78 -75
- 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/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/conversions.rb +8 -8
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- 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/time/calculations.rb +1 -1
- 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 +2 -2
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -13
- data/lib/active_support/core_ext.rb +1 -0
- data/lib/active_support/current_attributes.rb +26 -25
- data/lib/active_support/descendants_tracker.rb +175 -69
- data/lib/active_support/error_reporter.rb +117 -0
- 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 +18 -9
- data/lib/active_support/gem_version.rb +1 -1
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n_railtie.rb +1 -1
- data/lib/active_support/inflector/inflections.rb +12 -3
- data/lib/active_support/inflector/methods.rb +2 -2
- data/lib/active_support/isolated_execution_state.rb +56 -0
- data/lib/active_support/logger_thread_safe_level.rb +2 -3
- data/lib/active_support/message_encryptor.rb +5 -0
- data/lib/active_support/multibyte/unicode.rb +0 -12
- data/lib/active_support/notifications/fanout.rb +61 -55
- data/lib/active_support/notifications/instrumenter.rb +15 -15
- data/lib/active_support/notifications.rb +5 -21
- data/lib/active_support/option_merger.rb +4 -0
- data/lib/active_support/per_thread_registry.rb +4 -0
- data/lib/active_support/railtie.rb +38 -11
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/subscriber.rb +2 -18
- data/lib/active_support/tagged_logging.rb +1 -1
- data/lib/active_support/testing/deprecation.rb +52 -1
- data/lib/active_support/testing/isolation.rb +1 -1
- data/lib/active_support/time_with_zone.rb +34 -6
- data/lib/active_support/values/time_zone.rb +5 -0
- data/lib/active_support/xml_mini.rb +3 -3
- data/lib/active_support.rb +7 -4
- metadata +23 -6
@@ -1,28 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "active_support/deprecation"
|
5
|
-
|
6
|
-
module ActiveSupport
|
7
|
-
module IncludeTimeWithZone # :nodoc:
|
8
|
-
# Extends the default Range#include? to support ActiveSupport::TimeWithZone.
|
9
|
-
#
|
10
|
-
# (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
|
11
|
-
#
|
12
|
-
def include?(value)
|
13
|
-
if self.begin.is_a?(TimeWithZone) || self.end.is_a?(TimeWithZone)
|
14
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
15
|
-
Using `Range#include?` to check the inclusion of a value in
|
16
|
-
a date time range is deprecated.
|
17
|
-
It is recommended to use `Range#cover?` instead of `Range#include?` to
|
18
|
-
check the inclusion of a value in a date time range.
|
19
|
-
MSG
|
20
|
-
cover?(value)
|
21
|
-
else
|
22
|
-
super
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
3
|
+
# frozen_string_literal: true
|
27
4
|
|
28
|
-
|
5
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
6
|
+
`active_support/core_ext/range/include_time_with_zone` is deprecated and will be removed in Rails 7.1.
|
7
|
+
MSG
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/range/conversions"
|
4
|
+
require "active_support/core_ext/range/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
|
4
5
|
require "active_support/core_ext/range/compare_range"
|
5
|
-
require "active_support/core_ext/range/include_time_with_zone"
|
6
6
|
require "active_support/core_ext/range/overlaps"
|
7
7
|
require "active_support/core_ext/range/each"
|
@@ -305,7 +305,7 @@ class Time
|
|
305
305
|
other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
|
306
306
|
end
|
307
307
|
alias_method :minus_without_coercion, :-
|
308
|
-
alias_method :-, :minus_with_coercion
|
308
|
+
alias_method :-, :minus_with_coercion # rubocop:disable Lint/DuplicateMethods
|
309
309
|
|
310
310
|
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
|
311
311
|
# can be chronologically compared with a Time
|
@@ -27,12 +27,12 @@ class Time
|
|
27
27
|
|
28
28
|
# Converts to a formatted string. See DATE_FORMATS for built-in formats.
|
29
29
|
#
|
30
|
-
# This method is aliased to <tt>
|
30
|
+
# This method is aliased to <tt>to_fs</tt>.
|
31
31
|
#
|
32
32
|
# time = Time.now # => 2007-01-18 06:10:17 -06:00
|
33
33
|
#
|
34
34
|
# time.to_formatted_s(:time) # => "06:10"
|
35
|
-
# time.
|
35
|
+
# time.to_formatted_s(:time) # => "06:10"
|
36
36
|
#
|
37
37
|
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
|
38
38
|
# time.to_formatted_s(:number) # => "20070118061017"
|
@@ -54,11 +54,12 @@ class Time
|
|
54
54
|
if formatter = DATE_FORMATS[format]
|
55
55
|
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
|
56
56
|
else
|
57
|
+
# Change to `to_s` when deprecation is gone. Also deprecate `to_default_s`.
|
57
58
|
to_default_s
|
58
59
|
end
|
59
60
|
end
|
61
|
+
alias_method :to_fs, :to_formatted_s
|
60
62
|
alias_method :to_default_s, :to_s
|
61
|
-
alias_method :to_s, :to_formatted_s
|
62
63
|
|
63
64
|
# Returns a formatted string of the offset from UTC, or an alternative
|
64
65
|
# string if the time zone is already UTC.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
class Time
|
6
|
+
NOT_SET = Object.new # :nodoc:
|
7
|
+
def to_s(format = NOT_SET) # :nodoc:
|
8
|
+
if formatter = DATE_FORMATS[format]
|
9
|
+
ActiveSupport::Deprecation.warn(
|
10
|
+
"Time#to_s(#{format.inspect}) is deprecated. Please use Time#to_formatted_s(#{format.inspect}) instead."
|
11
|
+
)
|
12
|
+
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
|
13
|
+
elsif format == NOT_SET
|
14
|
+
to_default_s
|
15
|
+
else
|
16
|
+
ActiveSupport::Deprecation.warn(
|
17
|
+
"Time#to_s(#{format.inspect}) is deprecated. Please use Time#to_formatted_s(#{format.inspect}) instead."
|
18
|
+
)
|
19
|
+
to_default_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -12,7 +12,7 @@ class Time
|
|
12
12
|
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
|
13
13
|
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
|
14
14
|
def zone
|
15
|
-
|
15
|
+
::ActiveSupport::IsolatedExecutionState[:time_zone] || zone_default
|
16
16
|
end
|
17
17
|
|
18
18
|
# Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
|
@@ -39,7 +39,7 @@ class Time
|
|
39
39
|
# end
|
40
40
|
# end
|
41
41
|
def zone=(time_zone)
|
42
|
-
|
42
|
+
::ActiveSupport::IsolatedExecutionState[:time_zone] = find_zone!(time_zone)
|
43
43
|
end
|
44
44
|
|
45
45
|
# Allows override of <tt>Time.zone</tt> locally inside supplied block;
|
@@ -4,4 +4,5 @@ require "active_support/core_ext/time/acts_like"
|
|
4
4
|
require "active_support/core_ext/time/calculations"
|
5
5
|
require "active_support/core_ext/time/compatibility"
|
6
6
|
require "active_support/core_ext/time/conversions"
|
7
|
+
require "active_support/core_ext/time/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
|
7
8
|
require "active_support/core_ext/time/zones"
|
@@ -1,15 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def parser
|
8
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
9
|
-
URI.parser is deprecated and will be removed in Rails 7.0.
|
10
|
-
Use `URI::DEFAULT_PARSER` instead.
|
11
|
-
MSG
|
12
|
-
URI::DEFAULT_PARSER
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
3
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
4
|
+
`active_support/core_ext/uri` is deprecated and will be removed in Rails 7.1.
|
5
|
+
MSG
|
@@ -98,25 +98,37 @@ module ActiveSupport
|
|
98
98
|
|
99
99
|
# Declares one or more attributes that will be given both class and instance accessor methods.
|
100
100
|
def attribute(*names)
|
101
|
-
generated_attribute_methods
|
101
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
|
102
102
|
names.each do |name|
|
103
|
-
|
104
|
-
|
103
|
+
owner.define_cached_method(name, namespace: :current_attributes) do |batch|
|
104
|
+
batch <<
|
105
|
+
"def #{name}" <<
|
106
|
+
"attributes[:#{name}]" <<
|
107
|
+
"end"
|
105
108
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
+
owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
|
110
|
+
batch <<
|
111
|
+
"def #{name}=(value)" <<
|
112
|
+
"attributes[:#{name}] = value" <<
|
113
|
+
"end"
|
109
114
|
end
|
110
115
|
end
|
111
116
|
end
|
112
117
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
|
119
|
+
names.each do |name|
|
120
|
+
owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
|
121
|
+
batch <<
|
122
|
+
"def #{name}" <<
|
123
|
+
"instance.#{name}" <<
|
124
|
+
"end"
|
125
|
+
end
|
126
|
+
owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
|
127
|
+
batch <<
|
128
|
+
"def #{name}=(value)" <<
|
129
|
+
"instance.#{name} = value" <<
|
130
|
+
"end"
|
131
|
+
end
|
120
132
|
end
|
121
133
|
end
|
122
134
|
end
|
@@ -143,24 +155,13 @@ module ActiveSupport
|
|
143
155
|
current_instances.clear
|
144
156
|
end
|
145
157
|
|
146
|
-
def _use_thread_variables=(value) # :nodoc:
|
147
|
-
clear_all
|
148
|
-
@@use_thread_variables = value
|
149
|
-
end
|
150
|
-
@@use_thread_variables = false
|
151
|
-
|
152
158
|
private
|
153
159
|
def generated_attribute_methods
|
154
160
|
@generated_attribute_methods ||= Module.new.tap { |mod| include mod }
|
155
161
|
end
|
156
162
|
|
157
163
|
def current_instances
|
158
|
-
|
159
|
-
Thread.current.thread_variable_get(:current_attributes_instances) ||
|
160
|
-
Thread.current.thread_variable_set(:current_attributes_instances, {})
|
161
|
-
else
|
162
|
-
Thread.current[:current_attributes_instances] ||= {}
|
163
|
-
end
|
164
|
+
IsolatedExecutionState[:current_attributes_instances] ||= {}
|
164
165
|
end
|
165
166
|
|
166
167
|
def current_instances_key
|
@@ -1,113 +1,219 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "weakref"
|
4
|
+
require "active_support/ruby_features"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
# This module provides an internal implementation to track descendants
|
7
8
|
# which is faster than iterating through ObjectSpace.
|
8
9
|
module DescendantsTracker
|
9
|
-
@@direct_descendants = {}
|
10
|
-
|
11
10
|
class << self
|
12
11
|
def direct_descendants(klass)
|
13
|
-
|
14
|
-
|
12
|
+
ActiveSupport::Deprecation.warn(<<~MSG)
|
13
|
+
ActiveSupport::DescendantsTracker.direct_descendants is deprecated and will be removed in Rails 7.1.
|
14
|
+
Use ActiveSupport::DescendantsTracker.subclasses instead.
|
15
|
+
MSG
|
16
|
+
subclasses(klass)
|
15
17
|
end
|
16
|
-
|
18
|
+
end
|
19
|
+
|
20
|
+
@clear_disabled = false
|
21
|
+
|
22
|
+
if RubyFeatures::CLASS_SUBCLASSES
|
23
|
+
@@excluded_descendants = if RUBY_ENGINE == "ruby"
|
24
|
+
# On MRI `ObjectSpace::WeakMap` keys are weak references.
|
25
|
+
# So we can simply use WeakMap as a `Set`.
|
26
|
+
ObjectSpace::WeakMap.new
|
27
|
+
else
|
28
|
+
# On TruffleRuby `ObjectSpace::WeakMap` keys are strong references.
|
29
|
+
# So we use `object_id` as a key and the actual object as a value.
|
30
|
+
#
|
31
|
+
# JRuby for now doesn't have Class#descendant, but when it will, it will likely
|
32
|
+
# have the same WeakMap semantic than Truffle so we future proof this as much as possible.
|
33
|
+
class WeakSet # :nodoc:
|
34
|
+
def initialize
|
35
|
+
@map = ObjectSpace::WeakMap.new
|
36
|
+
end
|
17
37
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
38
|
+
def [](object)
|
39
|
+
@map.key?(object.object_id)
|
40
|
+
end
|
41
|
+
|
42
|
+
def []=(object, _present)
|
43
|
+
@map[object_id] = object
|
44
|
+
end
|
45
|
+
end
|
46
|
+
WeakSet.new
|
22
47
|
end
|
23
48
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
49
|
+
class << self
|
50
|
+
def disable_clear! # :nodoc:
|
51
|
+
unless @clear_disabled
|
52
|
+
@clear_disabled = true
|
53
|
+
remove_method(:subclasses)
|
54
|
+
remove_method(:descendants)
|
55
|
+
@@excluded_descendants = nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def subclasses(klass)
|
60
|
+
klass.subclasses
|
28
61
|
end
|
29
62
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
63
|
+
def descendants(klass)
|
64
|
+
klass.descendants
|
65
|
+
end
|
66
|
+
|
67
|
+
def clear(classes) # :nodoc:
|
68
|
+
raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled
|
69
|
+
|
70
|
+
classes.each do |klass|
|
71
|
+
@@excluded_descendants[klass] = true
|
72
|
+
klass.descendants.each do |descendant|
|
73
|
+
@@excluded_descendants[descendant] = true
|
36
74
|
end
|
37
75
|
end
|
38
76
|
end
|
77
|
+
|
78
|
+
def native? # :nodoc:
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def subclasses
|
84
|
+
subclasses = super
|
85
|
+
subclasses.reject! { |d| @@excluded_descendants[d] }
|
86
|
+
subclasses
|
87
|
+
end
|
88
|
+
|
89
|
+
def descendants
|
90
|
+
descendants = super
|
91
|
+
descendants.reject! { |d| @@excluded_descendants[d] }
|
92
|
+
descendants
|
39
93
|
end
|
40
94
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
95
|
+
def direct_descendants
|
96
|
+
ActiveSupport::Deprecation.warn(<<~MSG)
|
97
|
+
ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1.
|
98
|
+
Use #subclasses instead.
|
99
|
+
MSG
|
100
|
+
subclasses
|
45
101
|
end
|
102
|
+
else
|
103
|
+
@@direct_descendants = {}
|
104
|
+
|
105
|
+
class << self
|
106
|
+
def disable_clear! # :nodoc:
|
107
|
+
@clear_disabled = true
|
108
|
+
end
|
109
|
+
|
110
|
+
def subclasses(klass)
|
111
|
+
descendants = @@direct_descendants[klass]
|
112
|
+
descendants ? descendants.to_a : []
|
113
|
+
end
|
114
|
+
|
115
|
+
def descendants(klass)
|
116
|
+
arr = []
|
117
|
+
accumulate_descendants(klass, arr)
|
118
|
+
arr
|
119
|
+
end
|
120
|
+
|
121
|
+
def clear(classes) # :nodoc:
|
122
|
+
raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled
|
46
123
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
124
|
+
@@direct_descendants.each do |klass, direct_descendants_of_klass|
|
125
|
+
if classes.member?(klass)
|
126
|
+
@@direct_descendants.delete(klass)
|
127
|
+
else
|
128
|
+
direct_descendants_of_klass.reject! do |direct_descendant_of_class|
|
129
|
+
classes.member?(direct_descendant_of_class)
|
130
|
+
end
|
53
131
|
end
|
54
132
|
end
|
55
133
|
end
|
56
|
-
end
|
57
134
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
135
|
+
def native? # :nodoc:
|
136
|
+
false
|
137
|
+
end
|
62
138
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
139
|
+
# This is the only method that is not thread safe, but is only ever called
|
140
|
+
# during the eager loading phase.
|
141
|
+
def store_inherited(klass, descendant)
|
142
|
+
(@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
|
143
|
+
end
|
67
144
|
|
68
|
-
|
69
|
-
|
70
|
-
|
145
|
+
private
|
146
|
+
def accumulate_descendants(klass, acc)
|
147
|
+
if direct_descendants = @@direct_descendants[klass]
|
148
|
+
direct_descendants.each do |direct_descendant|
|
149
|
+
acc << direct_descendant
|
150
|
+
accumulate_descendants(direct_descendant, acc)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
71
155
|
|
72
|
-
|
73
|
-
|
74
|
-
|
156
|
+
def inherited(base)
|
157
|
+
DescendantsTracker.store_inherited(self, base)
|
158
|
+
super
|
159
|
+
end
|
75
160
|
|
76
|
-
def
|
77
|
-
|
161
|
+
def direct_descendants
|
162
|
+
ActiveSupport::Deprecation.warn(<<~MSG)
|
163
|
+
ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1.
|
164
|
+
Use #subclasses instead.
|
165
|
+
MSG
|
166
|
+
DescendantsTracker.subclasses(self)
|
78
167
|
end
|
79
168
|
|
80
|
-
def
|
81
|
-
|
169
|
+
def subclasses
|
170
|
+
DescendantsTracker.subclasses(self)
|
82
171
|
end
|
83
172
|
|
84
|
-
def
|
85
|
-
|
173
|
+
def descendants
|
174
|
+
DescendantsTracker.descendants(self)
|
86
175
|
end
|
87
176
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
177
|
+
# DescendantsArray is an array that contains weak references to classes.
|
178
|
+
class DescendantsArray # :nodoc:
|
179
|
+
include Enumerable
|
180
|
+
|
181
|
+
def initialize
|
182
|
+
@refs = []
|
94
183
|
end
|
95
|
-
self
|
96
|
-
end
|
97
184
|
|
98
|
-
|
99
|
-
|
100
|
-
|
185
|
+
def initialize_copy(orig)
|
186
|
+
@refs = @refs.dup
|
187
|
+
end
|
101
188
|
|
102
|
-
|
103
|
-
|
104
|
-
|
189
|
+
def <<(klass)
|
190
|
+
@refs << WeakRef.new(klass)
|
191
|
+
end
|
105
192
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
193
|
+
def each
|
194
|
+
@refs.reject! do |ref|
|
195
|
+
yield ref.__getobj__
|
196
|
+
false
|
197
|
+
rescue WeakRef::RefError
|
198
|
+
true
|
199
|
+
end
|
200
|
+
self
|
201
|
+
end
|
202
|
+
|
203
|
+
def refs_size
|
204
|
+
@refs.size
|
205
|
+
end
|
206
|
+
|
207
|
+
def cleanup!
|
208
|
+
@refs.delete_if { |ref| !ref.weakref_alive? }
|
209
|
+
end
|
210
|
+
|
211
|
+
def reject!
|
212
|
+
@refs.reject! do |ref|
|
213
|
+
yield ref.__getobj__
|
214
|
+
rescue WeakRef::RefError
|
215
|
+
true
|
216
|
+
end
|
111
217
|
end
|
112
218
|
end
|
113
219
|
end
|
@@ -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
|