activesupport 5.2.4.4 → 6.1.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +353 -435
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support.rb +14 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache.rb +142 -78
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +32 -20
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +70 -43
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +70 -3
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/enumerable.rb +171 -75
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +14 -2
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +34 -13
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +70 -13
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +6 -1
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/dependencies.rb +109 -34
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/deprecation/behaviors.rb +16 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +22 -4
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +64 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +49 -58
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +50 -6
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/rescuable.rb +4 -4
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +72 -28
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +30 -9
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +51 -0
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +32 -17
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +10 -3
- metadata +58 -32
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "weakref"
|
4
|
+
|
3
5
|
module ActiveSupport
|
4
6
|
# This module provides an internal implementation to track descendants
|
5
7
|
# which is faster than iterating through ObjectSpace.
|
@@ -8,8 +10,10 @@ module ActiveSupport
|
|
8
10
|
|
9
11
|
class << self
|
10
12
|
def direct_descendants(klass)
|
11
|
-
@@direct_descendants[klass]
|
13
|
+
descendants = @@direct_descendants[klass]
|
14
|
+
descendants ? descendants.to_a : []
|
12
15
|
end
|
16
|
+
alias_method :subclasses, :direct_descendants
|
13
17
|
|
14
18
|
def descendants(klass)
|
15
19
|
arr = []
|
@@ -20,10 +24,10 @@ module ActiveSupport
|
|
20
24
|
def clear
|
21
25
|
if defined? ActiveSupport::Dependencies
|
22
26
|
@@direct_descendants.each do |klass, descendants|
|
23
|
-
if
|
27
|
+
if Dependencies.autoloaded?(klass)
|
24
28
|
@@direct_descendants.delete(klass)
|
25
29
|
else
|
26
|
-
descendants.reject! { |v|
|
30
|
+
descendants.reject! { |v| Dependencies.autoloaded?(v) }
|
27
31
|
end
|
28
32
|
end
|
29
33
|
else
|
@@ -34,16 +38,18 @@ module ActiveSupport
|
|
34
38
|
# This is the only method that is not thread safe, but is only ever called
|
35
39
|
# during the eager loading phase.
|
36
40
|
def store_inherited(klass, descendant)
|
37
|
-
(@@direct_descendants[klass] ||=
|
41
|
+
(@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
|
38
42
|
end
|
39
43
|
|
40
44
|
private
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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)
|
50
|
+
end
|
51
|
+
end
|
45
52
|
end
|
46
|
-
end
|
47
53
|
end
|
48
54
|
|
49
55
|
def inherited(base)
|
@@ -54,9 +60,53 @@ module ActiveSupport
|
|
54
60
|
def direct_descendants
|
55
61
|
DescendantsTracker.direct_descendants(self)
|
56
62
|
end
|
63
|
+
alias_method :subclasses, :direct_descendants
|
57
64
|
|
58
65
|
def descendants
|
59
66
|
DescendantsTracker.descendants(self)
|
60
67
|
end
|
68
|
+
|
69
|
+
# DescendantsArray is an array that contains weak references to classes.
|
70
|
+
class DescendantsArray # :nodoc:
|
71
|
+
include Enumerable
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@refs = []
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize_copy(orig)
|
78
|
+
@refs = @refs.dup
|
79
|
+
end
|
80
|
+
|
81
|
+
def <<(klass)
|
82
|
+
@refs << WeakRef.new(klass)
|
83
|
+
end
|
84
|
+
|
85
|
+
def each
|
86
|
+
@refs.reject! do |ref|
|
87
|
+
yield ref.__getobj__
|
88
|
+
false
|
89
|
+
rescue WeakRef::RefError
|
90
|
+
true
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def refs_size
|
96
|
+
@refs.size
|
97
|
+
end
|
98
|
+
|
99
|
+
def cleanup!
|
100
|
+
@refs.delete_if { |ref| !ref.weakref_alive? }
|
101
|
+
end
|
102
|
+
|
103
|
+
def reject!
|
104
|
+
@refs.reject! do |ref|
|
105
|
+
yield ref.__getobj__
|
106
|
+
rescue WeakRef::RefError
|
107
|
+
true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
61
111
|
end
|
62
112
|
end
|
@@ -4,7 +4,6 @@ require "active_support/core_ext/array/conversions"
|
|
4
4
|
require "active_support/core_ext/module/delegation"
|
5
5
|
require "active_support/core_ext/object/acts_like"
|
6
6
|
require "active_support/core_ext/string/filters"
|
7
|
-
require "active_support/deprecation"
|
8
7
|
|
9
8
|
module ActiveSupport
|
10
9
|
# Provides accurate date and time measurements using Date#advance and
|
@@ -40,7 +39,7 @@ module ActiveSupport
|
|
40
39
|
|
41
40
|
def +(other)
|
42
41
|
if Duration === other
|
43
|
-
seconds = value + other.parts
|
42
|
+
seconds = value + other.parts.fetch(:seconds, 0)
|
44
43
|
new_parts = other.parts.merge(seconds: seconds)
|
45
44
|
new_value = value + other.value
|
46
45
|
|
@@ -52,8 +51,8 @@ module ActiveSupport
|
|
52
51
|
|
53
52
|
def -(other)
|
54
53
|
if Duration === other
|
55
|
-
seconds = value - other.parts
|
56
|
-
new_parts = other.parts.
|
54
|
+
seconds = value - other.parts.fetch(:seconds, 0)
|
55
|
+
new_parts = other.parts.transform_values(&:-@)
|
57
56
|
new_parts = new_parts.merge(seconds: seconds)
|
58
57
|
new_value = value - other.value
|
59
58
|
|
@@ -65,7 +64,7 @@ module ActiveSupport
|
|
65
64
|
|
66
65
|
def *(other)
|
67
66
|
if Duration === other
|
68
|
-
new_parts = other.parts.
|
67
|
+
new_parts = other.parts.transform_values { |other_value| value * other_value }
|
69
68
|
new_value = value * other.value
|
70
69
|
|
71
70
|
Duration.new(new_value, new_parts)
|
@@ -148,31 +147,31 @@ module ActiveSupport
|
|
148
147
|
end
|
149
148
|
|
150
149
|
def seconds(value) #:nodoc:
|
151
|
-
new(value,
|
150
|
+
new(value, seconds: value)
|
152
151
|
end
|
153
152
|
|
154
153
|
def minutes(value) #:nodoc:
|
155
|
-
new(value * SECONDS_PER_MINUTE,
|
154
|
+
new(value * SECONDS_PER_MINUTE, minutes: value)
|
156
155
|
end
|
157
156
|
|
158
157
|
def hours(value) #:nodoc:
|
159
|
-
new(value * SECONDS_PER_HOUR,
|
158
|
+
new(value * SECONDS_PER_HOUR, hours: value)
|
160
159
|
end
|
161
160
|
|
162
161
|
def days(value) #:nodoc:
|
163
|
-
new(value * SECONDS_PER_DAY,
|
162
|
+
new(value * SECONDS_PER_DAY, days: value)
|
164
163
|
end
|
165
164
|
|
166
165
|
def weeks(value) #:nodoc:
|
167
|
-
new(value * SECONDS_PER_WEEK,
|
166
|
+
new(value * SECONDS_PER_WEEK, weeks: value)
|
168
167
|
end
|
169
168
|
|
170
169
|
def months(value) #:nodoc:
|
171
|
-
new(value * SECONDS_PER_MONTH,
|
170
|
+
new(value * SECONDS_PER_MONTH, months: value)
|
172
171
|
end
|
173
172
|
|
174
173
|
def years(value) #:nodoc:
|
175
|
-
new(value * SECONDS_PER_YEAR,
|
174
|
+
new(value * SECONDS_PER_YEAR, years: value)
|
176
175
|
end
|
177
176
|
|
178
177
|
# Creates a new Duration from a seconds value that is converted
|
@@ -182,16 +181,20 @@ module ActiveSupport
|
|
182
181
|
# ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
|
183
182
|
#
|
184
183
|
def build(value)
|
184
|
+
unless value.is_a?(::Numeric)
|
185
|
+
raise TypeError, "can't build an #{self.name} from a #{value.class.name}"
|
186
|
+
end
|
187
|
+
|
185
188
|
parts = {}
|
186
|
-
remainder = value.
|
189
|
+
remainder = value.round(9)
|
187
190
|
|
188
191
|
PARTS.each do |part|
|
189
192
|
unless part == :seconds
|
190
193
|
part_in_seconds = PARTS_IN_SECONDS[part]
|
191
194
|
parts[part] = remainder.div(part_in_seconds)
|
192
|
-
remainder
|
195
|
+
remainder %= part_in_seconds
|
193
196
|
end
|
194
|
-
end
|
197
|
+
end unless value == 0
|
195
198
|
|
196
199
|
parts[:seconds] = remainder
|
197
200
|
|
@@ -199,7 +202,6 @@ module ActiveSupport
|
|
199
202
|
end
|
200
203
|
|
201
204
|
private
|
202
|
-
|
203
205
|
def calculate_total_seconds(parts)
|
204
206
|
parts.inject(0) do |total, (part, value)|
|
205
207
|
total + value * PARTS_IN_SECONDS[part]
|
@@ -208,14 +210,16 @@ module ActiveSupport
|
|
208
210
|
end
|
209
211
|
|
210
212
|
def initialize(value, parts) #:nodoc:
|
211
|
-
@value, @parts = value, parts
|
212
|
-
@parts.
|
213
|
-
@parts.reject! { |k, v| v.zero? }
|
213
|
+
@value, @parts = value, parts
|
214
|
+
@parts.reject! { |k, v| v.zero? } unless value == 0
|
214
215
|
end
|
215
216
|
|
216
217
|
def coerce(other) #:nodoc:
|
217
|
-
|
218
|
+
case other
|
219
|
+
when Scalar
|
218
220
|
[other, self]
|
221
|
+
when Duration
|
222
|
+
[Scalar.new(other.value), self]
|
219
223
|
else
|
220
224
|
[Scalar.new(other), self]
|
221
225
|
end
|
@@ -235,13 +239,12 @@ module ActiveSupport
|
|
235
239
|
# are treated as seconds.
|
236
240
|
def +(other)
|
237
241
|
if Duration === other
|
238
|
-
parts = @parts.
|
239
|
-
|
240
|
-
parts[key] += value
|
242
|
+
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
243
|
+
value + other_value
|
241
244
|
end
|
242
245
|
Duration.new(value + other.value, parts)
|
243
246
|
else
|
244
|
-
seconds = @parts
|
247
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
245
248
|
Duration.new(value + other, @parts.merge(seconds: seconds))
|
246
249
|
end
|
247
250
|
end
|
@@ -255,9 +258,9 @@ module ActiveSupport
|
|
255
258
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
256
259
|
def *(other)
|
257
260
|
if Scalar === other || Duration === other
|
258
|
-
Duration.new(value * other.value, parts.
|
261
|
+
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
259
262
|
elsif Numeric === other
|
260
|
-
Duration.new(value * other, parts.
|
263
|
+
Duration.new(value * other, parts.transform_values { |number| number * other })
|
261
264
|
else
|
262
265
|
raise_type_error(other)
|
263
266
|
end
|
@@ -266,11 +269,11 @@ module ActiveSupport
|
|
266
269
|
# Divides this Duration by a Numeric and returns a new Duration.
|
267
270
|
def /(other)
|
268
271
|
if Scalar === other
|
269
|
-
Duration.new(value / other.value, parts.
|
272
|
+
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
270
273
|
elsif Duration === other
|
271
274
|
value / other.value
|
272
275
|
elsif Numeric === other
|
273
|
-
Duration.new(value / other, parts.
|
276
|
+
Duration.new(value / other, parts.transform_values { |number| number / other })
|
274
277
|
else
|
275
278
|
raise_type_error(other)
|
276
279
|
end
|
@@ -289,7 +292,11 @@ module ActiveSupport
|
|
289
292
|
end
|
290
293
|
|
291
294
|
def -@ #:nodoc:
|
292
|
-
Duration.new(-value, parts.
|
295
|
+
Duration.new(-value, parts.transform_values(&:-@))
|
296
|
+
end
|
297
|
+
|
298
|
+
def +@ #:nodoc:
|
299
|
+
self
|
293
300
|
end
|
294
301
|
|
295
302
|
def is_a?(klass) #:nodoc:
|
@@ -336,12 +343,55 @@ module ActiveSupport
|
|
336
343
|
# 1.year.to_i # => 31556952
|
337
344
|
#
|
338
345
|
# In such cases, Ruby's core
|
339
|
-
# Date[
|
340
|
-
# Time[
|
346
|
+
# Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
|
347
|
+
# Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
|
341
348
|
# date and time arithmetic.
|
342
349
|
def to_i
|
343
350
|
@value.to_i
|
344
351
|
end
|
352
|
+
alias :in_seconds :to_i
|
353
|
+
|
354
|
+
# Returns the amount of minutes a duration covers as a float
|
355
|
+
#
|
356
|
+
# 1.day.in_minutes # => 1440.0
|
357
|
+
def in_minutes
|
358
|
+
in_seconds / SECONDS_PER_MINUTE.to_f
|
359
|
+
end
|
360
|
+
|
361
|
+
# Returns the amount of hours a duration covers as a float
|
362
|
+
#
|
363
|
+
# 1.day.in_hours # => 24.0
|
364
|
+
def in_hours
|
365
|
+
in_seconds / SECONDS_PER_HOUR.to_f
|
366
|
+
end
|
367
|
+
|
368
|
+
# Returns the amount of days a duration covers as a float
|
369
|
+
#
|
370
|
+
# 12.hours.in_days # => 0.5
|
371
|
+
def in_days
|
372
|
+
in_seconds / SECONDS_PER_DAY.to_f
|
373
|
+
end
|
374
|
+
|
375
|
+
# Returns the amount of weeks a duration covers as a float
|
376
|
+
#
|
377
|
+
# 2.months.in_weeks # => 8.696
|
378
|
+
def in_weeks
|
379
|
+
in_seconds / SECONDS_PER_WEEK.to_f
|
380
|
+
end
|
381
|
+
|
382
|
+
# Returns the amount of months a duration covers as a float
|
383
|
+
#
|
384
|
+
# 9.weeks.in_months # => 2.07
|
385
|
+
def in_months
|
386
|
+
in_seconds / SECONDS_PER_MONTH.to_f
|
387
|
+
end
|
388
|
+
|
389
|
+
# Returns the amount of years a duration covers as a float
|
390
|
+
#
|
391
|
+
# 30.days.in_years # => 0.082
|
392
|
+
def in_years
|
393
|
+
in_seconds / SECONDS_PER_YEAR.to_f
|
394
|
+
end
|
345
395
|
|
346
396
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
347
397
|
# same parts as this one.
|
@@ -370,10 +420,9 @@ module ActiveSupport
|
|
370
420
|
alias :before :ago
|
371
421
|
|
372
422
|
def inspect #:nodoc:
|
373
|
-
return "
|
423
|
+
return "#{value} seconds" if parts.empty?
|
374
424
|
|
375
425
|
parts.
|
376
|
-
reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
|
377
426
|
sort_by { |unit, _ | PARTS.index(unit) }.
|
378
427
|
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
379
428
|
to_sentence(locale: ::I18n.default_locale)
|
@@ -398,10 +447,15 @@ module ActiveSupport
|
|
398
447
|
end
|
399
448
|
|
400
449
|
private
|
401
|
-
|
402
450
|
def sum(sign, time = ::Time.current)
|
403
|
-
|
404
|
-
|
451
|
+
unless time.acts_like?(:time) || time.acts_like?(:date)
|
452
|
+
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
453
|
+
end
|
454
|
+
|
455
|
+
if parts.empty?
|
456
|
+
time.since(sign * value)
|
457
|
+
else
|
458
|
+
parts.inject(time) do |t, (type, number)|
|
405
459
|
if type == :seconds
|
406
460
|
t.since(sign * number)
|
407
461
|
elsif type == :minutes
|
@@ -411,8 +465,6 @@ module ActiveSupport
|
|
411
465
|
else
|
412
466
|
t.advance(type => sign * number)
|
413
467
|
end
|
414
|
-
else
|
415
|
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
416
468
|
end
|
417
469
|
end
|
418
470
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "strscan"
|
4
|
-
require "active_support/core_ext/regexp"
|
5
4
|
|
6
5
|
module ActiveSupport
|
7
6
|
class Duration
|
@@ -14,8 +13,8 @@ module ActiveSupport
|
|
14
13
|
class ParsingError < ::ArgumentError; end
|
15
14
|
|
16
15
|
PERIOD_OR_COMMA = /\.|,/
|
17
|
-
PERIOD = "."
|
18
|
-
COMMA = ","
|
16
|
+
PERIOD = "."
|
17
|
+
COMMA = ","
|
19
18
|
|
20
19
|
SIGN_MARKER = /\A\-|\+|/
|
21
20
|
DATE_MARKER = /P/
|
@@ -81,7 +80,6 @@ module ActiveSupport
|
|
81
80
|
end
|
82
81
|
|
83
82
|
private
|
84
|
-
|
85
83
|
def finished?
|
86
84
|
scanner.eos?
|
87
85
|
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/object/blank"
|
4
|
-
require "active_support/core_ext/hash/transform_values"
|
5
4
|
|
6
5
|
module ActiveSupport
|
7
6
|
class Duration
|
8
7
|
# Serializes duration to string according to ISO 8601 Duration format.
|
9
8
|
class ISO8601Serializer # :nodoc:
|
9
|
+
DATE_COMPONENTS = %i(years months days)
|
10
|
+
|
10
11
|
def initialize(duration, precision: nil)
|
11
12
|
@duration = duration
|
12
13
|
@precision = precision
|
@@ -14,26 +15,25 @@ module ActiveSupport
|
|
14
15
|
|
15
16
|
# Builds and returns output string.
|
16
17
|
def serialize
|
17
|
-
parts
|
18
|
-
return "PT0S"
|
18
|
+
parts = normalize
|
19
|
+
return "PT0S" if parts.empty?
|
19
20
|
|
20
|
-
output = "P"
|
21
|
+
output = +"P"
|
21
22
|
output << "#{parts[:years]}Y" if parts.key?(:years)
|
22
23
|
output << "#{parts[:months]}M" if parts.key?(:months)
|
23
|
-
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
|
24
24
|
output << "#{parts[:days]}D" if parts.key?(:days)
|
25
|
-
|
25
|
+
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
|
26
|
+
time = +""
|
26
27
|
time << "#{parts[:hours]}H" if parts.key?(:hours)
|
27
28
|
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
|
28
29
|
if parts.key?(:seconds)
|
29
30
|
time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
|
30
31
|
end
|
31
32
|
output << "T#{time}" unless time.empty?
|
32
|
-
|
33
|
+
output
|
33
34
|
end
|
34
35
|
|
35
36
|
private
|
36
|
-
|
37
37
|
# Return pair of duration's parts and whole duration sign.
|
38
38
|
# Parts are summarized (as they can become repetitive due to addition, etc).
|
39
39
|
# Zero parts are removed as not significant.
|
@@ -42,13 +42,17 @@ module ActiveSupport
|
|
42
42
|
parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
|
43
43
|
p[k] += v unless v.zero?
|
44
44
|
end
|
45
|
-
|
46
|
-
|
47
|
-
if parts
|
48
|
-
|
49
|
-
parts.transform_values!(&:-@)
|
45
|
+
|
46
|
+
# Convert weeks to days and remove weeks if mixed with date parts
|
47
|
+
if week_mixed_with_date?(parts)
|
48
|
+
parts[:days] += parts.delete(:weeks) * SECONDS_PER_WEEK / SECONDS_PER_DAY
|
50
49
|
end
|
51
|
-
|
50
|
+
|
51
|
+
parts
|
52
|
+
end
|
53
|
+
|
54
|
+
def week_mixed_with_date?(parts)
|
55
|
+
parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|