activesupport 8.0.4 → 8.1.0.beta1

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -198
  3. data/lib/active_support/backtrace_cleaner.rb +71 -0
  4. data/lib/active_support/cache/mem_cache_store.rb +13 -13
  5. data/lib/active_support/cache/redis_cache_store.rb +36 -30
  6. data/lib/active_support/cache/strategy/local_cache.rb +16 -7
  7. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  8. data/lib/active_support/cache.rb +69 -6
  9. data/lib/active_support/configurable.rb +28 -0
  10. data/lib/active_support/continuous_integration.rb +145 -0
  11. data/lib/active_support/core_ext/benchmark.rb +0 -1
  12. data/lib/active_support/core_ext/class/attribute.rb +6 -8
  13. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  14. data/lib/active_support/core_ext/enumerable.rb +2 -2
  15. data/lib/active_support/core_ext/erb/util.rb +3 -3
  16. data/lib/active_support/core_ext/object/json.rb +8 -1
  17. data/lib/active_support/core_ext/object/to_query.rb +5 -0
  18. data/lib/active_support/core_ext/range.rb +0 -1
  19. data/lib/active_support/core_ext/string/multibyte.rb +10 -1
  20. data/lib/active_support/core_ext/string/output_safety.rb +19 -12
  21. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  22. data/lib/active_support/current_attributes.rb +13 -10
  23. data/lib/active_support/deprecation/reporting.rb +4 -2
  24. data/lib/active_support/deprecation.rb +1 -1
  25. data/lib/active_support/editor.rb +70 -0
  26. data/lib/active_support/error_reporter.rb +50 -6
  27. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  28. data/lib/active_support/event_reporter.rb +570 -0
  29. data/lib/active_support/evented_file_update_checker.rb +5 -1
  30. data/lib/active_support/execution_context.rb +64 -7
  31. data/lib/active_support/file_update_checker.rb +8 -6
  32. data/lib/active_support/gem_version.rb +3 -3
  33. data/lib/active_support/gzip.rb +1 -0
  34. data/lib/active_support/hash_with_indifferent_access.rb +27 -7
  35. data/lib/active_support/i18n_railtie.rb +1 -2
  36. data/lib/active_support/inflector/inflections.rb +31 -15
  37. data/lib/active_support/inflector/transliterate.rb +6 -8
  38. data/lib/active_support/isolated_execution_state.rb +7 -13
  39. data/lib/active_support/json/decoding.rb +2 -2
  40. data/lib/active_support/json/encoding.rb +103 -14
  41. data/lib/active_support/log_subscriber.rb +2 -0
  42. data/lib/active_support/message_encryptors.rb +52 -0
  43. data/lib/active_support/message_pack/extensions.rb +5 -0
  44. data/lib/active_support/message_verifiers.rb +52 -0
  45. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  46. data/lib/active_support/messages/rotator.rb +5 -0
  47. data/lib/active_support/multibyte/chars.rb +8 -1
  48. data/lib/active_support/multibyte.rb +4 -0
  49. data/lib/active_support/notifications/instrumenter.rb +1 -1
  50. data/lib/active_support/railtie.rb +26 -12
  51. data/lib/active_support/syntax_error_proxy.rb +3 -0
  52. data/lib/active_support/test_case.rb +61 -6
  53. data/lib/active_support/testing/assertions.rb +34 -6
  54. data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
  55. data/lib/active_support/testing/event_reporter_assertions.rb +217 -0
  56. data/lib/active_support/testing/notification_assertions.rb +92 -0
  57. data/lib/active_support/testing/parallelization/server.rb +2 -15
  58. data/lib/active_support/testing/parallelization/worker.rb +4 -2
  59. data/lib/active_support/testing/parallelization.rb +14 -12
  60. data/lib/active_support/testing/tests_without_assertions.rb +1 -1
  61. data/lib/active_support/testing/time_helpers.rb +7 -3
  62. data/lib/active_support/time_with_zone.rb +19 -5
  63. data/lib/active_support/values/time_zone.rb +8 -1
  64. data/lib/active_support/xml_mini.rb +1 -4
  65. data/lib/active_support.rb +11 -0
  66. metadata +10 -5
  67. data/lib/active_support/core_ext/range/each.rb +0 -24
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Testing
5
+ # Provides test helpers for asserting on ActiveSupport::EventReporter events.
6
+ module EventReporterAssertions
7
+ module EventCollector # :nodoc:
8
+ @subscribed = false
9
+ @mutex = Mutex.new
10
+
11
+ class Event # :nodoc:
12
+ attr_reader :event_data
13
+
14
+ def initialize(event_data)
15
+ @event_data = event_data
16
+ end
17
+
18
+ def inspect
19
+ "#{event_data[:name]} (payload: #{event_data[:payload].inspect}, tags: #{event_data[:tags].inspect})"
20
+ end
21
+
22
+ def matches?(name, payload, tags)
23
+ return false unless name.to_s == event_data[:name]
24
+
25
+ if payload && payload.is_a?(Hash)
26
+ return false unless matches_hash?(payload, :payload)
27
+ end
28
+
29
+ return false unless matches_hash?(tags, :tags)
30
+ true
31
+ end
32
+
33
+ private
34
+ def matches_hash?(expected_hash, event_key)
35
+ expected_hash.all? do |k, v|
36
+ if v.is_a?(Regexp)
37
+ event_data.dig(event_key, k).to_s.match?(v)
38
+ else
39
+ event_data.dig(event_key, k) == v
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ class << self
46
+ def emit(event)
47
+ event_recorders&.each do |events|
48
+ events << Event.new(event)
49
+ end
50
+ true
51
+ end
52
+
53
+ def record
54
+ subscribe
55
+ events = []
56
+ event_recorders << events
57
+ begin
58
+ yield
59
+ events
60
+ ensure
61
+ event_recorders.delete_if { |r| events.equal?(r) }
62
+ end
63
+ end
64
+
65
+ private
66
+ def subscribe
67
+ return if @subscribed
68
+
69
+ @mutex.synchronize do
70
+ unless @subscribed
71
+ if ActiveSupport.event_reporter
72
+ ActiveSupport.event_reporter.subscribe(self)
73
+ @subscribed = true
74
+ else
75
+ raise Minitest::Assertion, "No event reporter is configured"
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def event_recorders
82
+ ActiveSupport::IsolatedExecutionState[:active_support_event_reporter_assertions] ||= []
83
+ end
84
+ end
85
+ end
86
+
87
+ # Asserts that the block does not cause an event to be reported to +Rails.event+.
88
+ #
89
+ # If no name is provided, passes if evaluated code in the yielded block reports no events.
90
+ #
91
+ # assert_no_event_reported do
92
+ # service_that_does_not_report_events.perform
93
+ # end
94
+ #
95
+ # If a name is provided, passes if evaluated code in the yielded block reports no events
96
+ # with that name.
97
+ #
98
+ # assert_no_event_reported("user.created") do
99
+ # service_that_does_not_report_events.perform
100
+ # end
101
+ def assert_no_event_reported(name = nil, payload: {}, tags: {}, &block)
102
+ events = EventCollector.record(&block)
103
+
104
+ if name.nil?
105
+ assert_predicate(events, :empty?)
106
+ else
107
+ matching_event = events.find { |event| event.matches?(name, payload, tags) }
108
+ if matching_event
109
+ message = "Expected no '#{name}' event to be reported, but found:\n " \
110
+ "#{matching_event.inspect}"
111
+ flunk(message)
112
+ end
113
+ assert(true)
114
+ end
115
+ end
116
+
117
+ # Asserts that the block causes an event with the given name to be reported
118
+ # to +Rails.event+.
119
+ #
120
+ # Passes if the evaluated code in the yielded block reports a matching event.
121
+ #
122
+ # assert_event_reported("user.created") do
123
+ # Rails.event.notify("user.created", { id: 123 })
124
+ # end
125
+ #
126
+ # To test further details about the reported event, you can specify payload and tag matchers.
127
+ #
128
+ # assert_event_reported("user.created",
129
+ # payload: { id: 123, name: "John Doe" },
130
+ # tags: { request_id: /[0-9]+/ }
131
+ # ) do
132
+ # Rails.event.tagged(request_id: "123") do
133
+ # Rails.event.notify("user.created", { id: 123, name: "John Doe" })
134
+ # end
135
+ # end
136
+ #
137
+ # The matchers support partial matching - only the specified keys need to match.
138
+ #
139
+ # assert_event_reported("user.created", payload: { id: 123 }) do
140
+ # Rails.event.notify("user.created", { id: 123, name: "John Doe" })
141
+ # end
142
+ def assert_event_reported(name, payload: nil, tags: {}, &block)
143
+ events = EventCollector.record(&block)
144
+
145
+ if events.empty?
146
+ flunk("Expected an event to be reported, but there were no events reported.")
147
+ elsif (event = events.find { |event| event.matches?(name, payload, tags) })
148
+ assert(true)
149
+ event.event_data
150
+ else
151
+ message = "Expected an event to be reported matching:\n " \
152
+ "name: #{name}\n " \
153
+ "payload: #{payload.inspect}\n " \
154
+ "tags: #{tags.inspect}\n" \
155
+ "but none of the #{events.size} reported events matched:\n " \
156
+ "#{events.map(&:inspect).join("\n ")}"
157
+ flunk(message)
158
+ end
159
+ end
160
+
161
+ # Asserts that the provided events were reported, regardless of order.
162
+ #
163
+ # assert_events_reported([
164
+ # { name: "user.created", payload: { id: 123 } },
165
+ # { name: "email.sent", payload: { to: "user@example.com" } }
166
+ # ]) do
167
+ # create_user_and_send_welcome_email
168
+ # end
169
+ #
170
+ # Supports the same payload and tag matching as +assert_event_reported+.
171
+ #
172
+ # assert_events_reported([
173
+ # {
174
+ # name: "process.started",
175
+ # payload: { id: 123 },
176
+ # tags: { request_id: /[0-9]+/ }
177
+ # },
178
+ # { name: "process.completed" }
179
+ # ]) do
180
+ # Rails.event.tagged(request_id: "456") do
181
+ # start_and_complete_process(123)
182
+ # end
183
+ # end
184
+ def assert_events_reported(expected_events, &block)
185
+ events = EventCollector.record(&block)
186
+
187
+ if events.empty? && expected_events.size > 0
188
+ flunk("Expected #{expected_events.size} events to be reported, but there were no events reported.")
189
+ end
190
+
191
+ events_copy = events.dup
192
+
193
+ expected_events.each do |expected_event|
194
+ name = expected_event[:name]
195
+ payload = expected_event[:payload] || {}
196
+ tags = expected_event[:tags] || {}
197
+
198
+ matching_event_index = events_copy.find_index { |event| event.matches?(name, payload, tags) }
199
+
200
+ if matching_event_index
201
+ events_copy.delete_at(matching_event_index)
202
+ else
203
+ message = "Expected an event to be reported matching:\n " \
204
+ "name: #{name.inspect}\n " \
205
+ "payload: #{payload.inspect}\n " \
206
+ "tags: #{tags.inspect}\n" \
207
+ "but none of the #{events.size} reported events matched:\n " \
208
+ "#{events.map(&:inspect).join("\n ")}"
209
+ flunk(message)
210
+ end
211
+ end
212
+
213
+ assert(true)
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Testing
5
+ module NotificationAssertions
6
+ # Assert a notification was emitted with a given +pattern+ and optional +payload+.
7
+ #
8
+ # You can assert that a notification was emitted by passing a pattern, which accepts
9
+ # either a string or regexp, an optional payload, and a block. While the block
10
+ # is executed, if a matching notification is emitted, the assertion will pass
11
+ # and the notification will be returned.
12
+ #
13
+ # Note that the payload is matched as a subset, meaning that the notification must
14
+ # contain at least the specified keys and values, but may contain additional ones.
15
+ #
16
+ # assert_notification("post.submitted", title: "Cool Post") do
17
+ # post.submit(title: "Cool Post", body: "Cool Body") # => emits matching notification
18
+ # end
19
+ #
20
+ # Using the returned notification, you can make more customized assertions.
21
+ #
22
+ # notification = assert_notification("post.submitted", title: "Cool Post") do
23
+ # ActiveSupport::Notifications.instrument("post.submitted", title: "Cool Post", body: Body.new("Cool Body"))
24
+ # end
25
+ #
26
+ # assert_instance_of(Body, notification.payload[:body])
27
+ #
28
+ def assert_notification(pattern, payload = nil, &block)
29
+ notifications = capture_notifications(pattern, &block)
30
+ assert_not_empty(notifications, "No #{pattern} notifications were found")
31
+
32
+ return notifications.first if payload.nil?
33
+
34
+ notification = notifications.find { |notification| notification.payload.slice(*payload.keys) == payload }
35
+ assert_not_nil(notification, "No #{pattern} notification with payload #{payload} was found")
36
+
37
+ notification
38
+ end
39
+
40
+ # Assert the number of notifications emitted with a given +pattern+.
41
+ #
42
+ # You can assert the number of notifications emitted by passing a pattern, which accepts
43
+ # either a string or regexp, a count, and a block. While the block is executed,
44
+ # the number of matching notifications emitted will be counted. After the block's
45
+ # execution completes, the assertion will pass if the count matches.
46
+ #
47
+ # assert_notifications_count("post.submitted", 1) do
48
+ # post.submit(title: "Cool Post") # => emits matching notification
49
+ # end
50
+ #
51
+ def assert_notifications_count(pattern, count, &block)
52
+ actual_count = capture_notifications(pattern, &block).count
53
+ assert_equal(count, actual_count, "Expected #{count} instead of #{actual_count} notifications for #{pattern}")
54
+ end
55
+
56
+ # Assert no notifications were emitted for a given +pattern+.
57
+ #
58
+ # You can assert no notifications were emitted by passing a pattern, which accepts
59
+ # either a string or regexp, and a block. While the block is executed, if no
60
+ # matching notifications are emitted, the assertion will pass.
61
+ #
62
+ # assert_no_notifications("post.submitted") do
63
+ # post.destroy # => emits non-matching notification
64
+ # end
65
+ #
66
+ def assert_no_notifications(pattern = nil, &block)
67
+ notifications = capture_notifications(pattern, &block)
68
+ error_message = if pattern
69
+ "Expected no notifications for #{pattern} but found #{notifications.size}"
70
+ else
71
+ "Expected no notifications but found #{notifications.size}"
72
+ end
73
+ assert_empty(notifications, error_message)
74
+ end
75
+
76
+ # Capture emitted notifications, optionally filtered by a +pattern+.
77
+ #
78
+ # You can capture emitted notifications, optionally filtered by a pattern,
79
+ # which accepts either a string or regexp, and a block.
80
+ #
81
+ # notifications = capture_notifications("post.submitted") do
82
+ # post.submit(title: "Cool Post") # => emits matching notification
83
+ # end
84
+ #
85
+ def capture_notifications(pattern = nil, &block)
86
+ notifications = []
87
+ ActiveSupport::Notifications.subscribed(->(n) { notifications << n }, pattern, &block)
88
+ notifications
89
+ end
90
+ end
91
+ end
92
+ end
@@ -14,7 +14,6 @@ module ActiveSupport
14
14
  def initialize
15
15
  @queue = Queue.new
16
16
  @active_workers = Concurrent::Map.new
17
- @worker_pids = Concurrent::Map.new
18
17
  @in_flight = Concurrent::Map.new
19
18
  end
20
19
 
@@ -41,24 +40,12 @@ module ActiveSupport
41
40
  end
42
41
  end
43
42
 
44
- def start_worker(worker_id, worker_pid)
43
+ def start_worker(worker_id)
45
44
  @active_workers[worker_id] = true
46
- @worker_pids[worker_id] = worker_pid
47
45
  end
48
46
 
49
- def stop_worker(worker_id, worker_pid)
47
+ def stop_worker(worker_id)
50
48
  @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
62
49
  end
63
50
 
64
51
  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, Process.pid)
21
+ @queue.start_worker(@id)
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, Process.pid)
32
+ @queue.stop_worker(@id)
33
33
  end
34
34
  end
35
35
 
@@ -78,6 +78,8 @@ module ActiveSupport
78
78
  end
79
79
 
80
80
  def after_fork
81
+ ActiveSupport::TestCase.parallel_worker_id = @number
82
+
81
83
  Parallelization.after_fork_hooks.each do |cb|
82
84
  cb.call(@number)
83
85
  end
@@ -9,6 +9,14 @@ require "active_support/testing/parallelization/worker"
9
9
  module ActiveSupport
10
10
  module Testing
11
11
  class Parallelization # :nodoc:
12
+ @@before_fork_hooks = []
13
+
14
+ def self.before_fork_hook(&blk)
15
+ @@before_fork_hooks << blk
16
+ end
17
+
18
+ cattr_reader :before_fork_hooks
19
+
12
20
  @@after_fork_hooks = []
13
21
 
14
22
  def self.after_fork_hook(&blk)
@@ -32,7 +40,12 @@ module ActiveSupport
32
40
  @url = DRb.start_service("drbunix:", @queue_server).uri
33
41
  end
34
42
 
43
+ def before_fork
44
+ Parallelization.before_fork_hooks.each(&:call)
45
+ end
46
+
35
47
  def start
48
+ before_fork
36
49
  @worker_pool = @worker_count.times.map do |worker|
37
50
  Worker.new(worker, @url).start
38
51
  end
@@ -47,19 +60,8 @@ module ActiveSupport
47
60
  end
48
61
 
49
62
  def shutdown
50
- dead_worker_pids = @worker_pool.filter_map do |pid|
51
- Process.waitpid(pid, Process::WNOHANG)
52
- rescue Errno::ECHILD
53
- pid
54
- end
55
- @queue_server.remove_dead_workers(dead_worker_pids)
56
-
57
63
  @queue_server.shutdown
58
- @worker_pool.each do |pid|
59
- Process.waitpid(pid)
60
- rescue Errno::ECHILD
61
- nil
62
- end
64
+ @worker_pool.each { |pid| Process.waitpid pid }
63
65
  end
64
66
  end
65
67
  end
@@ -11,7 +11,7 @@ module ActiveSupport
11
11
 
12
12
  if assertions.zero? && !skipped? && !error?
13
13
  file, line = method(name).source_location
14
- warn "Test is missing assertions: `#{name}` #{file}:#{line}"
14
+ warn "Test is missing assertions: `#{name}` #{File.expand_path(file)}:#{line}"
15
15
  end
16
16
  end
17
17
  end
@@ -238,12 +238,16 @@ module ActiveSupport
238
238
  end
239
239
  alias_method :unfreeze_time, :travel_back
240
240
 
241
- # Calls +travel_to+ with +Time.now+. Forwards optional <tt>with_usec</tt> argument.
241
+ # Calls +travel_to+ with +date_or_time+, which defaults to +Time.now+.
242
+ # Forwards optional <tt>with_usec</tt> argument.
242
243
  #
243
244
  # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
244
245
  # freeze_time
245
246
  # sleep(1)
246
247
  # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
248
+ # freeze_time Time.current + 1.day
249
+ # sleep(1)
250
+ # Time.current # => Mon, 10 Jul 2017 15:34:49 EST -05:00
247
251
  #
248
252
  # This method also accepts a block, which will return the current time back to its original
249
253
  # state at the end of the block:
@@ -254,8 +258,8 @@ module ActiveSupport
254
258
  # User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
255
259
  # end
256
260
  # Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
257
- def freeze_time(with_usec: false, &block)
258
- travel_to Time.now, with_usec: with_usec, &block
261
+ def freeze_time(date_or_time = Time.now, with_usec: false, &block)
262
+ travel_to date_or_time, with_usec: with_usec, &block
259
263
  end
260
264
 
261
265
  private
@@ -49,9 +49,15 @@ module ActiveSupport
49
49
  attr_reader :time_zone
50
50
 
51
51
  def initialize(utc_time, time_zone, local_time = nil, period = nil)
52
- @utc = utc_time ? transfer_time_values_to_utc_constructor(utc_time) : nil
53
52
  @time_zone, @time = time_zone, local_time
54
- @period = @utc ? period : get_period_and_ensure_valid_local_time(period)
53
+ if utc_time
54
+ @utc = transfer_time_values_to_utc_constructor(utc_time)
55
+ @period = period
56
+ else
57
+ @utc = nil
58
+ @period = get_period_and_ensure_valid_local_time(period)
59
+ end
60
+ @is_utc = zone == "UTC" || zone == "UCT"
55
61
  end
56
62
 
57
63
  # Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
@@ -103,7 +109,7 @@ module ActiveSupport
103
109
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
104
110
  # Time.zone.now.utc? # => false
105
111
  def utc?
106
- zone == "UTC" || zone == "UCT"
112
+ @is_utc
107
113
  end
108
114
  alias_method :gmt?, :utc?
109
115
 
@@ -146,7 +152,13 @@ module ActiveSupport
146
152
  #
147
153
  # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
148
154
  def xmlschema(fraction_digits = 0)
149
- "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}"
155
+ if @is_utc
156
+ utc.iso8601(fraction_digits || 0)
157
+ else
158
+ str = time.iso8601(fraction_digits || 0)
159
+ str[-1] = formatted_offset(true, "Z")
160
+ str
161
+ end
150
162
  end
151
163
  alias_method :iso8601, :xmlschema
152
164
  alias_method :rfc3339, :xmlschema
@@ -560,7 +572,9 @@ module ActiveSupport
560
572
  SECONDS_PER_DAY = 86400
561
573
 
562
574
  def incorporate_utc_offset(time, offset)
563
- if time.kind_of?(Date)
575
+ if offset.zero?
576
+ time
577
+ elsif time.kind_of?(Date)
564
578
  time + Rational(offset, SECONDS_PER_DAY)
565
579
  else
566
580
  time + offset
@@ -57,13 +57,14 @@ module ActiveSupport
57
57
  "Caracas" => "America/Caracas",
58
58
  "La Paz" => "America/La_Paz",
59
59
  "Santiago" => "America/Santiago",
60
+ "Asuncion" => "America/Asuncion",
60
61
  "Newfoundland" => "America/St_Johns",
61
62
  "Brasilia" => "America/Sao_Paulo",
62
63
  "Buenos Aires" => "America/Argentina/Buenos_Aires",
63
64
  "Montevideo" => "America/Montevideo",
64
65
  "Georgetown" => "America/Guyana",
65
66
  "Puerto Rico" => "America/Puerto_Rico",
66
- "Greenland" => "America/Godthab",
67
+ "Greenland" => "America/Nuuk",
67
68
  "Mid-Atlantic" => "Atlantic/South_Georgia",
68
69
  "Azores" => "Atlantic/Azores",
69
70
  "Cape Verde Is." => "Atlantic/Cape_Verde",
@@ -313,6 +314,12 @@ module ActiveSupport
313
314
  end
314
315
  # :startdoc:
315
316
 
317
+ # Returns a standard time zone name defined by IANA
318
+ # https://www.iana.org/time-zones
319
+ def standard_name
320
+ MAPPING[name] || name
321
+ end
322
+
316
323
  # Returns the offset of this time zone from UTC in seconds.
317
324
  def utc_offset
318
325
  @utc_offset || tzinfo&.current_period&.base_utc_offset
@@ -62,11 +62,10 @@ module ActiveSupport
62
62
  "yaml" => Proc.new { |yaml| yaml.to_yaml }
63
63
  } unless defined?(FORMATTING)
64
64
 
65
- # TODO use regexp instead of Date.parse
66
65
  unless defined?(PARSING)
67
66
  PARSING = {
68
67
  "symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
69
- "date" => Proc.new { |date| ::Date.parse(date) },
68
+ "date" => Proc.new { |date| ::Date.strptime(date, "%Y-%m-%d") },
70
69
  "datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
71
70
  "duration" => Proc.new { |duration| Duration.parse(duration) },
72
71
  "integer" => Proc.new { |integer| integer.to_i },
@@ -74,8 +73,6 @@ module ActiveSupport
74
73
  "decimal" => Proc.new do |number|
75
74
  if String === number
76
75
  number.to_d
77
- elsif Float === number
78
- BigDecimal(number, 0)
79
76
  else
80
77
  BigDecimal(number)
81
78
  end
@@ -40,12 +40,15 @@ module ActiveSupport
40
40
  autoload :CodeGenerator
41
41
  autoload :ActionableError
42
42
  autoload :ConfigurationFile
43
+ autoload :ContinuousIntegration
43
44
  autoload :CurrentAttributes
44
45
  autoload :Dependencies
45
46
  autoload :DescendantsTracker
47
+ autoload :Editor
46
48
  autoload :ExecutionWrapper
47
49
  autoload :Executor
48
50
  autoload :ErrorReporter
51
+ autoload :EventReporter
49
52
  autoload :FileUpdateChecker
50
53
  autoload :EventedFileUpdateChecker
51
54
  autoload :ForkTracker
@@ -91,6 +94,10 @@ module ActiveSupport
91
94
  autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
92
95
  autoload :TestCase
93
96
 
97
+ include Deprecation::DeprecatedConstantAccessor
98
+
99
+ deprecate_constant :Configurable, "class_attribute :config, default: {}", deprecator: ActiveSupport.deprecator
100
+
94
101
  def self.eager_load!
95
102
  super
96
103
 
@@ -99,10 +106,14 @@ module ActiveSupport
99
106
 
100
107
  cattr_accessor :test_order # :nodoc:
101
108
  cattr_accessor :test_parallelization_threshold, default: 50 # :nodoc:
109
+ cattr_accessor :parallelize_test_databases, default: true # :nodoc:
102
110
 
103
111
  @error_reporter = ActiveSupport::ErrorReporter.new
104
112
  singleton_class.attr_accessor :error_reporter # :nodoc:
105
113
 
114
+ @event_reporter = ActiveSupport::EventReporter.new
115
+ singleton_class.attr_accessor :event_reporter # :nodoc:
116
+
106
117
  def self.cache_format_version
107
118
  Cache.format_version
108
119
  end
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.0.4
4
+ version: 8.1.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -235,6 +235,7 @@ files:
235
235
  - lib/active_support/concurrency/share_lock.rb
236
236
  - lib/active_support/configurable.rb
237
237
  - lib/active_support/configuration_file.rb
238
+ - lib/active_support/continuous_integration.rb
238
239
  - lib/active_support/core_ext.rb
239
240
  - lib/active_support/core_ext/array.rb
240
241
  - lib/active_support/core_ext/array/access.rb
@@ -327,7 +328,6 @@ files:
327
328
  - lib/active_support/core_ext/range.rb
328
329
  - lib/active_support/core_ext/range/compare_range.rb
329
330
  - lib/active_support/core_ext/range/conversions.rb
330
- - lib/active_support/core_ext/range/each.rb
331
331
  - lib/active_support/core_ext/range/overlap.rb
332
332
  - lib/active_support/core_ext/range/sole.rb
333
333
  - lib/active_support/core_ext/regexp.rb
@@ -377,11 +377,14 @@ files:
377
377
  - lib/active_support/duration.rb
378
378
  - lib/active_support/duration/iso8601_parser.rb
379
379
  - lib/active_support/duration/iso8601_serializer.rb
380
+ - lib/active_support/editor.rb
380
381
  - lib/active_support/encrypted_configuration.rb
381
382
  - lib/active_support/encrypted_file.rb
382
383
  - lib/active_support/environment_inquirer.rb
383
384
  - lib/active_support/error_reporter.rb
384
385
  - lib/active_support/error_reporter/test_helper.rb
386
+ - lib/active_support/event_reporter.rb
387
+ - lib/active_support/event_reporter/test_helper.rb
385
388
  - lib/active_support/evented_file_update_checker.rb
386
389
  - lib/active_support/execution_context.rb
387
390
  - lib/active_support/execution_context/test_helper.rb
@@ -466,9 +469,11 @@ files:
466
469
  - lib/active_support/testing/declarative.rb
467
470
  - lib/active_support/testing/deprecation.rb
468
471
  - lib/active_support/testing/error_reporter_assertions.rb
472
+ - lib/active_support/testing/event_reporter_assertions.rb
469
473
  - lib/active_support/testing/file_fixtures.rb
470
474
  - lib/active_support/testing/isolation.rb
471
475
  - lib/active_support/testing/method_call_assertions.rb
476
+ - lib/active_support/testing/notification_assertions.rb
472
477
  - lib/active_support/testing/parallelization.rb
473
478
  - lib/active_support/testing/parallelization/server.rb
474
479
  - lib/active_support/testing/parallelization/worker.rb
@@ -494,10 +499,10 @@ licenses:
494
499
  - MIT
495
500
  metadata:
496
501
  bug_tracker_uri: https://github.com/rails/rails/issues
497
- changelog_uri: https://github.com/rails/rails/blob/v8.0.4/activesupport/CHANGELOG.md
498
- documentation_uri: https://api.rubyonrails.org/v8.0.4/
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/
499
504
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
500
- source_code_uri: https://github.com/rails/rails/tree/v8.0.4/activesupport
505
+ source_code_uri: https://github.com/rails/rails/tree/v8.1.0.beta1/activesupport
501
506
  rubygems_mfa_required: 'true'
502
507
  rdoc_options:
503
508
  - "--encoding"