activesupport 7.0.8.7 → 7.2.2.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 +143 -459
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +39 -7
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +49 -17
- data/lib/active_support/cache/mem_cache_store.rb +94 -128
- data/lib/active_support/cache/memory_store.rb +80 -25
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +165 -152
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +29 -14
- data/lib/active_support/cache.rb +363 -291
- data/lib/active_support/callbacks.rb +118 -134
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +10 -0
- data/lib/active_support/core_ext/array/conversions.rb +1 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/class/subclasses.rb +17 -34
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +1 -2
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
- 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 +2 -2
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +3 -75
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +4 -4
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +28 -7
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +1 -5
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -5
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +34 -177
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +36 -30
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +1 -3
- data/lib/active_support/core_ext/time/zones.rb +4 -4
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +53 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +65 -42
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +3 -5
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -172
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +30 -9
- data/lib/active_support/encrypted_file.rb +9 -4
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +160 -36
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +4 -5
- data/lib/active_support/file_update_checker.rb +5 -3
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +41 -25
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +23 -11
- data/lib/active_support/inflector/transliterate.rb +3 -1
- data/lib/active_support/isolated_execution_state.rb +26 -22
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +25 -43
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +6 -4
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +74 -34
- data/lib/active_support/logger.rb +22 -60
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +197 -53
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +305 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +220 -89
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +4 -2
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +29 -28
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +379 -318
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +67 -15
- data/lib/active_support/parameter_filter.rb +84 -69
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +25 -20
- data/lib/active_support/reloader.rb +12 -4
- data/lib/active_support/rescuable.rb +2 -0
- data/lib/active_support/secure_compare_rotator.rb +16 -9
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -25
- data/lib/active_support/test_case.rb +156 -7
- data/lib/active_support/testing/assertions.rb +28 -12
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +21 -9
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +12 -18
- data/lib/active_support/values/time_zone.rb +25 -14
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +12 -3
- data/lib/active_support.rb +15 -3
- metadata +140 -19
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/object/blank"
|
4
|
-
|
5
3
|
module ActiveSupport
|
6
4
|
class Duration
|
7
5
|
# Serializes duration to string according to ISO 8601 Duration format.
|
@@ -37,7 +35,6 @@ module ActiveSupport
|
|
37
35
|
# Return pair of duration's parts and whole duration sign.
|
38
36
|
# Parts are summarized (as they can become repetitive due to addition, etc).
|
39
37
|
# Zero parts are removed as not significant.
|
40
|
-
# If all parts are negative it will negate all of them and return minus as a sign.
|
41
38
|
def normalize
|
42
39
|
parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
|
43
40
|
p[k] += v unless v.zero?
|
@@ -52,7 +49,7 @@ module ActiveSupport
|
|
52
49
|
end
|
53
50
|
|
54
51
|
def week_mixed_with_date?(parts)
|
55
|
-
parts.key?(:weeks) &&
|
52
|
+
parts.key?(:weeks) && parts.keys.intersect?(DATE_COMPONENTS)
|
56
53
|
end
|
57
54
|
|
58
55
|
def format_seconds(seconds)
|
@@ -3,9 +3,10 @@
|
|
3
3
|
require "active_support/core_ext/array/conversions"
|
4
4
|
require "active_support/core_ext/module/delegation"
|
5
5
|
require "active_support/core_ext/object/acts_like"
|
6
|
-
require "active_support/core_ext/string/filters"
|
7
6
|
|
8
7
|
module ActiveSupport
|
8
|
+
# = Active Support \Duration
|
9
|
+
#
|
9
10
|
# Provides accurate date and time measurements using Date#advance and
|
10
11
|
# Time#advance, respectively. It mainly supports the methods on Numeric.
|
11
12
|
#
|
@@ -13,7 +14,7 @@ module ActiveSupport
|
|
13
14
|
class Duration
|
14
15
|
class Scalar < Numeric # :nodoc:
|
15
16
|
attr_reader :value
|
16
|
-
delegate :to_i, :to_f, :to_s, to:
|
17
|
+
delegate :to_i, :to_f, :to_s, to: :@value
|
17
18
|
|
18
19
|
def initialize(value)
|
19
20
|
@value = value
|
@@ -220,6 +221,8 @@ module ActiveSupport
|
|
220
221
|
end
|
221
222
|
end
|
222
223
|
|
224
|
+
Delegation.generate(self, [:to_f, :positive?, :negative?, :zero?, :abs], to: :@value, as: Integer, nilable: false)
|
225
|
+
|
223
226
|
def initialize(value, parts, variable = nil) # :nodoc:
|
224
227
|
@value, @parts = value, parts
|
225
228
|
@parts.reject! { |k, v| v.zero? } unless value == 0
|
@@ -231,7 +234,10 @@ module ActiveSupport
|
|
231
234
|
end
|
232
235
|
end
|
233
236
|
|
234
|
-
# 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}
|
235
241
|
def parts
|
236
242
|
@parts.dup
|
237
243
|
end
|
@@ -365,8 +371,8 @@ module ActiveSupport
|
|
365
371
|
# 1.year.to_i # => 31556952
|
366
372
|
#
|
367
373
|
# In such cases, Ruby's core
|
368
|
-
# Date[https://ruby-
|
369
|
-
# 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
|
370
376
|
# date and time arithmetic.
|
371
377
|
def to_i
|
372
378
|
@value.to_i
|
@@ -503,8 +509,8 @@ module ActiveSupport
|
|
503
509
|
value.respond_to?(method)
|
504
510
|
end
|
505
511
|
|
506
|
-
def method_missing(
|
507
|
-
value.public_send(
|
512
|
+
def method_missing(...)
|
513
|
+
value.public_send(...)
|
508
514
|
end
|
509
515
|
|
510
516
|
def raise_type_error(other)
|
@@ -4,9 +4,12 @@ require "yaml"
|
|
4
4
|
require "active_support/encrypted_file"
|
5
5
|
require "active_support/ordered_options"
|
6
6
|
require "active_support/core_ext/object/inclusion"
|
7
|
+
require "active_support/core_ext/hash/keys"
|
7
8
|
require "active_support/core_ext/module/delegation"
|
8
9
|
|
9
10
|
module ActiveSupport
|
11
|
+
# = Encrypted Configuration
|
12
|
+
#
|
10
13
|
# Provides convenience methods on top of EncryptedFile to access values stored
|
11
14
|
# as encrypted YAML.
|
12
15
|
#
|
@@ -30,12 +33,23 @@ module ActiveSupport
|
|
30
33
|
# # => KeyError
|
31
34
|
#
|
32
35
|
class EncryptedConfiguration < EncryptedFile
|
33
|
-
|
36
|
+
class InvalidContentError < RuntimeError
|
37
|
+
def initialize(content_path)
|
38
|
+
super "Invalid YAML in '#{content_path}'."
|
39
|
+
end
|
40
|
+
|
41
|
+
def message
|
42
|
+
cause.is_a?(Psych::SyntaxError) ? "#{super}\n\n #{cause.message}" : super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
34
46
|
delegate_missing_to :options
|
35
47
|
|
36
48
|
def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
|
37
49
|
super content_path: config_path, key_path: key_path,
|
38
50
|
env_key: env_key, raise_if_missing_key: raise_if_missing_key
|
51
|
+
@config = nil
|
52
|
+
@options = nil
|
39
53
|
end
|
40
54
|
|
41
55
|
# Reads the file and returns the decrypted content. See EncryptedFile#read.
|
@@ -46,10 +60,8 @@ module ActiveSupport
|
|
46
60
|
""
|
47
61
|
end
|
48
62
|
|
49
|
-
def
|
50
|
-
deserialize(
|
51
|
-
|
52
|
-
super
|
63
|
+
def validate! # :nodoc:
|
64
|
+
deserialize(read)
|
53
65
|
end
|
54
66
|
|
55
67
|
# Returns the decrypted content as a Hash with symbolized keys.
|
@@ -64,11 +76,15 @@ module ActiveSupport
|
|
64
76
|
@config ||= deserialize(read).deep_symbolize_keys
|
65
77
|
end
|
66
78
|
|
79
|
+
def inspect # :nodoc:
|
80
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
81
|
+
end
|
82
|
+
|
67
83
|
private
|
68
84
|
def deep_transform(hash)
|
69
85
|
return hash unless hash.is_a?(Hash)
|
70
86
|
|
71
|
-
h = ActiveSupport::
|
87
|
+
h = ActiveSupport::OrderedOptions.new
|
72
88
|
hash.each do |k, v|
|
73
89
|
h[k] = deep_transform(v)
|
74
90
|
end
|
@@ -79,9 +95,14 @@ module ActiveSupport
|
|
79
95
|
@options ||= deep_transform(config)
|
80
96
|
end
|
81
97
|
|
82
|
-
def deserialize(
|
83
|
-
|
84
|
-
|
98
|
+
def deserialize(content)
|
99
|
+
config = YAML.respond_to?(:unsafe_load) ?
|
100
|
+
YAML.unsafe_load(content, filename: content_path) :
|
101
|
+
YAML.load(content, filename: content_path)
|
102
|
+
|
103
|
+
config.presence || {}
|
104
|
+
rescue Psych::SyntaxError
|
105
|
+
raise InvalidContentError.new(content_path)
|
85
106
|
end
|
86
107
|
end
|
87
108
|
end
|
@@ -53,6 +53,12 @@ module ActiveSupport
|
|
53
53
|
read_env_key || read_key_file || handle_missing_key
|
54
54
|
end
|
55
55
|
|
56
|
+
# Returns truthy if #key is truthy. Returns falsy otherwise. Unlike #key,
|
57
|
+
# does not raise MissingKeyError when +raise_if_missing_key+ is true.
|
58
|
+
def key?
|
59
|
+
read_env_key || read_key_file
|
60
|
+
end
|
61
|
+
|
56
62
|
# Reads the file and returns the decrypted content.
|
57
63
|
#
|
58
64
|
# Raises:
|
@@ -63,7 +69,7 @@ module ActiveSupport
|
|
63
69
|
# decrypted or verified.
|
64
70
|
def read
|
65
71
|
if !key.nil? && content_path.exist?
|
66
|
-
decrypt content_path.binread
|
72
|
+
decrypt content_path.binread.strip
|
67
73
|
else
|
68
74
|
raise MissingContentError, content_path
|
69
75
|
end
|
@@ -104,7 +110,7 @@ module ActiveSupport
|
|
104
110
|
end
|
105
111
|
|
106
112
|
def encryptor
|
107
|
-
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
|
113
|
+
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER, serializer: Marshal)
|
108
114
|
end
|
109
115
|
|
110
116
|
|
@@ -113,8 +119,7 @@ module ActiveSupport
|
|
113
119
|
end
|
114
120
|
|
115
121
|
def read_key_file
|
116
|
-
|
117
|
-
@key_file_contents = (key_path.binread.strip if key_path.exist?)
|
122
|
+
@key_file_contents ||= (key_path.binread.strip if key_path.exist?)
|
118
123
|
end
|
119
124
|
|
120
125
|
def handle_missing_key
|
@@ -1,20 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/string_inquirer"
|
4
|
+
require "active_support/core_ext/object/inclusion"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
class EnvironmentInquirer < StringInquirer # :nodoc:
|
7
|
-
|
8
|
+
# Optimization for the three default environments, so this inquirer doesn't need to rely on
|
9
|
+
# the slower delegation through method_missing that StringInquirer would normally entail.
|
10
|
+
DEFAULT_ENVIRONMENTS = %w[ development test production ]
|
11
|
+
|
12
|
+
# Environments that'll respond true for #local?
|
13
|
+
LOCAL_ENVIRONMENTS = %w[ development test ]
|
14
|
+
|
8
15
|
def initialize(env)
|
16
|
+
raise(ArgumentError, "'local' is a reserved environment name") if env == "local"
|
17
|
+
|
9
18
|
super(env)
|
10
19
|
|
11
20
|
DEFAULT_ENVIRONMENTS.each do |default|
|
12
21
|
instance_variable_set :"@#{default}", env == default
|
13
22
|
end
|
23
|
+
|
24
|
+
@local = in? LOCAL_ENVIRONMENTS
|
14
25
|
end
|
15
26
|
|
16
27
|
DEFAULT_ENVIRONMENTS.each do |env|
|
17
|
-
class_eval
|
28
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
29
|
+
def #{env}?
|
30
|
+
@#{env}
|
31
|
+
end
|
32
|
+
RUBY
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns true if we're in the development or test environment.
|
36
|
+
def local?
|
37
|
+
@local
|
18
38
|
end
|
19
39
|
end
|
20
40
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport::ErrorReporter::TestHelper # :nodoc:
|
4
|
+
class ErrorSubscriber
|
5
|
+
attr_reader :events
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@events = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def report(error, handled:, severity:, source:, context:)
|
12
|
+
@events << [error, handled, severity, source, context]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
|
+
# = Active Support \Error Reporter
|
5
|
+
#
|
4
6
|
# +ActiveSupport::ErrorReporter+ is a common interface for error reporting services.
|
5
7
|
#
|
6
|
-
# To rescue and report any unhandled error, you can use the
|
8
|
+
# To rescue and report any unhandled error, you can use the #handle method:
|
7
9
|
#
|
8
10
|
# Rails.error.handle do
|
9
11
|
# do_something!
|
@@ -11,68 +13,151 @@ module ActiveSupport
|
|
11
13
|
#
|
12
14
|
# If an error is raised, it will be reported and swallowed.
|
13
15
|
#
|
14
|
-
# Alternatively if you want to report the error but not swallow it, you can use
|
16
|
+
# Alternatively, if you want to report the error but not swallow it, you can use #record:
|
15
17
|
#
|
16
18
|
# Rails.error.record do
|
17
19
|
# do_something!
|
18
20
|
# end
|
19
21
|
#
|
20
|
-
# Both methods can be restricted to only
|
22
|
+
# Both methods can be restricted to handle only a specific error class:
|
21
23
|
#
|
22
24
|
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
|
23
25
|
#
|
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
26
|
class ErrorReporter
|
42
27
|
SEVERITIES = %i(error warning info)
|
28
|
+
DEFAULT_SOURCE = "application"
|
29
|
+
DEFAULT_RESCUE = [StandardError].freeze
|
43
30
|
|
44
|
-
attr_accessor :logger
|
31
|
+
attr_accessor :logger, :debug_mode
|
32
|
+
|
33
|
+
UnexpectedError = Class.new(Exception)
|
45
34
|
|
46
35
|
def initialize(*subscribers, logger: nil)
|
47
36
|
@subscribers = subscribers.flatten
|
48
37
|
@logger = logger
|
38
|
+
@debug_mode = false
|
49
39
|
end
|
50
40
|
|
51
|
-
#
|
41
|
+
# Evaluates the given block, reporting and swallowing any unhandled error.
|
42
|
+
# If no error is raised, returns the return value of the block. Otherwise,
|
43
|
+
# returns the result of +fallback.call+, or +nil+ if +fallback+ is not
|
44
|
+
# specified.
|
52
45
|
#
|
46
|
+
# # Will report a TypeError to all subscribers and return nil.
|
53
47
|
# Rails.error.handle do
|
54
48
|
# 1 + '1'
|
55
49
|
# end
|
56
50
|
#
|
57
|
-
|
51
|
+
# Can be restricted to handle only specific error classes:
|
52
|
+
#
|
53
|
+
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
|
54
|
+
#
|
55
|
+
# ==== Options
|
56
|
+
#
|
57
|
+
# * +:severity+ - This value is passed along to subscribers to indicate how
|
58
|
+
# important the error report is. Can be +:error+, +:warning+, or +:info+.
|
59
|
+
# Defaults to +:warning+.
|
60
|
+
#
|
61
|
+
# * +:context+ - Extra information that is passed along to subscribers. For
|
62
|
+
# example:
|
63
|
+
#
|
64
|
+
# Rails.error.handle(context: { section: "admin" }) do
|
65
|
+
# # ...
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# * +:fallback+ - A callable that provides +handle+'s return value when an
|
69
|
+
# unhandled error is raised. For example:
|
70
|
+
#
|
71
|
+
# user = Rails.error.handle(fallback: -> { User.anonymous }) do
|
72
|
+
# User.find_by(params)
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# * +:source+ - This value is passed along to subscribers to indicate the
|
76
|
+
# source of the error. Subscribers can use this value to ignore certain
|
77
|
+
# errors. Defaults to <tt>"application"</tt>.
|
78
|
+
def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
|
79
|
+
error_classes = DEFAULT_RESCUE if error_classes.empty?
|
58
80
|
yield
|
59
|
-
rescue
|
60
|
-
report(error, handled: true, severity: severity, context: context)
|
81
|
+
rescue *error_classes => error
|
82
|
+
report(error, handled: true, severity: severity, context: context, source: source)
|
61
83
|
fallback.call if fallback
|
62
84
|
end
|
63
85
|
|
64
|
-
|
86
|
+
# Evaluates the given block, reporting and re-raising any unhandled error.
|
87
|
+
# If no error is raised, returns the return value of the block.
|
88
|
+
#
|
89
|
+
# # Will report a TypeError to all subscribers and re-raise it.
|
90
|
+
# Rails.error.record do
|
91
|
+
# 1 + '1'
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# Can be restricted to handle only specific error classes:
|
95
|
+
#
|
96
|
+
# tags = Rails.error.record(Redis::BaseError) { redis.get("tags") }
|
97
|
+
#
|
98
|
+
# ==== Options
|
99
|
+
#
|
100
|
+
# * +:severity+ - This value is passed along to subscribers to indicate how
|
101
|
+
# important the error report is. Can be +:error+, +:warning+, or +:info+.
|
102
|
+
# Defaults to +:error+.
|
103
|
+
#
|
104
|
+
# * +:context+ - Extra information that is passed along to subscribers. For
|
105
|
+
# example:
|
106
|
+
#
|
107
|
+
# Rails.error.record(context: { section: "admin" }) do
|
108
|
+
# # ...
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# * +:source+ - This value is passed along to subscribers to indicate the
|
112
|
+
# source of the error. Subscribers can use this value to ignore certain
|
113
|
+
# errors. Defaults to <tt>"application"</tt>.
|
114
|
+
def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
|
115
|
+
error_classes = DEFAULT_RESCUE if error_classes.empty?
|
65
116
|
yield
|
66
|
-
rescue
|
67
|
-
report(error, handled: false, severity: severity, context: context)
|
117
|
+
rescue *error_classes => error
|
118
|
+
report(error, handled: false, severity: severity, context: context, source: source)
|
68
119
|
raise
|
69
120
|
end
|
70
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
|
+
error.set_backtrace(caller(1)) if error.backtrace.nil?
|
148
|
+
|
149
|
+
if @debug_mode
|
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
|
+
|
71
156
|
# Register a new error subscriber. The subscriber must respond to
|
72
157
|
#
|
73
|
-
# report(Exception, handled: Boolean, context: Hash)
|
158
|
+
# report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)
|
74
159
|
#
|
75
|
-
# The +report+ method
|
160
|
+
# The +report+ method <b>should never</b> raise an error.
|
76
161
|
def subscribe(subscriber)
|
77
162
|
unless subscriber.respond_to?(:report)
|
78
163
|
raise ArgumentError, "Error subscribers must respond to #report"
|
@@ -80,26 +165,61 @@ module ActiveSupport
|
|
80
165
|
@subscribers << subscriber
|
81
166
|
end
|
82
167
|
|
83
|
-
#
|
168
|
+
# Unregister an error subscriber. Accepts either a subscriber or a class.
|
169
|
+
#
|
170
|
+
# subscriber = MyErrorSubscriber.new
|
171
|
+
# Rails.error.subscribe(subscriber)
|
172
|
+
#
|
173
|
+
# Rails.error.unsubscribe(subscriber)
|
174
|
+
# # or
|
175
|
+
# Rails.error.unsubscribe(MyErrorSubscriber)
|
176
|
+
def unsubscribe(subscriber)
|
177
|
+
@subscribers.delete_if { |s| subscriber === s }
|
178
|
+
end
|
179
|
+
|
180
|
+
# Prevent a subscriber from being notified of errors for the
|
181
|
+
# duration of the block. You may pass in the subscriber itself, or its class.
|
182
|
+
#
|
183
|
+
# This can be helpful for error reporting service integrations, when they wish
|
184
|
+
# to handle any errors higher in the stack.
|
185
|
+
def disable(subscriber)
|
186
|
+
disabled_subscribers = (ActiveSupport::IsolatedExecutionState[self] ||= [])
|
187
|
+
disabled_subscribers << subscriber
|
188
|
+
begin
|
189
|
+
yield
|
190
|
+
ensure
|
191
|
+
disabled_subscribers.delete(subscriber)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Update the execution context that is accessible to error subscribers. Any
|
196
|
+
# context passed to #handle, #record, or #report will be merged with the
|
197
|
+
# context set here.
|
84
198
|
#
|
85
199
|
# Rails.error.set_context(section: "checkout", user_id: @user.id)
|
86
200
|
#
|
87
|
-
# See +ActiveSupport::ExecutionContext.set+
|
88
201
|
def set_context(...)
|
89
202
|
ActiveSupport::ExecutionContext.set(...)
|
90
203
|
end
|
91
204
|
|
92
|
-
#
|
205
|
+
# Report an error directly to subscribers. You can use this method when the
|
206
|
+
# block-based #handle and #record methods are not suitable.
|
207
|
+
#
|
208
|
+
# Rails.error.report(error)
|
93
209
|
#
|
94
|
-
|
95
|
-
|
210
|
+
def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
|
211
|
+
return if error.instance_variable_defined?(:@__rails_error_reported)
|
212
|
+
|
96
213
|
unless SEVERITIES.include?(severity)
|
97
214
|
raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
|
98
215
|
end
|
99
216
|
|
100
217
|
full_context = ActiveSupport::ExecutionContext.to_h.merge(context)
|
218
|
+
disabled_subscribers = ActiveSupport::IsolatedExecutionState[self]
|
101
219
|
@subscribers.each do |subscriber|
|
102
|
-
|
220
|
+
unless disabled_subscribers&.any? { |s| s === subscriber }
|
221
|
+
subscriber.report(error, handled: handled, severity: severity, context: full_context, source: source)
|
222
|
+
end
|
103
223
|
rescue => subscriber_error
|
104
224
|
if logger
|
105
225
|
logger.fatal(
|
@@ -111,6 +231,10 @@ module ActiveSupport
|
|
111
231
|
end
|
112
232
|
end
|
113
233
|
|
234
|
+
unless error.frozen?
|
235
|
+
error.instance_variable_set(:@__rails_error_reported, true)
|
236
|
+
end
|
237
|
+
|
114
238
|
nil
|
115
239
|
end
|
116
240
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "active_support/error_reporter"
|
4
4
|
require "active_support/callbacks"
|
5
|
-
require "concurrent/hash"
|
6
5
|
|
7
6
|
module ActiveSupport
|
8
7
|
class ExecutionWrapper
|
@@ -84,14 +83,14 @@ module ActiveSupport
|
|
84
83
|
end
|
85
84
|
|
86
85
|
# Perform the work in the supplied block as an execution.
|
87
|
-
def self.wrap
|
86
|
+
def self.wrap(source: "application.active_support")
|
88
87
|
return yield if active?
|
89
88
|
|
90
89
|
instance = run!
|
91
90
|
begin
|
92
91
|
yield
|
93
92
|
rescue => error
|
94
|
-
error_reporter
|
93
|
+
error_reporter&.report(error, handled: false, source: source)
|
95
94
|
raise
|
96
95
|
ensure
|
97
96
|
instance.complete!
|
@@ -108,8 +107,8 @@ module ActiveSupport
|
|
108
107
|
end
|
109
108
|
end
|
110
109
|
|
111
|
-
def self.error_reporter
|
112
|
-
|
110
|
+
def self.error_reporter # :nodoc:
|
111
|
+
ActiveSupport.error_reporter
|
113
112
|
end
|
114
113
|
|
115
114
|
def self.active_key # :nodoc:
|
@@ -3,7 +3,9 @@
|
|
3
3
|
require "active_support/core_ext/time/calculations"
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
|
-
#
|
6
|
+
# = \File Update Checker
|
7
|
+
#
|
8
|
+
# FileUpdateChecker specifies the API used by \Rails to watch files
|
7
9
|
# and control reloading. The API depends on four methods:
|
8
10
|
#
|
9
11
|
# * +initialize+ which expects two parameters and one block as
|
@@ -20,7 +22,7 @@ module ActiveSupport
|
|
20
22
|
# After initialization, a call to +execute_if_updated+ must execute
|
21
23
|
# the block only if there was really a change in the filesystem.
|
22
24
|
#
|
23
|
-
# This class is used by Rails to reload the I18n framework whenever
|
25
|
+
# This class is used by \Rails to reload the I18n framework whenever
|
24
26
|
# they are changed upon a new request.
|
25
27
|
#
|
26
28
|
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
|
@@ -102,7 +104,7 @@ module ActiveSupport
|
|
102
104
|
@watched || begin
|
103
105
|
all = @files.select { |f| File.exist?(f) }
|
104
106
|
all.concat(Dir[@glob]) if @glob
|
105
|
-
all
|
107
|
+
all.tap(&:uniq!)
|
106
108
|
end
|
107
109
|
end
|
108
110
|
|
@@ -2,42 +2,21 @@
|
|
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
|
9
|
-
ForkTracker.
|
9
|
+
ForkTracker.after_fork_callback
|
10
10
|
end
|
11
11
|
pid
|
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
|
|
39
18
|
class << self
|
40
|
-
def
|
19
|
+
def after_fork_callback
|
41
20
|
new_pid = Process.pid
|
42
21
|
if @pid != new_pid
|
43
22
|
@callbacks.each(&:call)
|
@@ -46,14 +25,7 @@ module ActiveSupport
|
|
46
25
|
end
|
47
26
|
|
48
27
|
def hook!
|
49
|
-
|
50
|
-
::Process.singleton_class.prepend(ModernCoreExt)
|
51
|
-
elsif Process.respond_to?(:fork)
|
52
|
-
::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
|
53
|
-
::Kernel.prepend(CoreExtPrivate)
|
54
|
-
::Kernel.singleton_class.prepend(CoreExt)
|
55
|
-
::Process.singleton_class.prepend(CoreExt)
|
56
|
-
end
|
28
|
+
::Process.singleton_class.prepend(CoreExt)
|
57
29
|
end
|
58
30
|
|
59
31
|
def after_fork(&block)
|