activesupport 7.0.0.alpha2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -0
  3. data/lib/active_support/cache/mem_cache_store.rb +9 -5
  4. data/lib/active_support/cache/memory_store.rb +2 -2
  5. data/lib/active_support/cache/redis_cache_store.rb +3 -8
  6. data/lib/active_support/cache/strategy/local_cache.rb +6 -12
  7. data/lib/active_support/callbacks.rb +145 -50
  8. data/lib/active_support/code_generator.rb +65 -0
  9. data/lib/active_support/core_ext/array/conversions.rb +3 -1
  10. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  11. data/lib/active_support/core_ext/array.rb +1 -0
  12. data/lib/active_support/core_ext/class/subclasses.rb +4 -2
  13. data/lib/active_support/core_ext/date/calculations.rb +2 -2
  14. data/lib/active_support/core_ext/date/conversions.rb +3 -3
  15. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  16. data/lib/active_support/core_ext/date.rb +1 -0
  17. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  18. data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
  19. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  20. data/lib/active_support/core_ext/date_time.rb +1 -0
  21. data/lib/active_support/core_ext/digest/uuid.rb +26 -1
  22. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  23. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  24. data/lib/active_support/core_ext/numeric/conversions.rb +78 -75
  25. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  26. data/lib/active_support/core_ext/numeric.rb +1 -0
  27. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  28. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  29. data/lib/active_support/core_ext/pathname.rb +3 -0
  30. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  31. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  32. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -25
  33. data/lib/active_support/core_ext/range.rb +1 -1
  34. data/lib/active_support/core_ext/time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/time/conversions.rb +4 -3
  36. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  37. data/lib/active_support/core_ext/time/zones.rb +2 -2
  38. data/lib/active_support/core_ext/time.rb +1 -0
  39. data/lib/active_support/core_ext/uri.rb +3 -13
  40. data/lib/active_support/core_ext.rb +1 -0
  41. data/lib/active_support/current_attributes.rb +26 -25
  42. data/lib/active_support/descendants_tracker.rb +175 -69
  43. data/lib/active_support/duration.rb +4 -3
  44. data/lib/active_support/error_reporter.rb +117 -0
  45. data/lib/active_support/execution_context/test_helper.rb +13 -0
  46. data/lib/active_support/execution_context.rb +53 -0
  47. data/lib/active_support/execution_wrapper.rb +30 -4
  48. data/lib/active_support/executor/test_helper.rb +7 -0
  49. data/lib/active_support/fork_tracker.rb +18 -9
  50. data/lib/active_support/gem_version.rb +1 -1
  51. data/lib/active_support/html_safe_translation.rb +43 -0
  52. data/lib/active_support/i18n_railtie.rb +1 -1
  53. data/lib/active_support/inflector/inflections.rb +12 -3
  54. data/lib/active_support/inflector/methods.rb +2 -2
  55. data/lib/active_support/isolated_execution_state.rb +56 -0
  56. data/lib/active_support/logger_thread_safe_level.rb +2 -3
  57. data/lib/active_support/message_encryptor.rb +5 -0
  58. data/lib/active_support/message_verifier.rb +42 -10
  59. data/lib/active_support/multibyte/unicode.rb +0 -12
  60. data/lib/active_support/notifications/fanout.rb +61 -55
  61. data/lib/active_support/notifications/instrumenter.rb +15 -15
  62. data/lib/active_support/notifications.rb +5 -21
  63. data/lib/active_support/option_merger.rb +4 -0
  64. data/lib/active_support/per_thread_registry.rb +4 -0
  65. data/lib/active_support/railtie.rb +38 -11
  66. data/lib/active_support/ruby_features.rb +7 -0
  67. data/lib/active_support/subscriber.rb +2 -18
  68. data/lib/active_support/tagged_logging.rb +1 -1
  69. data/lib/active_support/testing/deprecation.rb +52 -1
  70. data/lib/active_support/testing/isolation.rb +1 -1
  71. data/lib/active_support/time_with_zone.rb +34 -6
  72. data/lib/active_support/values/time_zone.rb +5 -0
  73. data/lib/active_support/xml_mini.rb +3 -3
  74. data/lib/active_support.rb +7 -4
  75. metadata +25 -8
@@ -7,6 +7,16 @@ require "active_support/core_ext/object/try"
7
7
 
8
8
  module ActiveSupport
9
9
  module Notifications
10
+ class InstrumentationSubscriberError < RuntimeError
11
+ attr_reader :exceptions
12
+
13
+ def initialize(exceptions)
14
+ @exceptions = exceptions
15
+ exception_class_names = exceptions.map { |e| e.class.name }
16
+ super "Exception(s) occurred within instrumentation subscribers: #{exception_class_names.join(', ')}"
17
+ end
18
+ end
19
+
10
20
  # This is a default queue implementation that ships with Notifications.
11
21
  # It just pushes events to all registered log subscribers.
12
22
  #
@@ -59,19 +69,40 @@ module ActiveSupport
59
69
  end
60
70
 
61
71
  def start(name, id, payload)
62
- listeners_for(name).each { |s| s.start(name, id, payload) }
72
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.start(name, id, payload) }
63
73
  end
64
74
 
65
75
  def finish(name, id, payload, listeners = listeners_for(name))
66
- listeners.each { |s| s.finish(name, id, payload) }
76
+ iterate_guarding_exceptions(listeners) { |s| s.finish(name, id, payload) }
67
77
  end
68
78
 
69
79
  def publish(name, *args)
70
- listeners_for(name).each { |s| s.publish(name, *args) }
80
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
71
81
  end
72
82
 
73
83
  def publish_event(event)
74
- listeners_for(event.name).each { |s| s.publish_event(event) }
84
+ iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) }
85
+ end
86
+
87
+ def iterate_guarding_exceptions(listeners)
88
+ exceptions = nil
89
+
90
+ listeners.each do |s|
91
+ yield s
92
+ rescue Exception => e
93
+ exceptions ||= []
94
+ exceptions << e
95
+ end
96
+
97
+ if exceptions
98
+ if exceptions.size == 1
99
+ raise exceptions.first
100
+ else
101
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
102
+ end
103
+ end
104
+
105
+ listeners
75
106
  end
76
107
 
77
108
  def listeners_for(name)
@@ -108,23 +139,20 @@ module ActiveSupport
108
139
  end
109
140
  end
110
141
 
111
- wrap_all pattern, subscriber_class.new(pattern, listener)
112
- end
113
-
114
- def self.wrap_all(pattern, subscriber)
115
- unless pattern
116
- AllMessages.new(subscriber)
117
- else
118
- subscriber
119
- end
142
+ subscriber_class.new(pattern, listener)
120
143
  end
121
144
 
122
145
  class Matcher # :nodoc:
123
146
  attr_reader :pattern, :exclusions
124
147
 
125
148
  def self.wrap(pattern)
126
- return pattern if String === pattern
127
- new(pattern)
149
+ if String === pattern
150
+ pattern
151
+ elsif pattern.nil?
152
+ AllMessages.new
153
+ else
154
+ new(pattern)
155
+ end
128
156
  end
129
157
 
130
158
  def initialize(pattern)
@@ -139,6 +167,16 @@ module ActiveSupport
139
167
  def ===(name)
140
168
  pattern === name && !exclusions.include?(name)
141
169
  end
170
+
171
+ class AllMessages
172
+ def ===(name)
173
+ true
174
+ end
175
+
176
+ def unsubscribe!(*)
177
+ false
178
+ end
179
+ end
142
180
  end
143
181
 
144
182
  class Evented # :nodoc:
@@ -177,10 +215,6 @@ module ActiveSupport
177
215
  pattern === name
178
216
  end
179
217
 
180
- def matches?(name)
181
- pattern && pattern === name
182
- end
183
-
184
218
  def unsubscribe!(name)
185
219
  pattern.unsubscribe!(name)
186
220
  end
@@ -192,12 +226,12 @@ module ActiveSupport
192
226
  end
193
227
 
194
228
  def start(name, id, payload)
195
- timestack = Thread.current[:_timestack] ||= []
229
+ timestack = IsolatedExecutionState[:_timestack] ||= []
196
230
  timestack.push Time.now
197
231
  end
198
232
 
199
233
  def finish(name, id, payload)
200
- timestack = Thread.current[:_timestack]
234
+ timestack = IsolatedExecutionState[:_timestack]
201
235
  started = timestack.pop
202
236
  @delegate.call(name, started, Time.now, id, payload)
203
237
  end
@@ -209,27 +243,27 @@ module ActiveSupport
209
243
  end
210
244
 
211
245
  def start(name, id, payload)
212
- timestack = Thread.current[:_timestack_monotonic] ||= []
213
- timestack.push Concurrent.monotonic_time
246
+ timestack = IsolatedExecutionState[:_timestack_monotonic] ||= []
247
+ timestack.push Process.clock_gettime(Process::CLOCK_MONOTONIC)
214
248
  end
215
249
 
216
250
  def finish(name, id, payload)
217
- timestack = Thread.current[:_timestack_monotonic]
251
+ timestack = IsolatedExecutionState[:_timestack_monotonic]
218
252
  started = timestack.pop
219
- @delegate.call(name, started, Concurrent.monotonic_time, id, payload)
253
+ @delegate.call(name, started, Process.clock_gettime(Process::CLOCK_MONOTONIC), id, payload)
220
254
  end
221
255
  end
222
256
 
223
257
  class EventObject < Evented
224
258
  def start(name, id, payload)
225
- stack = Thread.current[:_event_stack] ||= []
259
+ stack = IsolatedExecutionState[:_event_stack] ||= []
226
260
  event = build_event name, id, payload
227
261
  event.start!
228
262
  stack.push event
229
263
  end
230
264
 
231
265
  def finish(name, id, payload)
232
- stack = Thread.current[:_event_stack]
266
+ stack = IsolatedExecutionState[:_event_stack]
233
267
  event = stack.pop
234
268
  event.payload = payload
235
269
  event.finish!
@@ -245,34 +279,6 @@ module ActiveSupport
245
279
  ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
246
280
  end
247
281
  end
248
-
249
- class AllMessages # :nodoc:
250
- def initialize(delegate)
251
- @delegate = delegate
252
- end
253
-
254
- def start(name, id, payload)
255
- @delegate.start name, id, payload
256
- end
257
-
258
- def finish(name, id, payload)
259
- @delegate.finish name, id, payload
260
- end
261
-
262
- def publish(name, *args)
263
- @delegate.publish name, *args
264
- end
265
-
266
- def subscribed_to?(name)
267
- true
268
- end
269
-
270
- def unsubscribe!(*)
271
- false
272
- end
273
-
274
- alias :matches? :===
275
- end
276
282
  end
277
283
  end
278
284
  end
@@ -62,12 +62,12 @@ module ActiveSupport
62
62
  def initialize(name, start, ending, transaction_id, payload)
63
63
  @name = name
64
64
  @payload = payload.dup
65
- @time = start
65
+ @time = start ? start.to_f * 1_000.0 : start
66
66
  @transaction_id = transaction_id
67
- @end = ending
67
+ @end = ending ? ending.to_f * 1_000.0 : ending
68
68
  @children = []
69
- @cpu_time_start = 0
70
- @cpu_time_finish = 0
69
+ @cpu_time_start = 0.0
70
+ @cpu_time_finish = 0.0
71
71
  @allocation_count_start = 0
72
72
  @allocation_count_finish = 0
73
73
  end
@@ -102,7 +102,7 @@ module ActiveSupport
102
102
  # Returns the CPU time (in milliseconds) passed since the call to
103
103
  # +start!+ and the call to +finish!+
104
104
  def cpu_time
105
- (@cpu_time_finish - @cpu_time_start) * 1000
105
+ @cpu_time_finish - @cpu_time_start
106
106
  end
107
107
 
108
108
  # Returns the idle time time (in milliseconds) passed since the call to
@@ -130,7 +130,7 @@ module ActiveSupport
130
130
  #
131
131
  # @event.duration # => 1000.138
132
132
  def duration
133
- 1000.0 * (self.end - time)
133
+ self.end - time
134
134
  end
135
135
 
136
136
  def <<(event)
@@ -143,28 +143,28 @@ module ActiveSupport
143
143
 
144
144
  private
145
145
  def now
146
- Concurrent.monotonic_time
146
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
147
147
  end
148
148
 
149
149
  begin
150
- Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
150
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
151
151
 
152
152
  def now_cpu
153
- Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
153
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
154
154
  end
155
155
  rescue
156
- def now_cpu
157
- 0
156
+ def now_cpu # rubocop:disable Lint/DuplicateMethods
157
+ 0.0
158
158
  end
159
159
  end
160
160
 
161
- if defined?(JRUBY_VERSION)
161
+ if GC.stat.key?(:total_allocated_objects)
162
162
  def now_allocations
163
- 0
163
+ GC.stat(:total_allocated_objects)
164
164
  end
165
- else
165
+ else # Likely on JRuby, TruffleRuby
166
166
  def now_allocations
167
- GC.stat :total_allocated_objects
167
+ 0
168
168
  end
169
169
  end
170
170
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "active_support/notifications/instrumenter"
4
4
  require "active_support/notifications/fanout"
5
- require "active_support/per_thread_registry"
6
5
 
7
6
  module ActiveSupport
8
7
  # = Notifications
@@ -261,28 +260,13 @@ module ActiveSupport
261
260
  end
262
261
 
263
262
  def instrumenter
264
- InstrumentationRegistry.instance.instrumenter_for(notifier)
265
- end
266
- end
267
-
268
- # This class is a registry which holds all of the +Instrumenter+ objects
269
- # in a particular thread local. To access the +Instrumenter+ object for a
270
- # particular +notifier+, you can call the following method:
271
- #
272
- # InstrumentationRegistry.instrumenter_for(notifier)
273
- #
274
- # The instrumenters for multiple notifiers are held in a single instance of
275
- # this class.
276
- class InstrumentationRegistry # :nodoc:
277
- extend ActiveSupport::PerThreadRegistry
278
-
279
- def initialize
280
- @registry = {}
263
+ registry[notifier] ||= Instrumenter.new(notifier)
281
264
  end
282
265
 
283
- def instrumenter_for(notifier)
284
- @registry[notifier] ||= Instrumenter.new(notifier)
285
- end
266
+ private
267
+ def registry
268
+ ActiveSupport::IsolatedExecutionState[:active_support_notifications_registry] ||= {}
269
+ end
286
270
  end
287
271
 
288
272
  self.notifier = Fanout.new
@@ -30,5 +30,9 @@ module ActiveSupport
30
30
  @context.__send__(method, *arguments, &block)
31
31
  end
32
32
  end
33
+
34
+ def respond_to_missing?(*arguments)
35
+ @context.respond_to?(*arguments)
36
+ end
33
37
  end
34
38
  end
@@ -40,6 +40,10 @@ module ActiveSupport
40
40
  # If the class has an initializer, it must accept no arguments.
41
41
  module PerThreadRegistry
42
42
  def self.extended(object)
43
+ ActiveSupport::Deprecation.warn(<<~MSG)
44
+ ActiveSupport::PerThreadRegistry is deprecated and will be removed in Rails 7.1.
45
+ Use `Module#thread_mattr_accessor` instead.
46
+ MSG
43
47
  object.instance_variable_set :@per_thread_registry_key, object.name.freeze
44
48
  end
45
49
 
@@ -6,9 +6,16 @@ require "active_support/i18n_railtie"
6
6
  module ActiveSupport
7
7
  class Railtie < Rails::Railtie # :nodoc:
8
8
  config.active_support = ActiveSupport::OrderedOptions.new
9
+ config.active_support.disable_to_s_conversion = false
9
10
 
10
11
  config.eager_load_namespaces << ActiveSupport
11
12
 
13
+ initializer "active_support.isolation_level" do |app|
14
+ if level = app.config.active_support.delete(:isolation_level)
15
+ ActiveSupport::IsolatedExecutionState.isolation_level = level
16
+ end
17
+ end
18
+
12
19
  initializer "active_support.remove_deprecated_time_with_zone_name" do |app|
13
20
  config.after_initialize do
14
21
  if app.config.active_support.remove_deprecated_time_with_zone_name
@@ -27,14 +34,30 @@ module ActiveSupport
27
34
  end
28
35
  end
29
36
 
37
+ initializer "active_support.reset_execution_context" do |app|
38
+ app.reloader.before_class_unload { ActiveSupport::ExecutionContext.clear }
39
+ app.executor.to_run { ActiveSupport::ExecutionContext.clear }
40
+ app.executor.to_complete { ActiveSupport::ExecutionContext.clear }
41
+ end
42
+
30
43
  initializer "active_support.reset_all_current_attributes_instances" do |app|
44
+ executor_around_test_case = app.config.active_support.executor_around_test_case
45
+
31
46
  app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
32
47
  app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
33
48
  app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
34
49
 
35
50
  ActiveSupport.on_load(:active_support_test_case) do
36
- require "active_support/current_attributes/test_helper"
37
- include ActiveSupport::CurrentAttributes::TestHelper
51
+ if executor_around_test_case
52
+ require "active_support/executor/test_helper"
53
+ include ActiveSupport::Executor::TestHelper
54
+ else
55
+ require "active_support/current_attributes/test_helper"
56
+ include ActiveSupport::CurrentAttributes::TestHelper
57
+
58
+ require "active_support/execution_context/test_helper"
59
+ include ActiveSupport::ExecutionContext::TestHelper
60
+ end
38
61
  end
39
62
  end
40
63
 
@@ -90,6 +113,10 @@ module ActiveSupport
90
113
  end
91
114
  end
92
115
 
116
+ initializer "active_support.set_error_reporter" do |app|
117
+ ActiveSupport.error_reporter = app.executor.error_reporter
118
+ end
119
+
93
120
  initializer "active_support.set_configs" do |app|
94
121
  app.config.active_support.each do |k, v|
95
122
  k = "#{k}="
@@ -99,15 +126,6 @@ module ActiveSupport
99
126
 
100
127
  initializer "active_support.set_hash_digest_class" do |app|
101
128
  config.after_initialize do
102
- if app.config.active_support.use_sha1_digests
103
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
104
- config.active_support.use_sha1_digests is deprecated and will
105
- be removed from Rails 7.0. Use
106
- config.active_support.hash_digest_class = OpenSSL::Digest::SHA1 instead.
107
- MSG
108
- ActiveSupport::Digest.hash_digest_class = OpenSSL::Digest::SHA1
109
- end
110
-
111
129
  if klass = app.config.active_support.hash_digest_class
112
130
  ActiveSupport::Digest.hash_digest_class = klass
113
131
  end
@@ -121,5 +139,14 @@ module ActiveSupport
121
139
  end
122
140
  end
123
141
  end
142
+
143
+ initializer "active_support.set_rfc4122_namespaced_uuids" do |app|
144
+ config.after_initialize do
145
+ if app.config.active_support.use_rfc4122_namespaced_uuids
146
+ require "active_support/core_ext/digest"
147
+ ::Digest::UUID.use_rfc4122_namespaced_uuids = app.config.active_support.use_rfc4122_namespaced_uuids
148
+ end
149
+ end
150
+ end
124
151
  end
125
152
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module RubyFeatures # :nodoc:
5
+ CLASS_SUBCLASSES = Class.method_defined?(:subclasses) # RUBY_VERSION >= "3.1"
6
+ end
7
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/per_thread_registry"
4
3
  require "active_support/notifications"
5
4
 
6
5
  module ActiveSupport
@@ -157,23 +156,8 @@ module ActiveSupport
157
156
 
158
157
  private
159
158
  def event_stack
160
- SubscriberQueueRegistry.instance.get_queue(@queue_key)
159
+ registry = ActiveSupport::IsolatedExecutionState[:active_support_subscriber_queue_registry] ||= {}
160
+ registry[@queue_key] ||= []
161
161
  end
162
162
  end
163
-
164
- # This is a registry for all the event stacks kept for subscribers.
165
- #
166
- # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
167
- # for further details.
168
- class SubscriberQueueRegistry # :nodoc:
169
- extend PerThreadRegistry
170
-
171
- def initialize
172
- @registry = {}
173
- end
174
-
175
- def get_queue(queue_key)
176
- @registry[queue_key] ||= []
177
- end
178
- end
179
163
  end
@@ -57,7 +57,7 @@ module ActiveSupport
57
57
  def current_tags
58
58
  # We use our object ID here to avoid conflicting with other instances
59
59
  thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
60
- Thread.current[thread_key] ||= []
60
+ IsolatedExecutionState[thread_key] ||= []
61
61
  end
62
62
 
63
63
  def tags_text
@@ -4,7 +4,30 @@ require "active_support/deprecation"
4
4
 
5
5
  module ActiveSupport
6
6
  module Testing
7
- module Deprecation # :nodoc:
7
+ module Deprecation
8
+ # Asserts that a matching deprecation warning was emitted by the given deprecator during the execution of the yielded block.
9
+ #
10
+ # assert_deprecated(/foo/, CustomDeprecator) do
11
+ # CustomDeprecator.warn "foo should no longer be used"
12
+ # end
13
+ #
14
+ # The +match+ object may be a +Regexp+, or +String+ appearing in the message.
15
+ #
16
+ # assert_deprecated('foo', CustomDeprecator) do
17
+ # CustomDeprecator.warn "foo should no longer be used"
18
+ # end
19
+ #
20
+ # If the +match+ is omitted (or explicitly +nil+), any deprecation warning will match.
21
+ #
22
+ # assert_deprecated(nil, CustomDeprecator) do
23
+ # CustomDeprecator.warn "foo should no longer be used"
24
+ # end
25
+ #
26
+ # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
27
+ #
28
+ # assert_deprecated do
29
+ # ActiveSupport::Deprecation.warn "foo should no longer be used"
30
+ # end
8
31
  def assert_deprecated(match = nil, deprecator = nil, &block)
9
32
  result, warnings = collect_deprecations(deprecator, &block)
10
33
  assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
@@ -15,12 +38,40 @@ module ActiveSupport
15
38
  result
16
39
  end
17
40
 
41
+ # Asserts that no deprecation warnings are emitted by the given deprecator during the execution of the yielded block.
42
+ #
43
+ # assert_not_deprecated(CustomDeprecator) do
44
+ # CustomDeprecator.warn "message" # fails assertion
45
+ # end
46
+ #
47
+ # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
48
+ #
49
+ # assert_not_deprecated do
50
+ # ActiveSupport::Deprecation.warn "message" # fails assertion
51
+ # end
52
+ #
53
+ # assert_not_deprecated do
54
+ # CustomDeprecator.warn "message" # passes assertion
55
+ # end
18
56
  def assert_not_deprecated(deprecator = nil, &block)
19
57
  result, deprecations = collect_deprecations(deprecator, &block)
20
58
  assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
21
59
  result
22
60
  end
23
61
 
62
+ # Returns an array of all the deprecation warnings emitted by the given
63
+ # +deprecator+ during the execution of the yielded block.
64
+ #
65
+ # collect_deprecations(CustomDeprecator) do
66
+ # CustomDeprecator.warn "message"
67
+ # end # => ["message"]
68
+ #
69
+ # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
70
+ #
71
+ # collect_deprecations do
72
+ # CustomDeprecator.warn "custom message"
73
+ # ActiveSupport::Deprecation.warn "message"
74
+ # end # => ["message"]
24
75
  def collect_deprecations(deprecator = nil)
25
76
  deprecator ||= ActiveSupport::Deprecation
26
77
  old_behavior = deprecator.behavior
@@ -63,7 +63,7 @@ module ActiveSupport
63
63
  module Subprocess
64
64
  ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
65
65
 
66
- # Crazy H4X to get this working in windows / jruby with
66
+ # Complicated H4X to get this working in windows / jruby with
67
67
  # no forking.
68
68
  def run_in_isolation(&blk)
69
69
  require "tempfile"
@@ -33,7 +33,7 @@ module ActiveSupport
33
33
  # t.dst? # => true
34
34
  # t.utc_offset # => -14400
35
35
  # t.zone # => "EDT"
36
- # t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
36
+ # t.to_formatted_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
37
37
  # t + 1.day # => Mon, 19 May 2008 13:27:25.031505668 EDT -04:00
38
38
  # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00.000000000 EST -05:00
39
39
  # t > Time.utc(1999) # => true
@@ -202,25 +202,53 @@ module ActiveSupport
202
202
  #
203
203
  # Time.zone.now.rfc2822 # => "Tue, 01 Jan 2013 04:51:39 +0000"
204
204
  def rfc2822
205
- to_s(:rfc822)
205
+ to_formatted_s(:rfc822)
206
206
  end
207
207
  alias_method :rfc822, :rfc2822
208
208
 
209
+ NOT_SET = Object.new # :nodoc:
210
+
211
+ # Returns a string of the object's date and time.
212
+ def to_s(format = NOT_SET)
213
+ if format == :db
214
+ ActiveSupport::Deprecation.warn(
215
+ "TimeWithZone#to_s(:db) is deprecated. Please use TimeWithZone#to_formatted_s(:db) instead."
216
+ )
217
+ utc.to_formatted_s(format)
218
+ elsif formatter = ::Time::DATE_FORMATS[format]
219
+ ActiveSupport::Deprecation.warn(
220
+ "TimeWithZone#to_s(#{format.inspect}) is deprecated. Please use TimeWithZone#to_formatted_s(#{format.inspect}) instead."
221
+ )
222
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
223
+ elsif format == NOT_SET
224
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
225
+ else
226
+ ActiveSupport::Deprecation.warn(
227
+ "TimeWithZone#to_s(#{format.inspect}) is deprecated. Please use TimeWithZone#to_formatted_s(#{format.inspect}) instead."
228
+ )
229
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
230
+ end
231
+ end
232
+
209
233
  # Returns a string of the object's date and time.
234
+ #
235
+ # This method is aliased to <tt>to_fs</tt>.
236
+ #
210
237
  # Accepts an optional <tt>format</tt>:
211
238
  # * <tt>:default</tt> - default value, mimics Ruby Time#to_s format.
212
239
  # * <tt>:db</tt> - format outputs time in UTC :db time. See Time#to_formatted_s(:db).
213
240
  # * Any key in <tt>Time::DATE_FORMATS</tt> can be used. See active_support/core_ext/time/conversions.rb.
214
- def to_s(format = :default)
241
+ def to_formatted_s(format = :default)
215
242
  if format == :db
216
- utc.to_s(format)
243
+ utc.to_formatted_s(format)
217
244
  elsif formatter = ::Time::DATE_FORMATS[format]
218
245
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
219
246
  else
220
- "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
247
+ # Change to to_s when deprecation is gone.
248
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}"
221
249
  end
222
250
  end
223
- alias_method :to_formatted_s, :to_s
251
+ alias_method :to_fs, :to_formatted_s
224
252
 
225
253
  # Replaces <tt>%Z</tt> directive with +zone before passing to Time#strftime,
226
254
  # so that zone information is correct.
@@ -385,6 +385,11 @@ module ActiveSupport
385
385
  # If the string is invalid then an +ArgumentError+ will be raised unlike +parse+
386
386
  # which usually returns +nil+ when given an invalid date string.
387
387
  def iso8601(str)
388
+ # Historically `Date._iso8601(nil)` returns `{}`, but in the `date` gem versions `3.2.1`, `3.1.2`, `3.0.2`,
389
+ # and `2.0.1`, `Date._iso8601(nil)` raises `TypeError` https://github.com/ruby/date/issues/39
390
+ # Future `date` releases are expected to revert back to the original behavior.
391
+ raise ArgumentError, "invalid date" if str.nil?
392
+
388
393
  parts = Date._iso8601(str)
389
394
 
390
395
  year = parts.fetch(:year)
@@ -54,7 +54,7 @@ module ActiveSupport
54
54
 
55
55
  FORMATTING = {
56
56
  "symbol" => Proc.new { |symbol| symbol.to_s },
57
- "date" => Proc.new { |date| date.to_s(:db) },
57
+ "date" => Proc.new { |date| date.to_formatted_s(:db) },
58
58
  "dateTime" => Proc.new { |time| time.xmlschema },
59
59
  "binary" => Proc.new { |binary| ::Base64.encode64(binary) },
60
60
  "yaml" => Proc.new { |yaml| yaml.to_yaml }
@@ -181,11 +181,11 @@ module ActiveSupport
181
181
  end
182
182
 
183
183
  def current_thread_backend
184
- Thread.current[:xml_mini_backend]
184
+ IsolatedExecutionState[:xml_mini_backend]
185
185
  end
186
186
 
187
187
  def current_thread_backend=(name)
188
- Thread.current[:xml_mini_backend] = name && cast_backend_name_to_module(name)
188
+ IsolatedExecutionState[:xml_mini_backend] = name && cast_backend_name_to_module(name)
189
189
  end
190
190
 
191
191
  def cast_backend_name_to_module(name)