activesupport 6.1.4.1 → 7.0.0.rc2

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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +201 -489
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +0 -2
  6. data/lib/active_support/benchmarkable.rb +2 -2
  7. data/lib/active_support/cache/file_store.rb +15 -9
  8. data/lib/active_support/cache/mem_cache_store.rb +127 -32
  9. data/lib/active_support/cache/memory_store.rb +23 -15
  10. data/lib/active_support/cache/null_store.rb +10 -2
  11. data/lib/active_support/cache/redis_cache_store.rb +42 -67
  12. data/lib/active_support/cache/strategy/local_cache.rb +35 -61
  13. data/lib/active_support/cache.rb +189 -45
  14. data/lib/active_support/callbacks.rb +180 -81
  15. data/lib/active_support/code_generator.rb +65 -0
  16. data/lib/active_support/concern.rb +5 -5
  17. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  18. data/lib/active_support/concurrency/share_lock.rb +2 -2
  19. data/lib/active_support/configurable.rb +6 -3
  20. data/lib/active_support/configuration_file.rb +1 -1
  21. data/lib/active_support/core_ext/array/access.rb +1 -5
  22. data/lib/active_support/core_ext/array/conversions.rb +9 -7
  23. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  24. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  25. data/lib/active_support/core_ext/array.rb +1 -0
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  27. data/lib/active_support/core_ext/class/subclasses.rb +4 -2
  28. data/lib/active_support/core_ext/date/blank.rb +1 -1
  29. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  30. data/lib/active_support/core_ext/date/conversions.rb +3 -3
  31. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  32. data/lib/active_support/core_ext/date.rb +1 -0
  33. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  34. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
  36. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  37. data/lib/active_support/core_ext/date_time.rb +1 -0
  38. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  39. data/lib/active_support/core_ext/enumerable.rb +64 -12
  40. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  41. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  42. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  43. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  44. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  45. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  46. data/lib/active_support/core_ext/name_error.rb +2 -8
  47. data/lib/active_support/core_ext/numeric/conversions.rb +79 -76
  48. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  49. data/lib/active_support/core_ext/numeric.rb +1 -0
  50. data/lib/active_support/core_ext/object/blank.rb +2 -2
  51. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  52. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  53. data/lib/active_support/core_ext/object/json.rb +29 -24
  54. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  55. data/lib/active_support/core_ext/object/try.rb +20 -20
  56. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  57. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  58. data/lib/active_support/core_ext/pathname.rb +3 -0
  59. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  60. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  61. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  62. data/lib/active_support/core_ext/range/each.rb +1 -1
  63. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -25
  64. data/lib/active_support/core_ext/range.rb +1 -1
  65. data/lib/active_support/core_ext/string/filters.rb +1 -1
  66. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  67. data/lib/active_support/core_ext/string/output_safety.rb +60 -36
  68. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  69. data/lib/active_support/core_ext/time/calculations.rb +7 -6
  70. data/lib/active_support/core_ext/time/conversions.rb +4 -3
  71. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  72. data/lib/active_support/core_ext/time/zones.rb +4 -19
  73. data/lib/active_support/core_ext/time.rb +1 -0
  74. data/lib/active_support/core_ext/uri.rb +3 -27
  75. data/lib/active_support/core_ext.rb +1 -0
  76. data/lib/active_support/current_attributes.rb +31 -14
  77. data/lib/active_support/dependencies/interlock.rb +10 -18
  78. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  79. data/lib/active_support/dependencies.rb +58 -788
  80. data/lib/active_support/deprecation/behaviors.rb +4 -1
  81. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  82. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  83. data/lib/active_support/deprecation.rb +1 -1
  84. data/lib/active_support/descendants_tracker.rb +177 -68
  85. data/lib/active_support/digest.rb +5 -3
  86. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  87. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  88. data/lib/active_support/duration.rb +77 -48
  89. data/lib/active_support/encrypted_configuration.rb +11 -1
  90. data/lib/active_support/encrypted_file.rb +1 -1
  91. data/lib/active_support/environment_inquirer.rb +1 -1
  92. data/lib/active_support/error_reporter.rb +117 -0
  93. data/lib/active_support/evented_file_update_checker.rb +1 -1
  94. data/lib/active_support/execution_context/test_helper.rb +13 -0
  95. data/lib/active_support/execution_context.rb +53 -0
  96. data/lib/active_support/execution_wrapper.rb +30 -4
  97. data/lib/active_support/executor/test_helper.rb +7 -0
  98. data/lib/active_support/fork_tracker.rb +19 -12
  99. data/lib/active_support/gem_version.rb +4 -4
  100. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  101. data/lib/active_support/html_safe_translation.rb +43 -0
  102. data/lib/active_support/i18n.rb +1 -0
  103. data/lib/active_support/i18n_railtie.rb +1 -1
  104. data/lib/active_support/inflector/inflections.rb +23 -7
  105. data/lib/active_support/inflector/methods.rb +24 -48
  106. data/lib/active_support/isolated_execution_state.rb +56 -0
  107. data/lib/active_support/json/encoding.rb +3 -3
  108. data/lib/active_support/key_generator.rb +18 -1
  109. data/lib/active_support/locale/en.yml +1 -1
  110. data/lib/active_support/log_subscriber.rb +13 -3
  111. data/lib/active_support/logger_thread_safe_level.rb +4 -13
  112. data/lib/active_support/message_encryptor.rb +8 -3
  113. data/lib/active_support/message_verifier.rb +4 -4
  114. data/lib/active_support/messages/metadata.rb +2 -2
  115. data/lib/active_support/multibyte/chars.rb +10 -11
  116. data/lib/active_support/multibyte/unicode.rb +0 -12
  117. data/lib/active_support/multibyte.rb +1 -1
  118. data/lib/active_support/notifications/fanout.rb +91 -65
  119. data/lib/active_support/notifications/instrumenter.rb +32 -15
  120. data/lib/active_support/notifications.rb +15 -21
  121. data/lib/active_support/number_helper/number_converter.rb +1 -3
  122. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  123. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  124. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  125. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  126. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  127. data/lib/active_support/number_helper.rb +0 -2
  128. data/lib/active_support/option_merger.rb +8 -16
  129. data/lib/active_support/ordered_hash.rb +1 -1
  130. data/lib/active_support/parameter_filter.rb +5 -0
  131. data/lib/active_support/per_thread_registry.rb +5 -0
  132. data/lib/active_support/railtie.rb +69 -19
  133. data/lib/active_support/rescuable.rb +2 -2
  134. data/lib/active_support/ruby_features.rb +7 -0
  135. data/lib/active_support/secure_compare_rotator.rb +1 -1
  136. data/lib/active_support/string_inquirer.rb +0 -2
  137. data/lib/active_support/subscriber.rb +7 -18
  138. data/lib/active_support/tagged_logging.rb +2 -2
  139. data/lib/active_support/test_case.rb +9 -21
  140. data/lib/active_support/testing/assertions.rb +35 -5
  141. data/lib/active_support/testing/deprecation.rb +52 -1
  142. data/lib/active_support/testing/isolation.rb +2 -2
  143. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  144. data/lib/active_support/testing/parallelization/server.rb +4 -0
  145. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  146. data/lib/active_support/testing/parallelization.rb +4 -0
  147. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  148. data/lib/active_support/testing/stream.rb +3 -5
  149. data/lib/active_support/testing/tagged_logging.rb +1 -1
  150. data/lib/active_support/testing/time_helpers.rb +13 -2
  151. data/lib/active_support/time_with_zone.rb +53 -12
  152. data/lib/active_support/values/time_zone.rb +30 -9
  153. data/lib/active_support/xml_mini/jdom.rb +1 -1
  154. data/lib/active_support/xml_mini/libxml.rb +5 -5
  155. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  156. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  157. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  158. data/lib/active_support/xml_mini/rexml.rb +1 -1
  159. data/lib/active_support/xml_mini.rb +5 -4
  160. data/lib/active_support.rb +17 -1
  161. metadata +27 -24
  162. data/lib/active_support/core_ext/marshal.rb +0 -26
  163. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -54,7 +54,7 @@ module ActiveSupport
54
54
  # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
55
55
  # [+log+] Log all deprecation warnings to +Rails.logger+.
56
56
  # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
57
- # [+silence+] Do nothing.
57
+ # [+silence+] Do nothing. On Rails, set <tt>config.active_support.report_deprecations = false</tt> to disable all behaviors.
58
58
  #
59
59
  # Setting behaviors only affects deprecations that happen after boot time.
60
60
  # For more information you can read the documentation of the +behavior=+ method.
@@ -93,6 +93,9 @@ module ActiveSupport
93
93
  # ActiveSupport::Deprecation.behavior = ->(message, callstack, deprecation_horizon, gem_name) {
94
94
  # # custom stuff
95
95
  # }
96
+ #
97
+ # If you are using Rails, you can set <tt>config.active_support.report_deprecations = false</tt> to disable
98
+ # all deprecation behaviors. This is similar to the +silence+ option but more performant.
96
99
  def behavior=(behavior)
97
100
  @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
98
101
  end
@@ -62,9 +62,9 @@ module ActiveSupport
62
62
  target_module.module_eval do
63
63
  redefine_method(method_name) do |*args, &block|
64
64
  deprecator.deprecation_warning(method_name, message)
65
- method.bind(self).call(*args, &block)
65
+ method.bind_call(self, *args, &block)
66
66
  end
67
- ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true)
67
+ ruby2_keywords(method_name)
68
68
  end
69
69
  else
70
70
  mod ||= Module.new
@@ -73,7 +73,7 @@ module ActiveSupport
73
73
  deprecator.deprecation_warning(method_name, message)
74
74
  super(*args, &block)
75
75
  end
76
- ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true)
76
+ ruby2_keywords(method_name)
77
77
  end
78
78
  end
79
79
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveSupport
4
4
  class Deprecation
5
- class DeprecationProxy #:nodoc:
5
+ class DeprecationProxy # :nodoc:
6
6
  def self.new(*args, &block)
7
7
  object = args.first
8
8
 
@@ -38,7 +38,7 @@ module ActiveSupport
38
38
  # and the second is a library name.
39
39
  #
40
40
  # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
41
- def initialize(deprecation_horizon = "6.2", gem_name = "Rails")
41
+ def initialize(deprecation_horizon = "7.1", gem_name = "Rails")
42
42
  self.gem_name = gem_name
43
43
  self.deprecation_horizon = deprecation_horizon
44
44
  # By default, warnings are not silenced and debugging is off.
@@ -1,110 +1,219 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "weakref"
4
+ require "active_support/ruby_features"
4
5
 
5
6
  module ActiveSupport
6
7
  # This module provides an internal implementation to track descendants
7
8
  # which is faster than iterating through ObjectSpace.
8
9
  module DescendantsTracker
9
- @@direct_descendants = {}
10
-
11
10
  class << self
12
11
  def direct_descendants(klass)
13
- descendants = @@direct_descendants[klass]
14
- descendants ? descendants.to_a : []
12
+ ActiveSupport::Deprecation.warn(<<~MSG)
13
+ ActiveSupport::DescendantsTracker.direct_descendants is deprecated and will be removed in Rails 7.1.
14
+ Use ActiveSupport::DescendantsTracker.subclasses instead.
15
+ MSG
16
+ subclasses(klass)
15
17
  end
16
- alias_method :subclasses, :direct_descendants
18
+ end
19
+
20
+ @clear_disabled = false
21
+
22
+ if RubyFeatures::CLASS_SUBCLASSES
23
+ @@excluded_descendants = if RUBY_ENGINE == "ruby"
24
+ # On MRI `ObjectSpace::WeakMap` keys are weak references.
25
+ # So we can simply use WeakMap as a `Set`.
26
+ ObjectSpace::WeakMap.new
27
+ else
28
+ # On TruffleRuby `ObjectSpace::WeakMap` keys are strong references.
29
+ # So we use `object_id` as a key and the actual object as a value.
30
+ #
31
+ # JRuby for now doesn't have Class#descendant, but when it will, it will likely
32
+ # have the same WeakMap semantic than Truffle so we future proof this as much as possible.
33
+ class WeakSet # :nodoc:
34
+ def initialize
35
+ @map = ObjectSpace::WeakMap.new
36
+ end
17
37
 
18
- def descendants(klass)
19
- arr = []
20
- accumulate_descendants(klass, arr)
21
- arr
38
+ def [](object)
39
+ @map.key?(object.object_id)
40
+ end
41
+
42
+ def []=(object, _present)
43
+ @map[object_id] = object
44
+ end
45
+ end
46
+ WeakSet.new
22
47
  end
23
48
 
24
- def clear
25
- if defined? ActiveSupport::Dependencies
26
- @@direct_descendants.each do |klass, descendants|
27
- if Dependencies.autoloaded?(klass)
28
- @@direct_descendants.delete(klass)
29
- else
30
- descendants.reject! { |v| Dependencies.autoloaded?(v) }
49
+ class << self
50
+ def disable_clear! # :nodoc:
51
+ unless @clear_disabled
52
+ @clear_disabled = true
53
+ remove_method(:subclasses)
54
+ remove_method(:descendants)
55
+ @@excluded_descendants = nil
56
+ end
57
+ end
58
+
59
+ def subclasses(klass)
60
+ klass.subclasses
61
+ end
62
+
63
+ def descendants(klass)
64
+ klass.descendants
65
+ end
66
+
67
+ def clear(classes) # :nodoc:
68
+ raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled
69
+
70
+ classes.each do |klass|
71
+ @@excluded_descendants[klass] = true
72
+ klass.descendants.each do |descendant|
73
+ @@excluded_descendants[descendant] = true
31
74
  end
32
75
  end
33
- else
34
- @@direct_descendants.clear
35
76
  end
77
+
78
+ def native? # :nodoc:
79
+ true
80
+ end
81
+ end
82
+
83
+ def subclasses
84
+ subclasses = super
85
+ subclasses.reject! { |d| @@excluded_descendants[d] }
86
+ subclasses
36
87
  end
37
88
 
38
- # This is the only method that is not thread safe, but is only ever called
39
- # during the eager loading phase.
40
- def store_inherited(klass, descendant)
41
- (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
89
+ def descendants
90
+ descendants = super
91
+ descendants.reject! { |d| @@excluded_descendants[d] }
92
+ descendants
42
93
  end
43
94
 
44
- private
45
- def accumulate_descendants(klass, acc)
46
- if direct_descendants = @@direct_descendants[klass]
47
- direct_descendants.each do |direct_descendant|
48
- acc << direct_descendant
49
- accumulate_descendants(direct_descendant, acc)
95
+ def direct_descendants
96
+ ActiveSupport::Deprecation.warn(<<~MSG)
97
+ ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1.
98
+ Use #subclasses instead.
99
+ MSG
100
+ subclasses
101
+ end
102
+ else
103
+ @@direct_descendants = {}
104
+
105
+ class << self
106
+ def disable_clear! # :nodoc:
107
+ @clear_disabled = true
108
+ end
109
+
110
+ def subclasses(klass)
111
+ descendants = @@direct_descendants[klass]
112
+ descendants ? descendants.to_a : []
113
+ end
114
+
115
+ def descendants(klass)
116
+ arr = []
117
+ accumulate_descendants(klass, arr)
118
+ arr
119
+ end
120
+
121
+ def clear(classes) # :nodoc:
122
+ raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled
123
+
124
+ @@direct_descendants.each do |klass, direct_descendants_of_klass|
125
+ if classes.member?(klass)
126
+ @@direct_descendants.delete(klass)
127
+ else
128
+ direct_descendants_of_klass.reject! do |direct_descendant_of_class|
129
+ classes.member?(direct_descendant_of_class)
130
+ end
50
131
  end
51
132
  end
52
133
  end
53
- end
54
134
 
55
- def inherited(base)
56
- DescendantsTracker.store_inherited(self, base)
57
- super
58
- end
135
+ def native? # :nodoc:
136
+ false
137
+ end
59
138
 
60
- def direct_descendants
61
- DescendantsTracker.direct_descendants(self)
62
- end
63
- alias_method :subclasses, :direct_descendants
139
+ # This is the only method that is not thread safe, but is only ever called
140
+ # during the eager loading phase.
141
+ def store_inherited(klass, descendant)
142
+ (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
143
+ end
64
144
 
65
- def descendants
66
- DescendantsTracker.descendants(self)
67
- end
145
+ private
146
+ def accumulate_descendants(klass, acc)
147
+ if direct_descendants = @@direct_descendants[klass]
148
+ direct_descendants.each do |direct_descendant|
149
+ acc << direct_descendant
150
+ accumulate_descendants(direct_descendant, acc)
151
+ end
152
+ end
153
+ end
154
+ end
68
155
 
69
- # DescendantsArray is an array that contains weak references to classes.
70
- class DescendantsArray # :nodoc:
71
- include Enumerable
156
+ def inherited(base)
157
+ DescendantsTracker.store_inherited(self, base)
158
+ super
159
+ end
72
160
 
73
- def initialize
74
- @refs = []
161
+ def direct_descendants
162
+ ActiveSupport::Deprecation.warn(<<~MSG)
163
+ ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1.
164
+ Use #subclasses instead.
165
+ MSG
166
+ DescendantsTracker.subclasses(self)
75
167
  end
76
168
 
77
- def initialize_copy(orig)
78
- @refs = @refs.dup
169
+ def subclasses
170
+ DescendantsTracker.subclasses(self)
79
171
  end
80
172
 
81
- def <<(klass)
82
- @refs << WeakRef.new(klass)
173
+ def descendants
174
+ DescendantsTracker.descendants(self)
83
175
  end
84
176
 
85
- def each
86
- @refs.reject! do |ref|
87
- yield ref.__getobj__
88
- false
89
- rescue WeakRef::RefError
90
- true
177
+ # DescendantsArray is an array that contains weak references to classes.
178
+ class DescendantsArray # :nodoc:
179
+ include Enumerable
180
+
181
+ def initialize
182
+ @refs = []
91
183
  end
92
- self
93
- end
94
184
 
95
- def refs_size
96
- @refs.size
97
- end
185
+ def initialize_copy(orig)
186
+ @refs = @refs.dup
187
+ end
98
188
 
99
- def cleanup!
100
- @refs.delete_if { |ref| !ref.weakref_alive? }
101
- end
189
+ def <<(klass)
190
+ @refs << WeakRef.new(klass)
191
+ end
102
192
 
103
- def reject!
104
- @refs.reject! do |ref|
105
- yield ref.__getobj__
106
- rescue WeakRef::RefError
107
- true
193
+ def each
194
+ @refs.reject! do |ref|
195
+ yield ref.__getobj__
196
+ false
197
+ rescue WeakRef::RefError
198
+ true
199
+ end
200
+ self
201
+ end
202
+
203
+ def refs_size
204
+ @refs.size
205
+ end
206
+
207
+ def cleanup!
208
+ @refs.delete_if { |ref| !ref.weakref_alive? }
209
+ end
210
+
211
+ def reject!
212
+ @refs.reject! do |ref|
213
+ yield ref.__getobj__
214
+ rescue WeakRef::RefError
215
+ true
216
+ end
108
217
  end
109
218
  end
110
219
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "openssl"
4
+
3
5
  module ActiveSupport
4
- class Digest #:nodoc:
5
- class <<self
6
+ class Digest # :nodoc:
7
+ class << self
6
8
  def hash_digest_class
7
- @hash_digest_class ||= ::Digest::MD5
9
+ @hash_digest_class ||= OpenSSL::Digest::MD5
8
10
  end
9
11
 
10
12
  def hash_digest_class=(klass)
@@ -16,11 +16,11 @@ module ActiveSupport
16
16
  PERIOD = "."
17
17
  COMMA = ","
18
18
 
19
- SIGN_MARKER = /\A\-|\+|/
19
+ SIGN_MARKER = /\A-|\+|/
20
20
  DATE_MARKER = /P/
21
21
  TIME_MARKER = /T/
22
- DATE_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(Y|M|D|W)/
23
- TIME_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(H|M|S)/
22
+ DATE_COMPONENT = /(-?\d+(?:[.,]\d+)?)(Y|M|D|W)/
23
+ TIME_COMPONENT = /(-?\d+(?:[.,]\d+)?)(H|M|S)/
24
24
 
25
25
  DATE_TO_PART = { "Y" => :years, "M" => :months, "W" => :weeks, "D" => :days }
26
26
  TIME_TO_PART = { "H" => :hours, "M" => :minutes, "S" => :seconds }
@@ -27,7 +27,7 @@ module ActiveSupport
27
27
  time << "#{parts[:hours]}H" if parts.key?(:hours)
28
28
  time << "#{parts[:minutes]}M" if parts.key?(:minutes)
29
29
  if parts.key?(:seconds)
30
- time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
30
+ time << "#{format_seconds(parts[:seconds])}S"
31
31
  end
32
32
  output << "T#{time}" unless time.empty?
33
33
  output
@@ -54,6 +54,14 @@ module ActiveSupport
54
54
  def week_mixed_with_date?(parts)
55
55
  parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
56
56
  end
57
+
58
+ def format_seconds(seconds)
59
+ if @precision
60
+ sprintf("%0.0#{@precision}f", seconds)
61
+ else
62
+ seconds.to_s
63
+ end
64
+ end
57
65
  end
58
66
  end
59
67
  end