activesupport 6.1.4.1 → 7.0.0.alpha1

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +151 -526
  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 +119 -28
  9. data/lib/active_support/cache/memory_store.rb +21 -13
  10. data/lib/active_support/cache/null_store.rb +10 -2
  11. data/lib/active_support/cache/redis_cache_store.rb +39 -59
  12. data/lib/active_support/cache/strategy/local_cache.rb +29 -49
  13. data/lib/active_support/cache.rb +189 -45
  14. data/lib/active_support/callbacks.rb +35 -31
  15. data/lib/active_support/concern.rb +5 -5
  16. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  17. data/lib/active_support/concurrency/share_lock.rb +2 -2
  18. data/lib/active_support/configurable.rb +6 -3
  19. data/lib/active_support/configuration_file.rb +1 -1
  20. data/lib/active_support/core_ext/array/access.rb +1 -5
  21. data/lib/active_support/core_ext/array/conversions.rb +6 -6
  22. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  23. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  24. data/lib/active_support/core_ext/date/blank.rb +1 -1
  25. data/lib/active_support/core_ext/date/calculations.rb +2 -2
  26. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  27. data/lib/active_support/core_ext/digest/uuid.rb +13 -12
  28. data/lib/active_support/core_ext/enumerable.rb +64 -12
  29. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  30. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  31. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  32. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  33. data/lib/active_support/core_ext/name_error.rb +2 -8
  34. data/lib/active_support/core_ext/numeric/conversions.rb +2 -2
  35. data/lib/active_support/core_ext/object/blank.rb +2 -2
  36. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  37. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  38. data/lib/active_support/core_ext/object/json.rb +29 -24
  39. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  40. data/lib/active_support/core_ext/object/try.rb +20 -20
  41. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  42. data/lib/active_support/core_ext/range/each.rb +1 -1
  43. data/lib/active_support/core_ext/range/include_time_with_zone.rb +1 -1
  44. data/lib/active_support/core_ext/string/filters.rb +1 -1
  45. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  46. data/lib/active_support/core_ext/string/output_safety.rb +60 -36
  47. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  48. data/lib/active_support/core_ext/time/calculations.rb +6 -5
  49. data/lib/active_support/core_ext/time/zones.rb +2 -17
  50. data/lib/active_support/core_ext/uri.rb +1 -15
  51. data/lib/active_support/current_attributes.rb +17 -1
  52. data/lib/active_support/dependencies/interlock.rb +10 -18
  53. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  54. data/lib/active_support/dependencies.rb +58 -788
  55. data/lib/active_support/deprecation/behaviors.rb +4 -1
  56. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  57. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  58. data/lib/active_support/deprecation.rb +1 -1
  59. data/lib/active_support/descendants_tracker.rb +12 -9
  60. data/lib/active_support/digest.rb +5 -3
  61. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  62. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  63. data/lib/active_support/duration.rb +77 -48
  64. data/lib/active_support/encrypted_configuration.rb +11 -1
  65. data/lib/active_support/encrypted_file.rb +1 -1
  66. data/lib/active_support/environment_inquirer.rb +1 -1
  67. data/lib/active_support/evented_file_update_checker.rb +1 -1
  68. data/lib/active_support/fork_tracker.rb +2 -4
  69. data/lib/active_support/gem_version.rb +4 -4
  70. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  71. data/lib/active_support/i18n.rb +1 -0
  72. data/lib/active_support/inflector/inflections.rb +11 -4
  73. data/lib/active_support/inflector/methods.rb +23 -47
  74. data/lib/active_support/json/encoding.rb +3 -3
  75. data/lib/active_support/key_generator.rb +18 -1
  76. data/lib/active_support/locale/en.yml +1 -1
  77. data/lib/active_support/log_subscriber.rb +13 -3
  78. data/lib/active_support/logger_thread_safe_level.rb +5 -13
  79. data/lib/active_support/message_encryptor.rb +3 -3
  80. data/lib/active_support/message_verifier.rb +4 -4
  81. data/lib/active_support/messages/metadata.rb +2 -2
  82. data/lib/active_support/multibyte/chars.rb +10 -11
  83. data/lib/active_support/multibyte/unicode.rb +2 -2
  84. data/lib/active_support/multibyte.rb +1 -1
  85. data/lib/active_support/notifications/fanout.rb +31 -11
  86. data/lib/active_support/notifications/instrumenter.rb +17 -0
  87. data/lib/active_support/notifications.rb +10 -0
  88. data/lib/active_support/number_helper/number_converter.rb +1 -3
  89. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  90. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  91. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  92. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  93. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  94. data/lib/active_support/number_helper.rb +0 -2
  95. data/lib/active_support/option_merger.rb +4 -16
  96. data/lib/active_support/ordered_hash.rb +1 -1
  97. data/lib/active_support/parameter_filter.rb +5 -0
  98. data/lib/active_support/per_thread_registry.rb +1 -0
  99. data/lib/active_support/railtie.rb +34 -11
  100. data/lib/active_support/rescuable.rb +2 -2
  101. data/lib/active_support/secure_compare_rotator.rb +1 -1
  102. data/lib/active_support/string_inquirer.rb +0 -2
  103. data/lib/active_support/subscriber.rb +5 -0
  104. data/lib/active_support/tagged_logging.rb +1 -1
  105. data/lib/active_support/test_case.rb +9 -21
  106. data/lib/active_support/testing/assertions.rb +35 -5
  107. data/lib/active_support/testing/deprecation.rb +1 -1
  108. data/lib/active_support/testing/isolation.rb +1 -1
  109. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  110. data/lib/active_support/testing/parallelization/server.rb +4 -0
  111. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  112. data/lib/active_support/testing/parallelization.rb +4 -0
  113. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  114. data/lib/active_support/testing/stream.rb +3 -5
  115. data/lib/active_support/testing/tagged_logging.rb +1 -1
  116. data/lib/active_support/testing/time_helpers.rb +13 -2
  117. data/lib/active_support/time_with_zone.rb +19 -6
  118. data/lib/active_support/values/time_zone.rb +25 -9
  119. data/lib/active_support/xml_mini/jdom.rb +1 -1
  120. data/lib/active_support/xml_mini/libxml.rb +5 -5
  121. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  122. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  123. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  124. data/lib/active_support/xml_mini/rexml.rb +1 -1
  125. data/lib/active_support/xml_mini.rb +2 -1
  126. data/lib/active_support.rb +14 -1
  127. metadata +14 -28
  128. data/lib/active_support/core_ext/marshal.rb +0 -26
  129. 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.
@@ -21,17 +21,20 @@ module ActiveSupport
21
21
  arr
22
22
  end
23
23
 
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) }
24
+ def clear(only: nil)
25
+ if only.nil?
26
+ @@direct_descendants.clear
27
+ return
28
+ end
29
+
30
+ @@direct_descendants.each do |klass, direct_descendants_of_klass|
31
+ if only.member?(klass)
32
+ @@direct_descendants.delete(klass)
33
+ else
34
+ direct_descendants_of_klass.reject! do |direct_descendant_of_class|
35
+ only.member?(direct_descendant_of_class)
31
36
  end
32
37
  end
33
- else
34
- @@direct_descendants.clear
35
38
  end
36
39
  end
37
40
 
@@ -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
@@ -11,7 +11,7 @@ module ActiveSupport
11
11
  #
12
12
  # 1.month.ago # equivalent to Time.now.advance(months: -1)
13
13
  class Duration
14
- class Scalar < Numeric #:nodoc:
14
+ class Scalar < Numeric # :nodoc:
15
15
  attr_reader :value
16
16
  delegate :to_i, :to_f, :to_s, to: :value
17
17
 
@@ -39,11 +39,11 @@ module ActiveSupport
39
39
 
40
40
  def +(other)
41
41
  if Duration === other
42
- seconds = value + other.parts.fetch(:seconds, 0)
43
- new_parts = other.parts.merge(seconds: seconds)
42
+ seconds = value + other._parts.fetch(:seconds, 0)
43
+ new_parts = other._parts.merge(seconds: seconds)
44
44
  new_value = value + other.value
45
45
 
46
- Duration.new(new_value, new_parts)
46
+ Duration.new(new_value, new_parts, other.variable?)
47
47
  else
48
48
  calculate(:+, other)
49
49
  end
@@ -51,12 +51,12 @@ module ActiveSupport
51
51
 
52
52
  def -(other)
53
53
  if Duration === other
54
- seconds = value - other.parts.fetch(:seconds, 0)
55
- new_parts = other.parts.transform_values(&:-@)
54
+ seconds = value - other._parts.fetch(:seconds, 0)
55
+ new_parts = other._parts.transform_values(&:-@)
56
56
  new_parts = new_parts.merge(seconds: seconds)
57
57
  new_value = value - other.value
58
58
 
59
- Duration.new(new_value, new_parts)
59
+ Duration.new(new_value, new_parts, other.variable?)
60
60
  else
61
61
  calculate(:-, other)
62
62
  end
@@ -64,10 +64,10 @@ module ActiveSupport
64
64
 
65
65
  def *(other)
66
66
  if Duration === other
67
- new_parts = other.parts.transform_values { |other_value| value * other_value }
67
+ new_parts = other._parts.transform_values { |other_value| value * other_value }
68
68
  new_value = value * other.value
69
69
 
70
- Duration.new(new_value, new_parts)
70
+ Duration.new(new_value, new_parts, other.variable?)
71
71
  else
72
72
  calculate(:*, other)
73
73
  end
@@ -89,6 +89,10 @@ module ActiveSupport
89
89
  end
90
90
  end
91
91
 
92
+ def variable? # :nodoc:
93
+ false
94
+ end
95
+
92
96
  private
93
97
  def calculate(op, other)
94
98
  if Scalar === other
@@ -123,8 +127,9 @@ module ActiveSupport
123
127
  }.freeze
124
128
 
125
129
  PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
130
+ VARIABLE_PARTS = [:years, :months, :weeks, :days].freeze
126
131
 
127
- attr_accessor :value, :parts
132
+ attr_reader :value
128
133
 
129
134
  autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
130
135
  autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
@@ -140,38 +145,38 @@ module ActiveSupport
140
145
  new(calculate_total_seconds(parts), parts)
141
146
  end
142
147
 
143
- def ===(other) #:nodoc:
148
+ def ===(other) # :nodoc:
144
149
  other.is_a?(Duration)
145
150
  rescue ::NoMethodError
146
151
  false
147
152
  end
148
153
 
149
- def seconds(value) #:nodoc:
150
- new(value, seconds: value)
154
+ def seconds(value) # :nodoc:
155
+ new(value, { seconds: value }, false)
151
156
  end
152
157
 
153
- def minutes(value) #:nodoc:
154
- new(value * SECONDS_PER_MINUTE, minutes: value)
158
+ def minutes(value) # :nodoc:
159
+ new(value * SECONDS_PER_MINUTE, { minutes: value }, false)
155
160
  end
156
161
 
157
- def hours(value) #:nodoc:
158
- new(value * SECONDS_PER_HOUR, hours: value)
162
+ def hours(value) # :nodoc:
163
+ new(value * SECONDS_PER_HOUR, { hours: value }, false)
159
164
  end
160
165
 
161
- def days(value) #:nodoc:
162
- new(value * SECONDS_PER_DAY, days: value)
166
+ def days(value) # :nodoc:
167
+ new(value * SECONDS_PER_DAY, { days: value }, true)
163
168
  end
164
169
 
165
- def weeks(value) #:nodoc:
166
- new(value * SECONDS_PER_WEEK, weeks: value)
170
+ def weeks(value) # :nodoc:
171
+ new(value * SECONDS_PER_WEEK, { weeks: value }, true)
167
172
  end
168
173
 
169
- def months(value) #:nodoc:
170
- new(value * SECONDS_PER_MONTH, months: value)
174
+ def months(value) # :nodoc:
175
+ new(value * SECONDS_PER_MONTH, { months: value }, true)
171
176
  end
172
177
 
173
- def years(value) #:nodoc:
174
- new(value * SECONDS_PER_YEAR, years: value)
178
+ def years(value) # :nodoc:
179
+ new(value * SECONDS_PER_YEAR, { years: value }, true)
175
180
  end
176
181
 
177
182
  # Creates a new Duration from a seconds value that is converted
@@ -187,18 +192,23 @@ module ActiveSupport
187
192
 
188
193
  parts = {}
189
194
  remainder = value.round(9)
195
+ variable = false
190
196
 
191
197
  PARTS.each do |part|
192
198
  unless part == :seconds
193
199
  part_in_seconds = PARTS_IN_SECONDS[part]
194
200
  parts[part] = remainder.div(part_in_seconds)
195
201
  remainder %= part_in_seconds
202
+
203
+ unless parts[part].zero?
204
+ variable ||= VARIABLE_PARTS.include?(part)
205
+ end
196
206
  end
197
207
  end unless value == 0
198
208
 
199
209
  parts[:seconds] = remainder
200
210
 
201
- new(value, parts)
211
+ new(value, parts, variable)
202
212
  end
203
213
 
204
214
  private
@@ -209,12 +219,23 @@ module ActiveSupport
209
219
  end
210
220
  end
211
221
 
212
- def initialize(value, parts) #:nodoc:
222
+ def initialize(value, parts, variable = nil) # :nodoc:
213
223
  @value, @parts = value, parts
214
224
  @parts.reject! { |k, v| v.zero? } unless value == 0
225
+ @parts.freeze
226
+ @variable = variable
227
+
228
+ if @variable.nil?
229
+ @variable = @parts.any? { |part, _| VARIABLE_PARTS.include?(part) }
230
+ end
215
231
  end
216
232
 
217
- def coerce(other) #:nodoc:
233
+ # Returns a copy of the parts hash that defines the duration
234
+ def parts
235
+ @parts.dup
236
+ end
237
+
238
+ def coerce(other) # :nodoc:
218
239
  case other
219
240
  when Scalar
220
241
  [other, self]
@@ -239,13 +260,13 @@ module ActiveSupport
239
260
  # are treated as seconds.
240
261
  def +(other)
241
262
  if Duration === other
242
- parts = @parts.merge(other.parts) do |_key, value, other_value|
263
+ parts = @parts.merge(other._parts) do |_key, value, other_value|
243
264
  value + other_value
244
265
  end
245
- Duration.new(value + other.value, parts)
266
+ Duration.new(value + other.value, parts, @variable || other.variable?)
246
267
  else
247
268
  seconds = @parts.fetch(:seconds, 0) + other
248
- Duration.new(value + other, @parts.merge(seconds: seconds))
269
+ Duration.new(value + other, @parts.merge(seconds: seconds), @variable)
249
270
  end
250
271
  end
251
272
 
@@ -258,9 +279,9 @@ module ActiveSupport
258
279
  # Multiplies this Duration by a Numeric and returns a new Duration.
259
280
  def *(other)
260
281
  if Scalar === other || Duration === other
261
- Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
282
+ Duration.new(value * other.value, @parts.transform_values { |number| number * other.value }, @variable || other.variable?)
262
283
  elsif Numeric === other
263
- Duration.new(value * other, parts.transform_values { |number| number * other })
284
+ Duration.new(value * other, @parts.transform_values { |number| number * other }, @variable)
264
285
  else
265
286
  raise_type_error(other)
266
287
  end
@@ -269,11 +290,11 @@ module ActiveSupport
269
290
  # Divides this Duration by a Numeric and returns a new Duration.
270
291
  def /(other)
271
292
  if Scalar === other
272
- Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
293
+ Duration.new(value / other.value, @parts.transform_values { |number| number / other.value }, @variable)
273
294
  elsif Duration === other
274
295
  value / other.value
275
296
  elsif Numeric === other
276
- Duration.new(value / other, parts.transform_values { |number| number / other })
297
+ Duration.new(value / other, @parts.transform_values { |number| number / other }, @variable)
277
298
  else
278
299
  raise_type_error(other)
279
300
  end
@@ -291,15 +312,15 @@ module ActiveSupport
291
312
  end
292
313
  end
293
314
 
294
- def -@ #:nodoc:
295
- Duration.new(-value, parts.transform_values(&:-@))
315
+ def -@ # :nodoc:
316
+ Duration.new(-value, @parts.transform_values(&:-@), @variable)
296
317
  end
297
318
 
298
- def +@ #:nodoc:
319
+ def +@ # :nodoc:
299
320
  self
300
321
  end
301
322
 
302
- def is_a?(klass) #:nodoc:
323
+ def is_a?(klass) # :nodoc:
303
324
  Duration == klass || value.is_a?(klass)
304
325
  end
305
326
  alias :kind_of? :is_a?
@@ -419,24 +440,24 @@ module ActiveSupport
419
440
  alias :until :ago
420
441
  alias :before :ago
421
442
 
422
- def inspect #:nodoc:
423
- return "#{value} seconds" if parts.empty?
443
+ def inspect # :nodoc:
444
+ return "#{value} seconds" if @parts.empty?
424
445
 
425
- parts.
446
+ @parts.
426
447
  sort_by { |unit, _ | PARTS.index(unit) }.
427
448
  map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
428
- to_sentence(locale: ::I18n.default_locale)
449
+ to_sentence(locale: false)
429
450
  end
430
451
 
431
- def as_json(options = nil) #:nodoc:
452
+ def as_json(options = nil) # :nodoc:
432
453
  to_i
433
454
  end
434
455
 
435
- def init_with(coder) #:nodoc:
456
+ def init_with(coder) # :nodoc:
436
457
  initialize(coder["value"], coder["parts"])
437
458
  end
438
459
 
439
- def encode_with(coder) #:nodoc:
460
+ def encode_with(coder) # :nodoc:
440
461
  coder.map = { "value" => @value, "parts" => @parts }
441
462
  end
442
463
 
@@ -446,16 +467,24 @@ module ActiveSupport
446
467
  ISO8601Serializer.new(self, precision: precision).serialize
447
468
  end
448
469
 
470
+ def variable? # :nodoc:
471
+ @variable
472
+ end
473
+
474
+ def _parts # :nodoc:
475
+ @parts
476
+ end
477
+
449
478
  private
450
479
  def sum(sign, time = ::Time.current)
451
480
  unless time.acts_like?(:time) || time.acts_like?(:date)
452
481
  raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
453
482
  end
454
483
 
455
- if parts.empty?
484
+ if @parts.empty?
456
485
  time.since(sign * value)
457
486
  else
458
- parts.inject(time) do |t, (type, number)|
487
+ @parts.inject(time) do |t, (type, number)|
459
488
  if type == :seconds
460
489
  t.since(sign * number)
461
490
  elsif type == :minutes
@@ -34,8 +34,18 @@ module ActiveSupport
34
34
  end
35
35
 
36
36
  private
37
+ def deep_transform(hash)
38
+ return hash unless hash.is_a?(Hash)
39
+
40
+ h = ActiveSupport::InheritableOptions.new
41
+ hash.each do |k, v|
42
+ h[k] = deep_transform(v)
43
+ end
44
+ h
45
+ end
46
+
37
47
  def options
38
- @options ||= ActiveSupport::InheritableOptions.new(config)
48
+ @options ||= ActiveSupport::InheritableOptions.new(deep_transform(config))
39
49
  end
40
50
 
41
51
  def deserialize(config)