activesupport 8.1.0.beta1 → 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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -1
  3. data/lib/active_support/callbacks.rb +20 -8
  4. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
  5. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  6. data/lib/active_support/configurable.rb +6 -0
  7. data/lib/active_support/core_ext/array.rb +7 -7
  8. data/lib/active_support/core_ext/benchmark.rb +4 -11
  9. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  10. data/lib/active_support/core_ext/class/attribute.rb +8 -6
  11. data/lib/active_support/core_ext/class.rb +2 -2
  12. data/lib/active_support/core_ext/date.rb +5 -5
  13. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
  14. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  15. data/lib/active_support/core_ext/date_time.rb +5 -5
  16. data/lib/active_support/core_ext/digest.rb +1 -1
  17. data/lib/active_support/core_ext/enumerable.rb +2 -2
  18. data/lib/active_support/core_ext/file.rb +1 -1
  19. data/lib/active_support/core_ext/hash.rb +8 -8
  20. data/lib/active_support/core_ext/integer.rb +3 -3
  21. data/lib/active_support/core_ext/kernel.rb +3 -3
  22. data/lib/active_support/core_ext/module.rb +11 -11
  23. data/lib/active_support/core_ext/numeric.rb +3 -3
  24. data/lib/active_support/core_ext/object.rb +13 -13
  25. data/lib/active_support/core_ext/pathname.rb +2 -2
  26. data/lib/active_support/core_ext/range.rb +4 -4
  27. data/lib/active_support/core_ext/string.rb +13 -13
  28. data/lib/active_support/core_ext/symbol.rb +1 -1
  29. data/lib/active_support/core_ext/time/calculations.rb +0 -7
  30. data/lib/active_support/core_ext/time/compatibility.rb +2 -27
  31. data/lib/active_support/core_ext/time.rb +5 -5
  32. data/lib/active_support/core_ext.rb +1 -1
  33. data/lib/active_support/dependencies/interlock.rb +11 -5
  34. data/lib/active_support/dependencies.rb +6 -1
  35. data/lib/active_support/event_reporter.rb +24 -2
  36. data/lib/active_support/file_update_checker.rb +1 -1
  37. data/lib/active_support/gem_version.rb +1 -1
  38. data/lib/active_support/isolated_execution_state.rb +5 -2
  39. data/lib/active_support/json/encoding.rb +48 -19
  40. data/lib/active_support/log_subscriber.rb +0 -6
  41. data/lib/active_support/notifications/fanout.rb +64 -42
  42. data/lib/active_support/notifications/instrumenter.rb +1 -1
  43. data/lib/active_support/railtie.rb +7 -4
  44. data/lib/active_support/structured_event_subscriber.rb +99 -0
  45. data/lib/active_support/subscriber.rb +0 -5
  46. data/lib/active_support/testing/event_reporter_assertions.rb +11 -1
  47. data/lib/active_support/testing/parallelization/server.rb +15 -2
  48. data/lib/active_support/testing/parallelization/worker.rb +2 -2
  49. data/lib/active_support/testing/parallelization.rb +12 -1
  50. data/lib/active_support/time_with_zone.rb +3 -17
  51. data/lib/active_support/xml_mini.rb +2 -0
  52. data/lib/active_support.rb +13 -19
  53. metadata +18 -16
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/subscriber"
4
+
5
+ module ActiveSupport
6
+ # = Active Support Structured Event \Subscriber
7
+ #
8
+ # +ActiveSupport::StructuredEventSubscriber+ consumes ActiveSupport::Notifications
9
+ # in order to emit structured events via +Rails.event+.
10
+ #
11
+ # An example would be the Action Controller structured event subscriber, responsible for
12
+ # emitting request processing events:
13
+ #
14
+ # module ActionController
15
+ # class StructuredEventSubscriber < ActiveSupport::StructuredEventSubscriber
16
+ # attach_to :action_controller
17
+ #
18
+ # def start_processing(event)
19
+ # emit_event("controller.request_started",
20
+ # controller: event.payload[:controller],
21
+ # action: event.payload[:action],
22
+ # format: event.payload[:format]
23
+ # )
24
+ # end
25
+ # end
26
+ # end
27
+ #
28
+ # After configured, whenever a <tt>"start_processing.action_controller"</tt> notification is published,
29
+ # it will properly dispatch the event (+ActiveSupport::Notifications::Event+) to the +start_processing+ method.
30
+ # The subscriber can then emit a structured event via the +emit_event+ method.
31
+ class StructuredEventSubscriber < Subscriber
32
+ class_attribute :debug_methods, instance_accessor: false, default: [] # :nodoc:
33
+
34
+ DEBUG_CHECK = proc { !ActiveSupport.event_reporter.debug_mode? }
35
+
36
+ class << self
37
+ def attach_to(...) # :nodoc:
38
+ result = super
39
+ set_silenced_events
40
+ result
41
+ end
42
+
43
+ private
44
+ def set_silenced_events
45
+ if subscriber
46
+ subscriber.silenced_events = debug_methods.to_h { |method| ["#{method}.#{namespace}", DEBUG_CHECK] }
47
+ end
48
+ end
49
+
50
+ def debug_only(method)
51
+ self.debug_methods << method
52
+ set_silenced_events
53
+ end
54
+ end
55
+
56
+ def initialize
57
+ super
58
+ @silenced_events = {}
59
+ end
60
+
61
+ def silenced?(event)
62
+ ActiveSupport.event_reporter.subscribers.none? || @silenced_events[event]&.call
63
+ end
64
+
65
+ attr_writer :silenced_events # :nodoc:
66
+
67
+ # Emit a structured event via Rails.event.notify.
68
+ #
69
+ # ==== Arguments
70
+ #
71
+ # * +name+ - The event name as a string or symbol
72
+ # * +payload+ - The event payload as a hash or object
73
+ # * +caller_depth+ - Stack depth for source location (default: 1)
74
+ # * +kwargs+ - Additional payload data merged with the payload hash
75
+ def emit_event(name, payload = nil, caller_depth: 1, **kwargs)
76
+ ActiveSupport.event_reporter.notify(name, payload, caller_depth: caller_depth + 1, **kwargs)
77
+ rescue => e
78
+ handle_event_error(name, e)
79
+ end
80
+
81
+ # Like +emit_event+, but only emits when the event reporter is in debug mode
82
+ def emit_debug_event(name, payload = nil, caller_depth: 1, **kwargs)
83
+ ActiveSupport.event_reporter.debug(name, payload, caller_depth: caller_depth + 1, **kwargs)
84
+ rescue => e
85
+ handle_event_error(name, e)
86
+ end
87
+
88
+ def call(event)
89
+ super
90
+ rescue => e
91
+ handle_event_error(event.name, e)
92
+ end
93
+
94
+ private
95
+ def handle_event_error(name, error)
96
+ ActiveSupport.error_reporter.report(error, source: name)
97
+ end
98
+ end
99
+ end
@@ -137,10 +137,5 @@ module ActiveSupport
137
137
  method = event.name[0, event.name.index(".")]
138
138
  send(method, event)
139
139
  end
140
-
141
- def publish_event(event) # :nodoc:
142
- method = event.name[0, event.name.index(".")]
143
- send(method, event)
144
- end
145
140
  end
146
141
  end
@@ -149,7 +149,7 @@ module ActiveSupport
149
149
  event.event_data
150
150
  else
151
151
  message = "Expected an event to be reported matching:\n " \
152
- "name: #{name}\n " \
152
+ "name: #{name.inspect}\n " \
153
153
  "payload: #{payload.inspect}\n " \
154
154
  "tags: #{tags.inspect}\n" \
155
155
  "but none of the #{events.size} reported events matched:\n " \
@@ -212,6 +212,16 @@ module ActiveSupport
212
212
 
213
213
  assert(true)
214
214
  end
215
+
216
+ # Allows debug events to be reported to +Rails.event+ for the duration of a given block.
217
+ #
218
+ # with_debug_event_reporting do
219
+ # service_that_reports_debug_events.perform
220
+ # end
221
+ #
222
+ def with_debug_event_reporting(&block)
223
+ ActiveSupport.event_reporter.with_debug(&block)
224
+ end
215
225
  end
216
226
  end
217
227
  end
@@ -14,6 +14,7 @@ module ActiveSupport
14
14
  def initialize
15
15
  @queue = Queue.new
16
16
  @active_workers = Concurrent::Map.new
17
+ @worker_pids = Concurrent::Map.new
17
18
  @in_flight = Concurrent::Map.new
18
19
  end
19
20
 
@@ -40,12 +41,24 @@ module ActiveSupport
40
41
  end
41
42
  end
42
43
 
43
- def start_worker(worker_id)
44
+ def start_worker(worker_id, worker_pid)
44
45
  @active_workers[worker_id] = true
46
+ @worker_pids[worker_id] = worker_pid
45
47
  end
46
48
 
47
- def stop_worker(worker_id)
49
+ def stop_worker(worker_id, worker_pid)
48
50
  @active_workers.delete(worker_id)
51
+ @worker_pids.delete(worker_id)
52
+ end
53
+
54
+ def remove_dead_workers(dead_pids)
55
+ dead_pids.each do |dead_pid|
56
+ worker_id = @worker_pids.key(dead_pid)
57
+ if worker_id
58
+ @active_workers.delete(worker_id)
59
+ @worker_pids.delete(worker_id)
60
+ end
61
+ end
49
62
  end
50
63
 
51
64
  def active_workers?
@@ -18,7 +18,7 @@ module ActiveSupport
18
18
  DRb.stop_service
19
19
 
20
20
  @queue = DRbObject.new_with_uri(@url)
21
- @queue.start_worker(@id)
21
+ @queue.start_worker(@id, Process.pid)
22
22
 
23
23
  begin
24
24
  after_fork
@@ -29,7 +29,7 @@ module ActiveSupport
29
29
  set_process_title("(stopping)")
30
30
 
31
31
  run_cleanup
32
- @queue.stop_worker(@id)
32
+ @queue.stop_worker(@id, Process.pid)
33
33
  end
34
34
  end
35
35
 
@@ -60,8 +60,19 @@ module ActiveSupport
60
60
  end
61
61
 
62
62
  def shutdown
63
+ dead_worker_pids = @worker_pool.filter_map do |pid|
64
+ Process.waitpid(pid, Process::WNOHANG)
65
+ rescue Errno::ECHILD
66
+ pid
67
+ end
68
+ @queue_server.remove_dead_workers(dead_worker_pids)
69
+
63
70
  @queue_server.shutdown
64
- @worker_pool.each { |pid| Process.waitpid pid }
71
+ @worker_pool.each do |pid|
72
+ Process.waitpid(pid)
73
+ rescue Errno::ECHILD
74
+ nil
75
+ end
65
76
  end
66
77
  end
67
78
  end
@@ -311,16 +311,8 @@ module ActiveSupport
311
311
  if duration_of_variable_length?(other)
312
312
  method_missing(:+, other)
313
313
  else
314
- begin
315
- result = utc + other
316
- rescue TypeError
317
- result = utc.to_datetime.since(other)
318
- ActiveSupport.deprecator.warn(
319
- "Adding an instance of #{other.class} to an instance of #{self.class} is deprecated. This behavior will raise " \
320
- "a `TypeError` in Rails 8.1."
321
- )
322
- result.in_time_zone(time_zone)
323
- end
314
+ result = utc + other
315
+
324
316
  result.in_time_zone(time_zone)
325
317
  end
326
318
  end
@@ -503,13 +495,7 @@ module ActiveSupport
503
495
  # with the same UTC offset as +self+ or in the local system timezone
504
496
  # depending on the setting of +ActiveSupport.to_time_preserves_timezone+.
505
497
  def to_time
506
- if preserve_timezone == :zone
507
- @to_time_with_timezone ||= getlocal(time_zone)
508
- elsif preserve_timezone
509
- @to_time_with_instance_offset ||= getlocal(utc_offset)
510
- else
511
- @to_time_with_system_offset ||= getlocal
512
- end
498
+ @to_time_with_timezone ||= getlocal(time_zone)
513
499
  end
514
500
 
515
501
  # So that +self+ <tt>acts_like?(:time)</tt>.
@@ -73,6 +73,8 @@ module ActiveSupport
73
73
  "decimal" => Proc.new do |number|
74
74
  if String === number
75
75
  number.to_d
76
+ elsif Float === number
77
+ BigDecimal(number, 0)
76
78
  else
77
79
  BigDecimal(number)
78
80
  end
@@ -39,6 +39,7 @@ module ActiveSupport
39
39
  autoload :Concern
40
40
  autoload :CodeGenerator
41
41
  autoload :ActionableError
42
+ autoload :Configurable
42
43
  autoload :ConfigurationFile
43
44
  autoload :ContinuousIntegration
44
45
  autoload :CurrentAttributes
@@ -53,6 +54,7 @@ module ActiveSupport
53
54
  autoload :EventedFileUpdateChecker
54
55
  autoload :ForkTracker
55
56
  autoload :LogSubscriber
57
+ autoload :StructuredEventSubscriber
56
58
  autoload :IsolatedExecutionState
57
59
  autoload :Notifications
58
60
  autoload :Reloader
@@ -64,7 +66,6 @@ module ActiveSupport
64
66
  autoload :Benchmarkable
65
67
  autoload :Cache
66
68
  autoload :Callbacks
67
- autoload :Configurable
68
69
  autoload :ClassAttribute
69
70
  autoload :Deprecation
70
71
  autoload :Delegation
@@ -94,10 +95,6 @@ module ActiveSupport
94
95
  autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
95
96
  autoload :TestCase
96
97
 
97
- include Deprecation::DeprecatedConstantAccessor
98
-
99
- deprecate_constant :Configurable, "class_attribute :config, default: {}", deprecator: ActiveSupport.deprecator
100
-
101
98
  def self.eager_load!
102
99
  super
103
100
 
@@ -114,6 +111,8 @@ module ActiveSupport
114
111
  @event_reporter = ActiveSupport::EventReporter.new
115
112
  singleton_class.attr_accessor :event_reporter # :nodoc:
116
113
 
114
+ cattr_accessor :filter_parameters, default: [] # :nodoc:
115
+
117
116
  def self.cache_format_version
118
117
  Cache.format_version
119
118
  end
@@ -123,23 +122,18 @@ module ActiveSupport
123
122
  end
124
123
 
125
124
  def self.to_time_preserves_timezone
126
- DateAndTime::Compatibility.preserve_timezone
125
+ ActiveSupport.deprecator.warn(
126
+ "`config.active_support.to_time_preserves_timezone` is deprecated and will be removed in Rails 8.2"
127
+ )
128
+ @to_time_preserves_timezone
127
129
  end
128
130
 
129
131
  def self.to_time_preserves_timezone=(value)
130
- if !value
131
- ActiveSupport.deprecator.warn(
132
- "`to_time` will always preserve the receiver timezone rather than system local time in Rails 8.1. " \
133
- "To opt in to the new behavior, set `config.active_support.to_time_preserves_timezone = :zone`."
134
- )
135
- elsif value != :zone
136
- ActiveSupport.deprecator.warn(
137
- "`to_time` will always preserve the full timezone rather than offset of the receiver in Rails 8.1. " \
138
- "To opt in to the new behavior, set `config.active_support.to_time_preserves_timezone = :zone`."
139
- )
140
- end
141
-
142
- DateAndTime::Compatibility.preserve_timezone = value
132
+ ActiveSupport.deprecator.warn(
133
+ "`config.active_support.to_time_preserves_timezone` is deprecated and will be removed in Rails 8.2"
134
+ )
135
+
136
+ @to_time_preserves_timezone = value
143
137
  end
144
138
 
145
139
  def self.utc_to_local_returns_utc_offset_times
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesupport
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.0.beta1
4
+ version: 8.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -140,61 +140,61 @@ dependencies:
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  - !ruby/object:Gem::Dependency
143
- name: logger
143
+ name: json
144
144
  requirement: !ruby/object:Gem::Requirement
145
145
  requirements:
146
146
  - - ">="
147
147
  - !ruby/object:Gem::Version
148
- version: 1.4.2
148
+ version: '0'
149
149
  type: :runtime
150
150
  prerelease: false
151
151
  version_requirements: !ruby/object:Gem::Requirement
152
152
  requirements:
153
153
  - - ">="
154
154
  - !ruby/object:Gem::Version
155
- version: 1.4.2
155
+ version: '0'
156
156
  - !ruby/object:Gem::Dependency
157
- name: securerandom
157
+ name: logger
158
158
  requirement: !ruby/object:Gem::Requirement
159
159
  requirements:
160
160
  - - ">="
161
161
  - !ruby/object:Gem::Version
162
- version: '0.3'
162
+ version: 1.4.2
163
163
  type: :runtime
164
164
  prerelease: false
165
165
  version_requirements: !ruby/object:Gem::Requirement
166
166
  requirements:
167
167
  - - ">="
168
168
  - !ruby/object:Gem::Version
169
- version: '0.3'
169
+ version: 1.4.2
170
170
  - !ruby/object:Gem::Dependency
171
- name: uri
171
+ name: securerandom
172
172
  requirement: !ruby/object:Gem::Requirement
173
173
  requirements:
174
174
  - - ">="
175
175
  - !ruby/object:Gem::Version
176
- version: 0.13.1
176
+ version: '0.3'
177
177
  type: :runtime
178
178
  prerelease: false
179
179
  version_requirements: !ruby/object:Gem::Requirement
180
180
  requirements:
181
181
  - - ">="
182
182
  - !ruby/object:Gem::Version
183
- version: 0.13.1
183
+ version: '0.3'
184
184
  - !ruby/object:Gem::Dependency
185
- name: benchmark
185
+ name: uri
186
186
  requirement: !ruby/object:Gem::Requirement
187
187
  requirements:
188
188
  - - ">="
189
189
  - !ruby/object:Gem::Version
190
- version: '0.3'
190
+ version: 0.13.1
191
191
  type: :runtime
192
192
  prerelease: false
193
193
  version_requirements: !ruby/object:Gem::Requirement
194
194
  requirements:
195
195
  - - ">="
196
196
  - !ruby/object:Gem::Version
197
- version: '0.3'
197
+ version: 0.13.1
198
198
  description: A toolkit of support libraries and Ruby core extensions extracted from
199
199
  the Rails framework. Rich support for multibyte strings, internationalization, time
200
200
  zones, and testing.
@@ -233,6 +233,7 @@ files:
233
233
  - lib/active_support/concurrency/load_interlock_aware_monitor.rb
234
234
  - lib/active_support/concurrency/null_lock.rb
235
235
  - lib/active_support/concurrency/share_lock.rb
236
+ - lib/active_support/concurrency/thread_monitor.rb
236
237
  - lib/active_support/configurable.rb
237
238
  - lib/active_support/configuration_file.rb
238
239
  - lib/active_support/continuous_integration.rb
@@ -458,6 +459,7 @@ files:
458
459
  - lib/active_support/secure_compare_rotator.rb
459
460
  - lib/active_support/security_utils.rb
460
461
  - lib/active_support/string_inquirer.rb
462
+ - lib/active_support/structured_event_subscriber.rb
461
463
  - lib/active_support/subscriber.rb
462
464
  - lib/active_support/syntax_error_proxy.rb
463
465
  - lib/active_support/tagged_logging.rb
@@ -499,10 +501,10 @@ licenses:
499
501
  - MIT
500
502
  metadata:
501
503
  bug_tracker_uri: https://github.com/rails/rails/issues
502
- changelog_uri: https://github.com/rails/rails/blob/v8.1.0.beta1/activesupport/CHANGELOG.md
503
- documentation_uri: https://api.rubyonrails.org/v8.1.0.beta1/
504
+ changelog_uri: https://github.com/rails/rails/blob/v8.1.0/activesupport/CHANGELOG.md
505
+ documentation_uri: https://api.rubyonrails.org/v8.1.0/
504
506
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
505
- source_code_uri: https://github.com/rails/rails/tree/v8.1.0.beta1/activesupport
507
+ source_code_uri: https://github.com/rails/rails/tree/v8.1.0/activesupport
506
508
  rubygems_mfa_required: 'true'
507
509
  rdoc_options:
508
510
  - "--encoding"