activesupport 8.0.0 → 8.1.0

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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +391 -67
  3. data/README.rdoc +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +72 -1
  5. data/lib/active_support/broadcast_logger.rb +61 -74
  6. data/lib/active_support/cache/file_store.rb +2 -2
  7. data/lib/active_support/cache/mem_cache_store.rb +26 -28
  8. data/lib/active_support/cache/memory_store.rb +5 -5
  9. data/lib/active_support/cache/null_store.rb +2 -2
  10. data/lib/active_support/cache/redis_cache_store.rb +37 -31
  11. data/lib/active_support/cache/strategy/local_cache.rb +72 -27
  12. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  13. data/lib/active_support/cache.rb +72 -9
  14. data/lib/active_support/callbacks.rb +27 -11
  15. data/lib/active_support/class_attribute.rb +15 -8
  16. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
  17. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  18. data/lib/active_support/configurable.rb +34 -0
  19. data/lib/active_support/continuous_integration.rb +145 -0
  20. data/lib/active_support/core_ext/array/conversions.rb +3 -3
  21. data/lib/active_support/core_ext/array.rb +7 -7
  22. data/lib/active_support/core_ext/benchmark.rb +4 -11
  23. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  24. data/lib/active_support/core_ext/class/attribute.rb +20 -5
  25. data/lib/active_support/core_ext/class.rb +2 -2
  26. data/lib/active_support/core_ext/date.rb +5 -5
  27. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
  28. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  29. data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
  30. data/lib/active_support/core_ext/date_time.rb +5 -5
  31. data/lib/active_support/core_ext/digest.rb +1 -1
  32. data/lib/active_support/core_ext/enumerable.rb +17 -5
  33. data/lib/active_support/core_ext/erb/util.rb +5 -5
  34. data/lib/active_support/core_ext/file.rb +1 -1
  35. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
  36. data/lib/active_support/core_ext/hash.rb +8 -8
  37. data/lib/active_support/core_ext/integer.rb +3 -3
  38. data/lib/active_support/core_ext/kernel.rb +3 -3
  39. data/lib/active_support/core_ext/module/introspection.rb +3 -0
  40. data/lib/active_support/core_ext/module.rb +11 -11
  41. data/lib/active_support/core_ext/numeric.rb +3 -3
  42. data/lib/active_support/core_ext/object/json.rb +8 -1
  43. data/lib/active_support/core_ext/object/to_query.rb +7 -1
  44. data/lib/active_support/core_ext/object/try.rb +2 -2
  45. data/lib/active_support/core_ext/object.rb +13 -13
  46. data/lib/active_support/core_ext/pathname.rb +2 -2
  47. data/lib/active_support/core_ext/range/overlap.rb +3 -3
  48. data/lib/active_support/core_ext/range/sole.rb +17 -0
  49. data/lib/active_support/core_ext/range.rb +4 -4
  50. data/lib/active_support/core_ext/securerandom.rb +24 -8
  51. data/lib/active_support/core_ext/string/filters.rb +3 -3
  52. data/lib/active_support/core_ext/string/multibyte.rb +12 -3
  53. data/lib/active_support/core_ext/string/output_safety.rb +19 -12
  54. data/lib/active_support/core_ext/string.rb +13 -13
  55. data/lib/active_support/core_ext/symbol.rb +1 -1
  56. data/lib/active_support/core_ext/time/calculations.rb +0 -7
  57. data/lib/active_support/core_ext/time/compatibility.rb +2 -27
  58. data/lib/active_support/core_ext/time.rb +5 -5
  59. data/lib/active_support/core_ext.rb +1 -1
  60. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  61. data/lib/active_support/current_attributes.rb +27 -17
  62. data/lib/active_support/dependencies/interlock.rb +11 -5
  63. data/lib/active_support/dependencies.rb +6 -1
  64. data/lib/active_support/deprecation/reporting.rb +4 -2
  65. data/lib/active_support/deprecation.rb +1 -1
  66. data/lib/active_support/editor.rb +70 -0
  67. data/lib/active_support/error_reporter.rb +61 -8
  68. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  69. data/lib/active_support/event_reporter.rb +592 -0
  70. data/lib/active_support/evented_file_update_checker.rb +5 -1
  71. data/lib/active_support/execution_context.rb +64 -7
  72. data/lib/active_support/execution_wrapper.rb +1 -1
  73. data/lib/active_support/file_update_checker.rb +8 -6
  74. data/lib/active_support/gem_version.rb +1 -1
  75. data/lib/active_support/gzip.rb +1 -0
  76. data/lib/active_support/hash_with_indifferent_access.rb +47 -24
  77. data/lib/active_support/i18n_railtie.rb +1 -2
  78. data/lib/active_support/inflector/inflections.rb +31 -15
  79. data/lib/active_support/inflector/transliterate.rb +6 -8
  80. data/lib/active_support/isolated_execution_state.rb +12 -15
  81. data/lib/active_support/json/decoding.rb +6 -4
  82. data/lib/active_support/json/encoding.rb +155 -19
  83. data/lib/active_support/lazy_load_hooks.rb +1 -1
  84. data/lib/active_support/log_subscriber.rb +2 -6
  85. data/lib/active_support/logger_thread_safe_level.rb +6 -3
  86. data/lib/active_support/message_encryptors.rb +54 -2
  87. data/lib/active_support/message_pack/extensions.rb +5 -0
  88. data/lib/active_support/message_verifier.rb +9 -0
  89. data/lib/active_support/message_verifiers.rb +57 -3
  90. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  91. data/lib/active_support/messages/rotator.rb +10 -0
  92. data/lib/active_support/multibyte/chars.rb +12 -2
  93. data/lib/active_support/multibyte.rb +4 -0
  94. data/lib/active_support/notifications/fanout.rb +64 -42
  95. data/lib/active_support/notifications/instrumenter.rb +1 -1
  96. data/lib/active_support/railtie.rb +32 -13
  97. data/lib/active_support/structured_event_subscriber.rb +99 -0
  98. data/lib/active_support/subscriber.rb +0 -5
  99. data/lib/active_support/syntax_error_proxy.rb +3 -0
  100. data/lib/active_support/test_case.rb +61 -6
  101. data/lib/active_support/testing/assertions.rb +34 -6
  102. data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
  103. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  104. data/lib/active_support/testing/notification_assertions.rb +92 -0
  105. data/lib/active_support/testing/parallelization/server.rb +15 -2
  106. data/lib/active_support/testing/parallelization/worker.rb +4 -2
  107. data/lib/active_support/testing/parallelization.rb +25 -1
  108. data/lib/active_support/testing/tests_without_assertions.rb +1 -1
  109. data/lib/active_support/testing/time_helpers.rb +7 -3
  110. data/lib/active_support/time_with_zone.rb +23 -23
  111. data/lib/active_support/values/time_zone.rb +8 -1
  112. data/lib/active_support/xml_mini.rb +3 -2
  113. data/lib/active_support.rb +20 -15
  114. metadata +27 -23
  115. data/lib/active_support/core_ext/range/each.rb +0 -24
  116. data/lib/active_support/testing/strict_warnings.rb +0 -43
@@ -149,12 +149,6 @@ module ActiveSupport
149
149
  log_exception(event.name, e)
150
150
  end
151
151
 
152
- def publish_event(event)
153
- super if logger
154
- rescue => e
155
- log_exception(event.name, e)
156
- end
157
-
158
152
  attr_writer :event_levels # :nodoc:
159
153
 
160
154
  private
@@ -184,6 +178,8 @@ module ActiveSupport
184
178
  end
185
179
 
186
180
  def log_exception(name, e)
181
+ ActiveSupport.error_reporter.report(e, source: name)
182
+
187
183
  if logger
188
184
  logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
189
185
  end
@@ -7,6 +7,11 @@ module ActiveSupport
7
7
  module LoggerThreadSafeLevel # :nodoc:
8
8
  extend ActiveSupport::Concern
9
9
 
10
+ def initialize(...)
11
+ super
12
+ @local_level_key = :"logger_thread_safe_level_#{object_id}"
13
+ end
14
+
10
15
  def local_level
11
16
  IsolatedExecutionState[local_level_key]
12
17
  end
@@ -40,8 +45,6 @@ module ActiveSupport
40
45
  end
41
46
 
42
47
  private
43
- def local_level_key
44
- @local_level_key ||= :"logger_thread_safe_level_#{object_id}"
45
- end
48
+ attr_reader :local_level_key
46
49
  end
47
50
  end
@@ -26,10 +26,13 @@ module ActiveSupport
26
26
  # as the first rotation and <tt>transitional = true</tt>. Then, after all
27
27
  # servers have been updated, perform a second rolling deploy with
28
28
  # <tt>transitional = false</tt>.
29
+ #
30
+ #--
31
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#transitional
29
32
 
30
33
  ##
31
- # :method: initialize
32
- # :call-seq: initialize(&secret_generator)
34
+ # :singleton-method: new
35
+ # :call-seq: new(&secret_generator)
33
36
  #
34
37
  # Initializes a new instance. +secret_generator+ must accept a salt and a
35
38
  # +secret_length+ kwarg, and return a suitable secret (string) or secrets
@@ -43,6 +46,9 @@ module ActiveSupport
43
46
  # end
44
47
  #
45
48
  # encryptors.rotate(base: "...")
49
+ #
50
+ #--
51
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#initialize
46
52
 
47
53
  ##
48
54
  # :method: []
@@ -51,12 +57,18 @@ module ActiveSupport
51
57
  # Returns a MessageEncryptor configured with a secret derived from the
52
58
  # given +salt+, and options from #rotate. MessageEncryptor instances will
53
59
  # be memoized, so the same +salt+ will return the same instance.
60
+ #
61
+ #--
62
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#[]
54
63
 
55
64
  ##
56
65
  # :method: []=
57
66
  # :call-seq: []=(salt, encryptor)
58
67
  #
59
68
  # Overrides a MessageEncryptor instance associated with a given +salt+.
69
+ #
70
+ #--
71
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#[]=
60
72
 
61
73
  ##
62
74
  # :method: rotate
@@ -106,18 +118,55 @@ module ActiveSupport
106
118
  #
107
119
  # # Uses `serializer: Marshal, url_safe: false`.
108
120
  # encryptors[:baz]
121
+ #
122
+ #--
123
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#rotate
124
+
125
+ ##
126
+ # :method: prepend
127
+ # :call-seq:
128
+ # prepend(**options)
129
+ # prepend(&block)
130
+ #
131
+ # Just like #rotate, but prepends the given options or block to the list of
132
+ # option sets.
133
+ #
134
+ # This can be useful when you have an already-configured +MessageEncryptors+
135
+ # instance, but you want to override the way messages are encrypted.
136
+ #
137
+ # module ThirdParty
138
+ # ENCRYPTORS = ActiveSupport::MessageEncryptors.new { ... }.
139
+ # rotate(serializer: Marshal, url_safe: true).
140
+ # rotate(serializer: Marshal, url_safe: false)
141
+ # end
142
+ #
143
+ # ThirdParty.ENCRYPTORS.prepend(serializer: JSON, url_safe: true)
144
+ #
145
+ # # Uses `serializer: JSON, url_safe: true`.
146
+ # # Falls back to `serializer: Marshal, url_safe: true` or
147
+ # # `serializer: Marshal, url_safe: false`.
148
+ # ThirdParty.ENCRYPTORS[:foo]
149
+ #
150
+ #--
151
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#prepend
109
152
 
110
153
  ##
111
154
  # :method: rotate_defaults
112
155
  # :call-seq: rotate_defaults
113
156
  #
114
157
  # Invokes #rotate with the default options.
158
+ #
159
+ #--
160
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#rotate_defaults
115
161
 
116
162
  ##
117
163
  # :method: clear_rotations
118
164
  # :call-seq: clear_rotations
119
165
  #
120
166
  # Clears the list of option sets.
167
+ #
168
+ #--
169
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#clear_rotations
121
170
 
122
171
  ##
123
172
  # :method: on_rotation
@@ -129,6 +178,9 @@ module ActiveSupport
129
178
  # For example, this callback could log each time it is called, and thus
130
179
  # indicate whether old option sets are still in use or can be removed from
131
180
  # rotation.
181
+ #
182
+ #--
183
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#on_rotation
132
184
 
133
185
  ##
134
186
  private
@@ -7,6 +7,7 @@ require "pathname"
7
7
  require "uri/generic"
8
8
  require "msgpack/bigint"
9
9
  require "active_support/hash_with_indifferent_access"
10
+ require "active_support/core_ext/string/output_safety"
10
11
  require "active_support/time"
11
12
 
12
13
  module ActiveSupport
@@ -102,6 +103,10 @@ module ActiveSupport
102
103
  packer: method(:write_hash_with_indifferent_access),
103
104
  unpacker: method(:read_hash_with_indifferent_access),
104
105
  recursive: true
106
+
107
+ registry.register_type 18, ActiveSupport::SafeBuffer,
108
+ packer: :to_s,
109
+ unpacker: :new
105
110
  end
106
111
 
107
112
  def install_unregistered_type_error(registry)
@@ -154,6 +154,8 @@ module ActiveSupport
154
154
  # not URL-safe. In other words, they can contain "+" and "/". If you want to
155
155
  # generate URL-safe strings (in compliance with "Base 64 Encoding with URL
156
156
  # and Filename Safe Alphabet" in RFC 4648), you can pass +true+.
157
+ # Note that MessageVerifier will always accept both URL-safe and URL-unsafe
158
+ # encoded messages, to allow a smooth transition between the two settings.
157
159
  #
158
160
  # [+:force_legacy_metadata_serializer+]
159
161
  # Whether to use the legacy metadata serializer, which serializes the
@@ -318,6 +320,13 @@ module ActiveSupport
318
320
  end
319
321
 
320
322
  private
323
+ def decode(encoded, url_safe: @url_safe)
324
+ catch :invalid_message_format do
325
+ return super
326
+ end
327
+ super(encoded, url_safe: !url_safe)
328
+ end
329
+
321
330
  def sign_encoded(encoded)
322
331
  digest = generate_digest(encoded)
323
332
  encoded << SEPARATOR << digest
@@ -26,10 +26,13 @@ module ActiveSupport
26
26
  # as the first rotation and <tt>transitional = true</tt>. Then, after all
27
27
  # servers have been updated, perform a second rolling deploy with
28
28
  # <tt>transitional = false</tt>.
29
+ #
30
+ #--
31
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#transitional
29
32
 
30
33
  ##
31
- # :method: initialize
32
- # :call-seq: initialize(&secret_generator)
34
+ # :singleton-method: new
35
+ # :call-seq: new(&secret_generator)
33
36
  #
34
37
  # Initializes a new instance. +secret_generator+ must accept a salt, and
35
38
  # return a suitable secret (string). +secret_generator+ may also accept
@@ -42,6 +45,9 @@ module ActiveSupport
42
45
  # end
43
46
  #
44
47
  # verifiers.rotate(base: "...")
48
+ #
49
+ #--
50
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#initialize
45
51
 
46
52
  ##
47
53
  # :method: []
@@ -50,16 +56,24 @@ module ActiveSupport
50
56
  # Returns a MessageVerifier configured with a secret derived from the
51
57
  # given +salt+, and options from #rotate. MessageVerifier instances will
52
58
  # be memoized, so the same +salt+ will return the same instance.
59
+ #
60
+ #--
61
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#[]
53
62
 
54
63
  ##
55
64
  # :method: []=
56
65
  # :call-seq: []=(salt, verifier)
57
66
  #
58
67
  # Overrides a MessageVerifier instance associated with a given +salt+.
68
+ #
69
+ #--
70
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#[]=
59
71
 
60
72
  ##
61
73
  # :method: rotate
62
- # :call-seq: rotate(**options)
74
+ # :call-seq:
75
+ # rotate(**options)
76
+ # rotate(&block)
63
77
  #
64
78
  # Adds +options+ to the list of option sets. Messages will be signed using
65
79
  # the first set in the list. When verifying, however, each set will be
@@ -102,18 +116,55 @@ module ActiveSupport
102
116
  #
103
117
  # # Uses `serializer: Marshal, url_safe: false`.
104
118
  # verifiers[:baz]
119
+ #
120
+ #--
121
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#rotate
122
+
123
+ ##
124
+ # :method: prepend
125
+ # :call-seq:
126
+ # prepend(**options)
127
+ # prepend(&block)
128
+ #
129
+ # Just like #rotate, but prepends the given options or block to the list of
130
+ # option sets.
131
+ #
132
+ # This can be useful when you have an already-configured +MessageVerifiers+
133
+ # instance, but you want to override the way messages are signed.
134
+ #
135
+ # module ThirdParty
136
+ # VERIFIERS = ActiveSupport::MessageVerifiers.new { ... }.
137
+ # rotate(serializer: Marshal, url_safe: true).
138
+ # rotate(serializer: Marshal, url_safe: false)
139
+ # end
140
+ #
141
+ # ThirdParty.VERIFIERS.prepend(serializer: JSON, url_safe: true)
142
+ #
143
+ # # Uses `serializer: JSON, url_safe: true`.
144
+ # # Falls back to `serializer: Marshal, url_safe: true` or
145
+ # # `serializer: Marshal, url_safe: false`.
146
+ # ThirdParty.VERIFIERS[:foo]
147
+ #
148
+ #--
149
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#prepend
105
150
 
106
151
  ##
107
152
  # :method: rotate_defaults
108
153
  # :call-seq: rotate_defaults
109
154
  #
110
155
  # Invokes #rotate with the default options.
156
+ #
157
+ #--
158
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#rotate_defaults
111
159
 
112
160
  ##
113
161
  # :method: clear_rotations
114
162
  # :call-seq: clear_rotations
115
163
  #
116
164
  # Clears the list of option sets.
165
+ #
166
+ #--
167
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#clear_rotations
117
168
 
118
169
  ##
119
170
  # :method: on_rotation
@@ -125,6 +176,9 @@ module ActiveSupport
125
176
  # For example, this callback could log each time it is called, and thus
126
177
  # indicate whether old option sets are still in use or can be removed from
127
178
  # rotation.
179
+ #
180
+ #--
181
+ # Implemented by ActiveSupport::Messages::RotationCoordinator#on_rotation
128
182
 
129
183
  ##
130
184
  private
@@ -32,6 +32,15 @@ module ActiveSupport
32
32
  self
33
33
  end
34
34
 
35
+ def prepend(**options, &block)
36
+ raise ArgumentError, "Options cannot be specified when using a block" if block && !options.empty?
37
+ changing_configuration!
38
+
39
+ @rotate_options.unshift(block || options)
40
+
41
+ self
42
+ end
43
+
35
44
  def rotate_defaults
36
45
  rotate()
37
46
  end
@@ -15,6 +15,11 @@ module ActiveSupport
15
15
  fall_back_to build_rotation(*args, **options)
16
16
  end
17
17
 
18
+ def on_rotation(&on_rotation)
19
+ @on_rotation = on_rotation
20
+ self
21
+ end
22
+
18
23
  def fall_back_to(fallback)
19
24
  @rotations << fallback
20
25
  self
@@ -40,6 +45,11 @@ module ActiveSupport
40
45
  end
41
46
  end
42
47
 
48
+ def initialize_dup(*)
49
+ super
50
+ @rotations = @rotations.dup
51
+ end
52
+
43
53
  private
44
54
  def build_rotation(*args, **options)
45
55
  self.class.new(*args, *@args.drop(args.length), **@options, **options)
@@ -53,9 +53,19 @@ module ActiveSupport # :nodoc:
53
53
  delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string
54
54
 
55
55
  # Creates a new Chars instance by wrapping _string_.
56
- def initialize(string)
56
+ def initialize(string, deprecation: true)
57
+ if deprecation
58
+ ActiveSupport.deprecator.warn(
59
+ "ActiveSupport::Multibyte::Chars is deprecated and will be removed in Rails 8.2. " \
60
+ "Use normal string methods instead."
61
+ )
62
+ end
63
+
57
64
  @wrapped_string = string
58
- @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
65
+ if string.encoding != Encoding::UTF_8
66
+ @wrapped_string = @wrapped_string.dup
67
+ @wrapped_string.force_encoding(Encoding::UTF_8)
68
+ end
59
69
  end
60
70
 
61
71
  # Forward all undefined methods to the wrapped string.
@@ -12,6 +12,10 @@ module ActiveSupport # :nodoc:
12
12
  #
13
13
  # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
14
14
  def self.proxy_class=(klass)
15
+ ActiveSupport.deprecator.warn(
16
+ "ActiveSupport::Multibyte.proxy_class= is deprecated and will be removed in Rails 8.2. " \
17
+ "Use normal string methods instead."
18
+ )
15
19
  @proxy_class = klass
16
20
  end
17
21
 
@@ -17,24 +17,30 @@ module ActiveSupport
17
17
 
18
18
  module FanoutIteration # :nodoc:
19
19
  private
20
- def iterate_guarding_exceptions(collection)
21
- exceptions = nil
22
-
23
- collection.each do |s|
24
- yield s
25
- rescue Exception => e
26
- exceptions ||= []
27
- exceptions << e
28
- end
20
+ def iterate_guarding_exceptions(collection, &block)
21
+ case collection.size
22
+ when 0
23
+ when 1
24
+ collection.each(&block)
25
+ else
26
+ exceptions = nil
29
27
 
30
- if exceptions
31
- exceptions = exceptions.flat_map do |exception|
32
- exception.is_a?(InstrumentationSubscriberError) ? exception.exceptions : [exception]
28
+ collection.each do |s|
29
+ yield s
30
+ rescue Exception => e
31
+ exceptions ||= []
32
+ exceptions << e
33
33
  end
34
- if exceptions.size == 1
35
- raise exceptions.first
36
- else
37
- raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
34
+
35
+ if exceptions
36
+ exceptions = exceptions.flat_map do |exception|
37
+ exception.is_a?(InstrumentationSubscriberError) ? exception.exceptions : [exception]
38
+ end
39
+ if exceptions.size == 1
40
+ raise exceptions.first
41
+ else
42
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
43
+ end
38
44
  end
39
45
  end
40
46
 
@@ -53,7 +59,6 @@ module ActiveSupport
53
59
  @other_subscribers = []
54
60
  @all_listeners_for = Concurrent::Map.new
55
61
  @groups_for = Concurrent::Map.new
56
- @silenceable_groups_for = Concurrent::Map.new
57
62
  end
58
63
 
59
64
  def inspect # :nodoc:
@@ -102,11 +107,9 @@ module ActiveSupport
102
107
  if key
103
108
  @all_listeners_for.delete(key)
104
109
  @groups_for.delete(key)
105
- @silenceable_groups_for.delete(key)
106
110
  else
107
111
  @all_listeners_for.clear
108
112
  @groups_for.clear
109
- @silenceable_groups_for.clear
110
113
  end
111
114
  end
112
115
 
@@ -184,25 +187,25 @@ module ActiveSupport
184
187
  end
185
188
  end
186
189
 
187
- def groups_for(name) # :nodoc:
188
- groups = @groups_for.compute_if_absent(name) do
189
- all_listeners_for(name).reject(&:silenceable).group_by(&:group_class).transform_values do |s|
190
- s.map(&:delegate)
191
- end
192
- end
190
+ def group_listeners(listeners) # :nodoc:
191
+ listeners.group_by(&:group_class).transform_values do |s|
192
+ s.map(&:delegate).freeze
193
+ end.freeze
194
+ end
193
195
 
194
- silenceable_groups = @silenceable_groups_for.compute_if_absent(name) do
195
- all_listeners_for(name).select(&:silenceable).group_by(&:group_class).transform_values do |s|
196
- s.map(&:delegate)
197
- end
196
+ def groups_for(name) # :nodoc:
197
+ silenceable_groups, groups = @groups_for.compute_if_absent(name) do
198
+ listeners = all_listeners_for(name)
199
+ listeners.partition(&:silenceable).map { |l| group_listeners(l) }
198
200
  end
199
201
 
200
202
  unless silenceable_groups.empty?
201
- groups = groups.dup
202
203
  silenceable_groups.each do |group_class, subscriptions|
203
204
  active_subscriptions = subscriptions.reject { |s| s.silenced?(name) }
204
205
  unless active_subscriptions.empty?
205
- groups[group_class] = (groups[group_class] || []) + active_subscriptions
206
+ groups = groups.dup if groups.frozen?
207
+ base_groups = groups[group_class]
208
+ groups[group_class] = base_groups ? base_groups + active_subscriptions : active_subscriptions
206
209
  end
207
210
  end
208
211
  end
@@ -227,13 +230,11 @@ module ActiveSupport
227
230
  class Handle
228
231
  include FanoutIteration
229
232
 
230
- def initialize(notifier, name, id, payload) # :nodoc:
233
+ def initialize(notifier, name, id, groups, payload) # :nodoc:
231
234
  @name = name
232
235
  @id = id
233
236
  @payload = payload
234
- @groups = notifier.groups_for(name).map do |group_klass, grouped_listeners|
235
- group_klass.new(grouped_listeners, name, id, payload)
236
- end
237
+ @groups = groups
237
238
  @state = :initialized
238
239
  end
239
240
 
@@ -267,10 +268,31 @@ module ActiveSupport
267
268
  end
268
269
  end
269
270
 
271
+ module NullHandle # :nodoc:
272
+ extend self
273
+
274
+ def start
275
+ end
276
+
277
+ def finish
278
+ end
279
+
280
+ def finish_with_values(_name, _id, _payload)
281
+ end
282
+ end
283
+
270
284
  include FanoutIteration
271
285
 
272
286
  def build_handle(name, id, payload)
273
- Handle.new(self, name, id, payload)
287
+ groups = groups_for(name).map do |group_klass, grouped_listeners|
288
+ group_klass.new(grouped_listeners, name, id, payload)
289
+ end
290
+
291
+ if groups.empty?
292
+ NullHandle
293
+ else
294
+ Handle.new(self, name, id, groups, payload)
295
+ end
274
296
  end
275
297
 
276
298
  def start(name, id, payload)
@@ -286,8 +308,8 @@ module ActiveSupport
286
308
  handle.finish_with_values(name, id, payload)
287
309
  end
288
310
 
289
- def publish(name, *args)
290
- iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
311
+ def publish(name, ...)
312
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, ...) }
291
313
  end
292
314
 
293
315
  def publish_event(event)
@@ -387,9 +409,9 @@ module ActiveSupport
387
409
  EventedGroup
388
410
  end
389
411
 
390
- def publish(name, *args)
412
+ def publish(...)
391
413
  if @can_publish
392
- @delegate.publish name, *args
414
+ @delegate.publish(...)
393
415
  end
394
416
  end
395
417
 
@@ -419,8 +441,8 @@ module ActiveSupport
419
441
  TimedGroup
420
442
  end
421
443
 
422
- def publish(name, *args)
423
- @delegate.call name, *args
444
+ def publish(...)
445
+ @delegate.call(...)
424
446
  end
425
447
  end
426
448
 
@@ -164,7 +164,7 @@ module ActiveSupport
164
164
  @cpu_time_finish - @cpu_time_start
165
165
  end
166
166
 
167
- # Returns the idle time time (in milliseconds) passed between the call to
167
+ # Returns the idle time (in milliseconds) passed between the call to
168
168
  # #start! and the call to #finish!.
169
169
  def idle_time
170
170
  diff = duration - cpu_time
@@ -15,7 +15,7 @@ module ActiveSupport
15
15
 
16
16
  initializer "active_support.isolation_level" do |app|
17
17
  config.after_initialize do
18
- if level = app.config.active_support.delete(:isolation_level)
18
+ if level = app.config.active_support.isolation_level
19
19
  ActiveSupport::IsolatedExecutionState.isolation_level = level
20
20
  end
21
21
  end
@@ -38,19 +38,35 @@ module ActiveSupport
38
38
  end
39
39
  end
40
40
 
41
- initializer "active_support.reset_execution_context" do |app|
42
- app.reloader.before_class_unload { ActiveSupport::ExecutionContext.clear }
43
- app.executor.to_run { ActiveSupport::ExecutionContext.clear }
44
- app.executor.to_complete { ActiveSupport::ExecutionContext.clear }
41
+ initializer "active_support.set_event_reporter_context_store" do |app|
42
+ config.after_initialize do
43
+ if klass = app.config.active_support.event_reporter_context_store
44
+ ActiveSupport::EventReporter.context_store = klass
45
+ end
46
+ end
45
47
  end
46
48
 
47
- initializer "active_support.reset_all_current_attributes_instances" do |app|
48
- app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
49
- app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
50
- app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
49
+ initializer "active_support.reset_execution_context" do |app|
50
+ app.reloader.before_class_unload do
51
+ ActiveSupport::CurrentAttributes.clear_all
52
+ ActiveSupport::ExecutionContext.clear
53
+ ActiveSupport.event_reporter.clear_context
54
+ end
55
+
56
+ app.executor.to_run do
57
+ ActiveSupport::ExecutionContext.push
58
+ end
59
+
60
+ app.executor.to_complete do
61
+ ActiveSupport::CurrentAttributes.clear_all
62
+ ActiveSupport::ExecutionContext.pop
63
+ ActiveSupport.event_reporter.clear_context
64
+ end
51
65
 
52
66
  ActiveSupport.on_load(:active_support_test_case) do
53
67
  if app.config.active_support.executor_around_test_case
68
+ ActiveSupport::ExecutionContext.nestable = true
69
+
54
70
  require "active_support/executor/test_helper"
55
71
  include ActiveSupport::Executor::TestHelper
56
72
  else
@@ -63,6 +79,13 @@ module ActiveSupport
63
79
  end
64
80
  end
65
81
 
82
+ initializer "active_support.set_filter_parameters" do |app|
83
+ config.after_initialize do
84
+ ActiveSupport.filter_parameters += Rails.application.config.filter_parameters
85
+ ActiveSupport.event_reporter.reload_payload_filter
86
+ end
87
+ end
88
+
66
89
  initializer "active_support.deprecation_behavior" do |app|
67
90
  if app.config.active_support.report_deprecations == false
68
91
  app.deprecators.silenced = true
@@ -96,10 +119,6 @@ module ActiveSupport
96
119
  config.eager_load_namespaces << TZInfo
97
120
  end
98
121
 
99
- initializer "active_support.to_time_preserves_timezone" do |app|
100
- ActiveSupport.to_time_preserves_timezone = app.config.active_support.to_time_preserves_timezone
101
- end
102
-
103
122
  # Sets the default week start
104
123
  # If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
105
124
  initializer "active_support.initialize_beginning_of_week" do |app|