activesupport 7.0.7.2 → 7.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +742 -285
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +25 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/builder.rb +1 -1
  10. data/lib/active_support/cache/coder.rb +153 -0
  11. data/lib/active_support/cache/entry.rb +128 -0
  12. data/lib/active_support/cache/file_store.rb +36 -9
  13. data/lib/active_support/cache/mem_cache_store.rb +84 -68
  14. data/lib/active_support/cache/memory_store.rb +76 -24
  15. data/lib/active_support/cache/null_store.rb +6 -0
  16. data/lib/active_support/cache/redis_cache_store.rb +128 -131
  17. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +20 -8
  19. data/lib/active_support/cache.rb +304 -246
  20. data/lib/active_support/callbacks.rb +38 -18
  21. data/lib/active_support/concern.rb +4 -2
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  23. data/lib/active_support/concurrency/null_lock.rb +13 -0
  24. data/lib/active_support/configurable.rb +10 -0
  25. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  26. data/lib/active_support/core_ext/array.rb +0 -1
  27. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  28. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  29. data/lib/active_support/core_ext/date.rb +0 -1
  30. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  31. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  32. data/lib/active_support/core_ext/date_time.rb +0 -1
  33. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  34. data/lib/active_support/core_ext/enumerable.rb +3 -75
  35. data/lib/active_support/core_ext/erb/util.rb +196 -0
  36. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  38. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  39. data/lib/active_support/core_ext/module/delegation.rb +40 -11
  40. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  41. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  42. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  43. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  44. data/lib/active_support/core_ext/numeric.rb +0 -1
  45. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  46. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  47. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  48. data/lib/active_support/core_ext/object/json.rb +10 -2
  49. data/lib/active_support/core_ext/object/with.rb +44 -0
  50. data/lib/active_support/core_ext/object/with_options.rb +3 -3
  51. data/lib/active_support/core_ext/object.rb +1 -0
  52. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  53. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  54. data/lib/active_support/core_ext/pathname.rb +1 -0
  55. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  56. data/lib/active_support/core_ext/range/{overlaps.rb → overlap.rb} +5 -3
  57. data/lib/active_support/core_ext/range.rb +1 -2
  58. data/lib/active_support/core_ext/securerandom.rb +24 -12
  59. data/lib/active_support/core_ext/string/filters.rb +20 -14
  60. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  61. data/lib/active_support/core_ext/string/output_safety.rb +38 -174
  62. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  63. data/lib/active_support/core_ext/time/calculations.rb +18 -2
  64. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  65. data/lib/active_support/core_ext/time/zones.rb +4 -4
  66. data/lib/active_support/core_ext/time.rb +0 -1
  67. data/lib/active_support/current_attributes.rb +15 -6
  68. data/lib/active_support/dependencies/autoload.rb +17 -12
  69. data/lib/active_support/deprecation/behaviors.rb +53 -32
  70. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  71. data/lib/active_support/deprecation/deprecators.rb +104 -0
  72. data/lib/active_support/deprecation/disallowed.rb +3 -5
  73. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  74. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  75. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  76. data/lib/active_support/deprecation/reporting.rb +35 -21
  77. data/lib/active_support/deprecation.rb +32 -5
  78. data/lib/active_support/deprecator.rb +7 -0
  79. data/lib/active_support/descendants_tracker.rb +104 -132
  80. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  81. data/lib/active_support/duration.rb +2 -1
  82. data/lib/active_support/encrypted_configuration.rb +30 -9
  83. data/lib/active_support/encrypted_file.rb +8 -3
  84. data/lib/active_support/environment_inquirer.rb +22 -2
  85. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  86. data/lib/active_support/error_reporter.rb +121 -35
  87. data/lib/active_support/execution_wrapper.rb +4 -4
  88. data/lib/active_support/file_update_checker.rb +4 -2
  89. data/lib/active_support/fork_tracker.rb +10 -2
  90. data/lib/active_support/gem_version.rb +4 -4
  91. data/lib/active_support/gzip.rb +2 -0
  92. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  93. data/lib/active_support/i18n.rb +1 -1
  94. data/lib/active_support/i18n_railtie.rb +20 -13
  95. data/lib/active_support/inflector/inflections.rb +2 -0
  96. data/lib/active_support/inflector/methods.rb +22 -10
  97. data/lib/active_support/inflector/transliterate.rb +3 -1
  98. data/lib/active_support/isolated_execution_state.rb +26 -22
  99. data/lib/active_support/json/decoding.rb +2 -1
  100. data/lib/active_support/json/encoding.rb +25 -43
  101. data/lib/active_support/key_generator.rb +9 -1
  102. data/lib/active_support/lazy_load_hooks.rb +6 -4
  103. data/lib/active_support/locale/en.yml +2 -0
  104. data/lib/active_support/log_subscriber.rb +78 -33
  105. data/lib/active_support/logger.rb +1 -1
  106. data/lib/active_support/logger_thread_safe_level.rb +9 -21
  107. data/lib/active_support/message_encryptor.rb +197 -53
  108. data/lib/active_support/message_encryptors.rb +140 -0
  109. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  110. data/lib/active_support/message_pack/extensions.rb +292 -0
  111. data/lib/active_support/message_pack/serializer.rb +63 -0
  112. data/lib/active_support/message_pack.rb +50 -0
  113. data/lib/active_support/message_verifier.rb +212 -93
  114. data/lib/active_support/message_verifiers.rb +134 -0
  115. data/lib/active_support/messages/codec.rb +65 -0
  116. data/lib/active_support/messages/metadata.rb +111 -45
  117. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  118. data/lib/active_support/messages/rotator.rb +34 -32
  119. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  120. data/lib/active_support/multibyte/chars.rb +2 -0
  121. data/lib/active_support/multibyte/unicode.rb +9 -37
  122. data/lib/active_support/notifications/fanout.rb +239 -81
  123. data/lib/active_support/notifications/instrumenter.rb +71 -14
  124. data/lib/active_support/notifications.rb +1 -1
  125. data/lib/active_support/number_helper/number_converter.rb +2 -2
  126. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  127. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  128. data/lib/active_support/ordered_hash.rb +3 -3
  129. data/lib/active_support/ordered_options.rb +14 -0
  130. data/lib/active_support/parameter_filter.rb +84 -69
  131. data/lib/active_support/proxy_object.rb +2 -0
  132. data/lib/active_support/railtie.rb +33 -21
  133. data/lib/active_support/reloader.rb +12 -4
  134. data/lib/active_support/rescuable.rb +2 -0
  135. data/lib/active_support/secure_compare_rotator.rb +16 -9
  136. data/lib/active_support/string_inquirer.rb +3 -1
  137. data/lib/active_support/subscriber.rb +9 -27
  138. data/lib/active_support/syntax_error_proxy.rb +49 -0
  139. data/lib/active_support/tagged_logging.rb +60 -24
  140. data/lib/active_support/test_case.rb +153 -6
  141. data/lib/active_support/testing/assertions.rb +25 -9
  142. data/lib/active_support/testing/autorun.rb +0 -2
  143. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  144. data/lib/active_support/testing/deprecation.rb +25 -25
  145. data/lib/active_support/testing/error_reporter_assertions.rb +108 -0
  146. data/lib/active_support/testing/isolation.rb +1 -1
  147. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  148. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  149. data/lib/active_support/testing/stream.rb +1 -1
  150. data/lib/active_support/testing/strict_warnings.rb +38 -0
  151. data/lib/active_support/testing/time_helpers.rb +32 -14
  152. data/lib/active_support/time_with_zone.rb +6 -42
  153. data/lib/active_support/values/time_zone.rb +9 -7
  154. data/lib/active_support/version.rb +1 -1
  155. data/lib/active_support/xml_mini/jdom.rb +3 -10
  156. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  157. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  158. data/lib/active_support/xml_mini/rexml.rb +1 -1
  159. data/lib/active_support/xml_mini.rb +2 -2
  160. data/lib/active_support.rb +13 -3
  161. metadata +103 -18
  162. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  163. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -37
  164. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -33
  165. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  166. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -33
  167. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  168. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -33
  169. data/lib/active_support/core_ext/uri.rb +0 -5
  170. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -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
- delegate :[], :fetch, to: :config
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 write(contents)
50
- deserialize(contents)
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::InheritableOptions.new
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(config)
83
- doc = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(config) : YAML.load(config)
84
- doc.presence || {}
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:
@@ -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
- return @key_file_contents if defined?(@key_file_contents)
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
- DEFAULT_ENVIRONMENTS = ["development", "test", "production"]
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 "def #{env}?; @#{env}; end"
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 +handle+ method:
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,35 +13,19 @@ 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 +record+
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 handle a specific exception class
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"
43
29
 
44
30
  attr_accessor :logger
45
31
 
@@ -48,31 +34,92 @@ module ActiveSupport
48
34
  @logger = logger
49
35
  end
50
36
 
51
- # Report any unhandled exception, and swallow it.
37
+ # Evaluates the given block, reporting and swallowing any unhandled error.
38
+ # If no error is raised, returns the return value of the block. Otherwise,
39
+ # returns the result of +fallback.call+, or +nil+ if +fallback+ is not
40
+ # specified.
52
41
  #
42
+ # # Will report a TypeError to all subscribers and return nil.
53
43
  # Rails.error.handle do
54
44
  # 1 + '1'
55
45
  # end
56
46
  #
57
- def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil)
47
+ # Can be restricted to handle only specific error classes:
48
+ #
49
+ # maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
50
+ #
51
+ # ==== Options
52
+ #
53
+ # * +:severity+ - This value is passed along to subscribers to indicate how
54
+ # important the error report is. Can be +:error+, +:warning+, or +:info+.
55
+ # Defaults to +:warning+.
56
+ #
57
+ # * +:context+ - Extra information that is passed along to subscribers. For
58
+ # example:
59
+ #
60
+ # Rails.error.handle(context: { section: "admin" }) do
61
+ # # ...
62
+ # end
63
+ #
64
+ # * +:fallback+ - A callable that provides +handle+'s return value when an
65
+ # unhandled error is raised. For example:
66
+ #
67
+ # user = Rails.error.handle(fallback: -> { User.anonymous }) do
68
+ # User.find_by(params)
69
+ # end
70
+ #
71
+ # * +:source+ - This value is passed along to subscribers to indicate the
72
+ # source of the error. Subscribers can use this value to ignore certain
73
+ # errors. Defaults to <tt>"application"</tt>.
74
+ def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
75
+ error_classes = [StandardError] if error_classes.blank?
58
76
  yield
59
- rescue error_class => error
60
- report(error, handled: true, severity: severity, context: context)
77
+ rescue *error_classes => error
78
+ report(error, handled: true, severity: severity, context: context, source: source)
61
79
  fallback.call if fallback
62
80
  end
63
81
 
64
- def record(error_class = StandardError, severity: :error, context: {})
82
+ # Evaluates the given block, reporting and re-raising any unhandled error.
83
+ # If no error is raised, returns the return value of the block.
84
+ #
85
+ # # Will report a TypeError to all subscribers and re-raise it.
86
+ # Rails.error.record do
87
+ # 1 + '1'
88
+ # end
89
+ #
90
+ # Can be restricted to handle only specific error classes:
91
+ #
92
+ # tags = Rails.error.record(Redis::BaseError) { redis.get("tags") }
93
+ #
94
+ # ==== Options
95
+ #
96
+ # * +:severity+ - This value is passed along to subscribers to indicate how
97
+ # important the error report is. Can be +:error+, +:warning+, or +:info+.
98
+ # Defaults to +:error+.
99
+ #
100
+ # * +:context+ - Extra information that is passed along to subscribers. For
101
+ # example:
102
+ #
103
+ # Rails.error.record(context: { section: "admin" }) do
104
+ # # ...
105
+ # end
106
+ #
107
+ # * +:source+ - This value is passed along to subscribers to indicate the
108
+ # source of the error. Subscribers can use this value to ignore certain
109
+ # errors. Defaults to <tt>"application"</tt>.
110
+ def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
111
+ error_classes = [StandardError] if error_classes.blank?
65
112
  yield
66
- rescue error_class => error
67
- report(error, handled: false, severity: severity, context: context)
113
+ rescue *error_classes => error
114
+ report(error, handled: false, severity: severity, context: context, source: source)
68
115
  raise
69
116
  end
70
117
 
71
118
  # Register a new error subscriber. The subscriber must respond to
72
119
  #
73
- # report(Exception, handled: Boolean, context: Hash)
120
+ # report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)
74
121
  #
75
- # The +report+ method +should+ never raise an error.
122
+ # The +report+ method <b>should never</b> raise an error.
76
123
  def subscribe(subscriber)
77
124
  unless subscriber.respond_to?(:report)
78
125
  raise ArgumentError, "Error subscribers must respond to #report"
@@ -80,26 +127,61 @@ module ActiveSupport
80
127
  @subscribers << subscriber
81
128
  end
82
129
 
83
- # Update the execution context that is accessible to error subscribers
130
+ # Unregister an error subscriber. Accepts either a subscriber or a class.
131
+ #
132
+ # subscriber = MyErrorSubscriber.new
133
+ # Rails.error.subscribe(subscriber)
134
+ #
135
+ # Rails.error.unsubscribe(subscriber)
136
+ # # or
137
+ # Rails.error.unsubscribe(MyErrorSubscriber)
138
+ def unsubscribe(subscriber)
139
+ @subscribers.delete_if { |s| subscriber === s }
140
+ end
141
+
142
+ # Prevent a subscriber from being notified of errors for the
143
+ # duration of the block. You may pass in the subscriber itself, or its class.
144
+ #
145
+ # This can be helpful for error reporting service integrations, when they wish
146
+ # to handle any errors higher in the stack.
147
+ def disable(subscriber)
148
+ disabled_subscribers = (ActiveSupport::IsolatedExecutionState[self] ||= [])
149
+ disabled_subscribers << subscriber
150
+ begin
151
+ yield
152
+ ensure
153
+ disabled_subscribers.delete(subscriber)
154
+ end
155
+ end
156
+
157
+ # Update the execution context that is accessible to error subscribers. Any
158
+ # context passed to #handle, #record, or #report will be merged with the
159
+ # context set here.
84
160
  #
85
161
  # Rails.error.set_context(section: "checkout", user_id: @user.id)
86
162
  #
87
- # See +ActiveSupport::ExecutionContext.set+
88
163
  def set_context(...)
89
164
  ActiveSupport::ExecutionContext.set(...)
90
165
  end
91
166
 
92
- # When the block based +handle+ and +record+ methods are not suitable, you can directly use +report+
167
+ # Report an error directly to subscribers. You can use this method when the
168
+ # block-based #handle and #record methods are not suitable.
169
+ #
170
+ # Rails.error.report(error)
93
171
  #
94
- # Rails.error.report(error, handled: true)
95
- def report(error, handled:, severity: handled ? :warning : :error, context: {})
172
+ def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
173
+ return if error.instance_variable_defined?(:@__rails_error_reported)
174
+
96
175
  unless SEVERITIES.include?(severity)
97
176
  raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
98
177
  end
99
178
 
100
179
  full_context = ActiveSupport::ExecutionContext.to_h.merge(context)
180
+ disabled_subscribers = ActiveSupport::IsolatedExecutionState[self]
101
181
  @subscribers.each do |subscriber|
102
- subscriber.report(error, handled: handled, severity: severity, context: full_context)
182
+ unless disabled_subscribers&.any? { |s| s === subscriber }
183
+ subscriber.report(error, handled: handled, severity: severity, context: full_context, source: source)
184
+ end
103
185
  rescue => subscriber_error
104
186
  if logger
105
187
  logger.fatal(
@@ -111,6 +193,10 @@ module ActiveSupport
111
193
  end
112
194
  end
113
195
 
196
+ unless error.frozen?
197
+ error.instance_variable_set(:@__rails_error_reported, true)
198
+ end
199
+
114
200
  nil
115
201
  end
116
202
  end
@@ -84,14 +84,14 @@ module ActiveSupport
84
84
  end
85
85
 
86
86
  # Perform the work in the supplied block as an execution.
87
- def self.wrap
87
+ def self.wrap(source: "application.active_support")
88
88
  return yield if active?
89
89
 
90
90
  instance = run!
91
91
  begin
92
92
  yield
93
93
  rescue => error
94
- error_reporter.report(error, handled: false)
94
+ error_reporter&.report(error, handled: false, source: source)
95
95
  raise
96
96
  ensure
97
97
  instance.complete!
@@ -108,8 +108,8 @@ module ActiveSupport
108
108
  end
109
109
  end
110
110
 
111
- def self.error_reporter
112
- @error_reporter ||= ActiveSupport::ErrorReporter.new
111
+ def self.error_reporter # :nodoc:
112
+ ActiveSupport.error_reporter
113
113
  end
114
114
 
115
115
  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
- # FileUpdateChecker specifies the API used by Rails to watch files
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
@@ -6,7 +6,7 @@ module ActiveSupport
6
6
  def _fork
7
7
  pid = super
8
8
  if pid == 0
9
- ForkTracker.check!
9
+ ForkTracker.after_fork_callback
10
10
  end
11
11
  pid
12
12
  end
@@ -37,7 +37,7 @@ module ActiveSupport
37
37
  @callbacks = []
38
38
 
39
39
  class << self
40
- def check!
40
+ def after_fork_callback
41
41
  new_pid = Process.pid
42
42
  if @pid != new_pid
43
43
  @callbacks.each(&:call)
@@ -45,6 +45,14 @@ module ActiveSupport
45
45
  end
46
46
  end
47
47
 
48
+ if Process.respond_to?(:_fork) # Ruby 3.1+
49
+ def check!
50
+ # We trust the `_fork` callback
51
+ end
52
+ else
53
+ alias_method :check!, :after_fork_callback
54
+ end
55
+
48
56
  def hook!
49
57
  if Process.respond_to?(:_fork) # Ruby 3.1+
50
58
  ::Process.singleton_class.prepend(ModernCoreExt)
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- # Returns the currently loaded version of Active Support as a <tt>Gem::Version</tt>.
4
+ # Returns the currently loaded version of Active Support as a +Gem::Version+.
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 0
12
- TINY = 7
13
- PRE = "2"
11
+ MINOR = 1
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -4,6 +4,8 @@ require "zlib"
4
4
  require "stringio"
5
5
 
6
6
  module ActiveSupport
7
+ # = Active Support \Gzip
8
+ #
7
9
  # A convenient wrapper for the zlib standard library that allows
8
10
  # compression/decompression of strings with gzip.
9
11
  #
@@ -6,6 +6,8 @@ require "active_support/core_ext/hash/except"
6
6
  require "active_support/core_ext/hash/slice"
7
7
 
8
8
  module ActiveSupport
9
+ # = \Hash With Indifferent Access
10
+ #
9
11
  # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
10
12
  # to be the same.
11
13
  #
@@ -37,7 +39,7 @@ module ActiveSupport
37
39
  #
38
40
  # but this class is intended for use cases where strings or symbols are the
39
41
  # expected keys and it is convenient to understand both as the same. For
40
- # example the +params+ hash in Ruby on Rails.
42
+ # example the +params+ hash in Ruby on \Rails.
41
43
  #
42
44
  # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
43
45
  #
@@ -45,7 +47,7 @@ module ActiveSupport
45
47
  #
46
48
  # which may be handy.
47
49
  #
48
- # To access this class outside of Rails, require the core extension with:
50
+ # To access this class outside of \Rails, require the core extension with:
49
51
  #
50
52
  # require "active_support/core_ext/hash/indifferent_access"
51
53
  #
@@ -113,7 +115,7 @@ module ActiveSupport
113
115
  # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 }
114
116
  #
115
117
  # The arguments can be either an
116
- # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
118
+ # +ActiveSupport::HashWithIndifferentAccess+ or a regular +Hash+.
117
119
  # In either case the merge respects the semantics of indifferent access.
118
120
  #
119
121
  # If the argument is a regular hash with keys +:key+ and <tt>"key"</tt> only one
@@ -218,8 +220,12 @@ module ActiveSupport
218
220
  # hash.default # => nil
219
221
  # hash.default('foo') # => 'foo'
220
222
  # hash.default(:foo) # => 'foo'
221
- def default(*args)
222
- super(*args.map { |arg| convert_key(arg) })
223
+ def default(key = (no_key = true))
224
+ if no_key
225
+ super()
226
+ else
227
+ super(convert_key(key))
228
+ end
223
229
  end
224
230
 
225
231
  # Returns an array of the values at the specified indices:
@@ -229,7 +235,8 @@ module ActiveSupport
229
235
  # hash[:b] = 'y'
230
236
  # hash.values_at('a', 'b') # => ["x", "y"]
231
237
  def values_at(*keys)
232
- super(*keys.map { |key| convert_key(key) })
238
+ keys.map! { |key| convert_key(key) }
239
+ super
233
240
  end
234
241
 
235
242
  # Returns an array of the values at the specified indices, but also
@@ -242,7 +249,8 @@ module ActiveSupport
242
249
  # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
243
250
  # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
244
251
  def fetch_values(*indices, &block)
245
- super(*indices.map { |key| convert_key(key) }, &block)
252
+ indices.map! { |key| convert_key(key) }
253
+ super
246
254
  end
247
255
 
248
256
  # Returns a shallow copy of the hash.
@@ -301,7 +309,7 @@ module ActiveSupport
301
309
  # hash.except(:a, "b") # => {c: 10}.with_indifferent_access
302
310
  # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access
303
311
  def except(*keys)
304
- slice(*self.keys - keys.map { |key| convert_key(key) })
312
+ dup.except!(*keys)
305
313
  end
306
314
  alias_method :without, :except
307
315
 
@@ -326,21 +334,31 @@ module ActiveSupport
326
334
  dup.tap { |hash| hash.reject!(*args, &block) }
327
335
  end
328
336
 
329
- def transform_values(*args, &block)
337
+ def transform_values(&block)
330
338
  return to_enum(:transform_values) unless block_given?
331
- dup.tap { |hash| hash.transform_values!(*args, &block) }
339
+ dup.tap { |hash| hash.transform_values!(&block) }
332
340
  end
333
341
 
334
- def transform_keys(*args, &block)
335
- return to_enum(:transform_keys) unless block_given?
336
- dup.tap { |hash| hash.transform_keys!(*args, &block) }
342
+ NOT_GIVEN = Object.new # :nodoc:
343
+
344
+ def transform_keys(hash = NOT_GIVEN, &block)
345
+ return to_enum(:transform_keys) if NOT_GIVEN.equal?(hash) && !block_given?
346
+ dup.tap { |h| h.transform_keys!(hash, &block) }
337
347
  end
338
348
 
339
- def transform_keys!
340
- return enum_for(:transform_keys!) { size } unless block_given?
341
- keys.each do |key|
342
- self[yield(key)] = delete(key)
349
+ def transform_keys!(hash = NOT_GIVEN, &block)
350
+ return to_enum(:transform_keys!) if NOT_GIVEN.equal?(hash) && !block_given?
351
+
352
+ if hash.nil?
353
+ super
354
+ elsif NOT_GIVEN.equal?(hash)
355
+ keys.each { |key| self[yield(key)] = delete(key) }
356
+ elsif block_given?
357
+ keys.each { |key| self[hash[key] || yield(key)] = delete(key) }
358
+ else
359
+ keys.each { |key| self[hash[key] || key] = delete(key) }
343
360
  end
361
+
344
362
  self
345
363
  end
346
364
 
@@ -7,7 +7,7 @@ begin
7
7
  require "i18n"
8
8
  require "i18n/backend/fallbacks"
9
9
  rescue LoadError => e
10
- $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
10
+ warn "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
11
11
  raise e
12
12
  end
13
13
  require "active_support/lazy_load_hooks"