activesupport 6.0.3.4 → 6.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +342 -472
- data/MIT-LICENSE +1 -1
- data/lib/active_support.rb +13 -1
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache.rb +85 -44
- data/lib/active_support/cache/file_store.rb +4 -3
- data/lib/active_support/cache/mem_cache_store.rb +21 -14
- data/lib/active_support/cache/memory_store.rb +46 -26
- data/lib/active_support/cache/redis_cache_store.rb +27 -27
- data/lib/active_support/cache/strategy/local_cache.rb +21 -6
- data/lib/active_support/callbacks.rb +65 -56
- data/lib/active_support/concern.rb +46 -2
- data/lib/active_support/configurable.rb +3 -3
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/enumerable.rb +76 -4
- data/lib/active_support/core_ext/hash/conversions.rb +2 -2
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +1 -1
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- 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/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +38 -28
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +6 -2
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range/compare_range.rb +9 -3
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/inflections.rb +38 -4
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +9 -9
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- 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 +19 -1
- data/lib/active_support/core_ext/time/conversions.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +5 -1
- data/lib/active_support/current_attributes.rb +7 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/dependencies.rb +43 -19
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/deprecation/behaviors.rb +15 -2
- 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 +3 -2
- data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/descendants_tracker.rb +6 -2
- data/lib/active_support/duration.rb +71 -22
- data/lib/active_support/duration/iso8601_serializer.rb +15 -9
- data/lib/active_support/encrypted_file.rb +19 -2
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +69 -133
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +42 -23
- data/lib/active_support/i18n_railtie.rb +14 -19
- data/lib/active_support/inflector/inflections.rb +1 -2
- data/lib/active_support/inflector/methods.rb +35 -31
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +5 -1
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +8 -0
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +34 -12
- data/lib/active_support/message_encryptor.rb +4 -7
- data/lib/active_support/message_verifier.rb +5 -5
- data/lib/active_support/messages/metadata.rb +9 -1
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +6 -5
- data/lib/active_support/multibyte/chars.rb +4 -42
- data/lib/active_support/multibyte/unicode.rb +9 -83
- data/lib/active_support/notifications.rb +31 -4
- data/lib/active_support/notifications/fanout.rb +23 -8
- data/lib/active_support/notifications/instrumenter.rb +6 -15
- data/lib/active_support/number_helper.rb +29 -14
- data/lib/active_support/number_helper/number_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -7
- data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/option_merger.rb +3 -2
- data/lib/active_support/ordered_options.rb +8 -2
- data/lib/active_support/parameter_filter.rb +15 -10
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +23 -1
- 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 -2
- data/lib/active_support/subscriber.rb +12 -7
- data/lib/active_support/tagged_logging.rb +29 -4
- data/lib/active_support/testing/assertions.rb +18 -11
- data/lib/active_support/testing/parallelization.rb +12 -95
- 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/time_helpers.rb +40 -3
- data/lib/active_support/time_with_zone.rb +66 -42
- data/lib/active_support/values/time_zone.rb +20 -10
- data/lib/active_support/xml_mini/rexml.rb +8 -1
- metadata +36 -38
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -56,11 +56,12 @@ module ActiveSupport
|
|
56
56
|
mod = nil
|
57
57
|
|
58
58
|
method_names.each do |method_name|
|
59
|
+
message = options[method_name]
|
59
60
|
if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
|
60
61
|
method = target_module.instance_method(method_name)
|
61
62
|
target_module.module_eval do
|
62
63
|
redefine_method(method_name) do |*args, &block|
|
63
|
-
deprecator.deprecation_warning(method_name,
|
64
|
+
deprecator.deprecation_warning(method_name, message)
|
64
65
|
method.bind(self).call(*args, &block)
|
65
66
|
end
|
66
67
|
ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true)
|
@@ -69,7 +70,7 @@ module ActiveSupport
|
|
69
70
|
mod ||= Module.new
|
70
71
|
mod.module_eval do
|
71
72
|
define_method(method_name) do |*args, &block|
|
72
|
-
deprecator.deprecation_warning(method_name,
|
73
|
+
deprecator.deprecation_warning(method_name, message)
|
73
74
|
super(*args, &block)
|
74
75
|
end
|
75
76
|
ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true)
|
@@ -121,7 +121,7 @@ module ActiveSupport
|
|
121
121
|
# (Backtrace information…)
|
122
122
|
# ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
|
123
123
|
class DeprecatedConstantProxy < Module
|
124
|
-
def self.new(*args, **
|
124
|
+
def self.new(*args, **options, &block)
|
125
125
|
object = args.first
|
126
126
|
|
127
127
|
return object unless object
|
@@ -129,7 +129,7 @@ module ActiveSupport
|
|
129
129
|
end
|
130
130
|
|
131
131
|
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.")
|
132
|
-
require "active_support/inflector/methods"
|
132
|
+
Kernel.require "active_support/inflector/methods"
|
133
133
|
|
134
134
|
@old_const = old_const
|
135
135
|
@new_const = new_const
|
@@ -147,7 +147,7 @@ module ActiveSupport
|
|
147
147
|
|
148
148
|
# Don't give a deprecation warning on methods that IRB may invoke
|
149
149
|
# during tab-completion.
|
150
|
-
delegate :hash, :instance_methods, :name, to: :target
|
150
|
+
delegate :hash, :instance_methods, :name, :respond_to?, to: :target
|
151
151
|
|
152
152
|
# Returns the class of the new constant.
|
153
153
|
#
|
@@ -6,7 +6,7 @@ module ActiveSupport
|
|
6
6
|
class Deprecation
|
7
7
|
module Reporting
|
8
8
|
# Whether to print a message (silent mode)
|
9
|
-
|
9
|
+
attr_writer :silenced
|
10
10
|
# Name of gem where method is deprecated
|
11
11
|
attr_accessor :gem_name
|
12
12
|
|
@@ -20,7 +20,11 @@ module ActiveSupport
|
|
20
20
|
|
21
21
|
callstack ||= caller_locations(2)
|
22
22
|
deprecation_message(callstack, message).tap do |m|
|
23
|
-
|
23
|
+
if deprecation_disallowed?(message)
|
24
|
+
disallowed_behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
|
25
|
+
else
|
26
|
+
behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
|
27
|
+
end
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
@@ -33,11 +37,50 @@ module ActiveSupport
|
|
33
37
|
# ActiveSupport::Deprecation.warn('something broke!')
|
34
38
|
# end
|
35
39
|
# # => nil
|
36
|
-
def silence
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
def silence(&block)
|
41
|
+
@silenced_thread.bind(true, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Allow previously disallowed deprecation warnings within the block.
|
45
|
+
# <tt>allowed_warnings</tt> can be an array containing strings, symbols, or regular
|
46
|
+
# expressions. (Symbols are treated as strings). These are compared against
|
47
|
+
# the text of deprecation warning messages generated within the block.
|
48
|
+
# Matching warnings will be exempt from the rules set by
|
49
|
+
# +ActiveSupport::Deprecation.disallowed_warnings+
|
50
|
+
#
|
51
|
+
# The optional <tt>if:</tt> argument accepts a truthy/falsy value or an object that
|
52
|
+
# responds to <tt>.call</tt>. If truthy, then matching warnings will be allowed.
|
53
|
+
# If falsey then the method yields to the block without allowing the warning.
|
54
|
+
#
|
55
|
+
# ActiveSupport::Deprecation.disallowed_behavior = :raise
|
56
|
+
# ActiveSupport::Deprecation.disallowed_warnings = [
|
57
|
+
# "something broke"
|
58
|
+
# ]
|
59
|
+
#
|
60
|
+
# ActiveSupport::Deprecation.warn('something broke!')
|
61
|
+
# # => ActiveSupport::DeprecationException
|
62
|
+
#
|
63
|
+
# ActiveSupport::Deprecation.allow ['something broke'] do
|
64
|
+
# ActiveSupport::Deprecation.warn('something broke!')
|
65
|
+
# end
|
66
|
+
# # => nil
|
67
|
+
#
|
68
|
+
# ActiveSupport::Deprecation.allow ['something broke'], if: Rails.env.production? do
|
69
|
+
# ActiveSupport::Deprecation.warn('something broke!')
|
70
|
+
# end
|
71
|
+
# # => ActiveSupport::DeprecationException for dev/test, nil for production
|
72
|
+
def allow(allowed_warnings = :all, if: true, &block)
|
73
|
+
conditional = binding.local_variable_get(:if)
|
74
|
+
conditional = conditional.call if conditional.respond_to?(:call)
|
75
|
+
if conditional
|
76
|
+
@explicitly_allowed_warnings.bind(allowed_warnings, &block)
|
77
|
+
else
|
78
|
+
yield
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def silenced
|
83
|
+
@silenced || @silenced_thread.value
|
41
84
|
end
|
42
85
|
|
43
86
|
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
|
@@ -13,6 +13,7 @@ module ActiveSupport
|
|
13
13
|
descendants = @@direct_descendants[klass]
|
14
14
|
descendants ? descendants.to_a : []
|
15
15
|
end
|
16
|
+
alias_method :subclasses, :direct_descendants
|
16
17
|
|
17
18
|
def descendants(klass)
|
18
19
|
arr = []
|
@@ -59,6 +60,7 @@ module ActiveSupport
|
|
59
60
|
def direct_descendants
|
60
61
|
DescendantsTracker.direct_descendants(self)
|
61
62
|
end
|
63
|
+
alias_method :subclasses, :direct_descendants
|
62
64
|
|
63
65
|
def descendants
|
64
66
|
DescendantsTracker.descendants(self)
|
@@ -77,15 +79,17 @@ module ActiveSupport
|
|
77
79
|
end
|
78
80
|
|
79
81
|
def <<(klass)
|
80
|
-
cleanup!
|
81
82
|
@refs << WeakRef.new(klass)
|
82
83
|
end
|
83
84
|
|
84
85
|
def each
|
85
|
-
@refs.
|
86
|
+
@refs.reject! do |ref|
|
86
87
|
yield ref.__getobj__
|
88
|
+
false
|
87
89
|
rescue WeakRef::RefError
|
90
|
+
true
|
88
91
|
end
|
92
|
+
self
|
89
93
|
end
|
90
94
|
|
91
95
|
def refs_size
|
@@ -39,7 +39,7 @@ module ActiveSupport
|
|
39
39
|
|
40
40
|
def +(other)
|
41
41
|
if Duration === other
|
42
|
-
seconds = value + other.parts
|
42
|
+
seconds = value + other.parts.fetch(:seconds, 0)
|
43
43
|
new_parts = other.parts.merge(seconds: seconds)
|
44
44
|
new_value = value + other.value
|
45
45
|
|
@@ -51,8 +51,8 @@ module ActiveSupport
|
|
51
51
|
|
52
52
|
def -(other)
|
53
53
|
if Duration === other
|
54
|
-
seconds = value - other.parts
|
55
|
-
new_parts = other.parts.
|
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
|
|
@@ -64,7 +64,7 @@ module ActiveSupport
|
|
64
64
|
|
65
65
|
def *(other)
|
66
66
|
if Duration === other
|
67
|
-
new_parts = other.parts.
|
67
|
+
new_parts = other.parts.transform_values { |other_value| value * other_value }
|
68
68
|
new_value = value * other.value
|
69
69
|
|
70
70
|
Duration.new(new_value, new_parts)
|
@@ -147,31 +147,31 @@ module ActiveSupport
|
|
147
147
|
end
|
148
148
|
|
149
149
|
def seconds(value) #:nodoc:
|
150
|
-
new(value,
|
150
|
+
new(value, seconds: value)
|
151
151
|
end
|
152
152
|
|
153
153
|
def minutes(value) #:nodoc:
|
154
|
-
new(value * SECONDS_PER_MINUTE,
|
154
|
+
new(value * SECONDS_PER_MINUTE, minutes: value)
|
155
155
|
end
|
156
156
|
|
157
157
|
def hours(value) #:nodoc:
|
158
|
-
new(value * SECONDS_PER_HOUR,
|
158
|
+
new(value * SECONDS_PER_HOUR, hours: value)
|
159
159
|
end
|
160
160
|
|
161
161
|
def days(value) #:nodoc:
|
162
|
-
new(value * SECONDS_PER_DAY,
|
162
|
+
new(value * SECONDS_PER_DAY, days: value)
|
163
163
|
end
|
164
164
|
|
165
165
|
def weeks(value) #:nodoc:
|
166
|
-
new(value * SECONDS_PER_WEEK,
|
166
|
+
new(value * SECONDS_PER_WEEK, weeks: value)
|
167
167
|
end
|
168
168
|
|
169
169
|
def months(value) #:nodoc:
|
170
|
-
new(value * SECONDS_PER_MONTH,
|
170
|
+
new(value * SECONDS_PER_MONTH, months: value)
|
171
171
|
end
|
172
172
|
|
173
173
|
def years(value) #:nodoc:
|
174
|
-
new(value * SECONDS_PER_YEAR,
|
174
|
+
new(value * SECONDS_PER_YEAR, years: value)
|
175
175
|
end
|
176
176
|
|
177
177
|
# Creates a new Duration from a seconds value that is converted
|
@@ -181,6 +181,10 @@ module ActiveSupport
|
|
181
181
|
# ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
|
182
182
|
#
|
183
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
|
+
|
184
188
|
parts = {}
|
185
189
|
remainder = value.round(9)
|
186
190
|
|
@@ -206,8 +210,7 @@ module ActiveSupport
|
|
206
210
|
end
|
207
211
|
|
208
212
|
def initialize(value, parts) #:nodoc:
|
209
|
-
@value, @parts = value, parts
|
210
|
-
@parts.default = 0
|
213
|
+
@value, @parts = value, parts
|
211
214
|
@parts.reject! { |k, v| v.zero? } unless value == 0
|
212
215
|
end
|
213
216
|
|
@@ -236,13 +239,12 @@ module ActiveSupport
|
|
236
239
|
# are treated as seconds.
|
237
240
|
def +(other)
|
238
241
|
if Duration === other
|
239
|
-
parts = @parts.
|
240
|
-
|
241
|
-
parts[key] += value
|
242
|
+
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
243
|
+
value + other_value
|
242
244
|
end
|
243
245
|
Duration.new(value + other.value, parts)
|
244
246
|
else
|
245
|
-
seconds = @parts
|
247
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
246
248
|
Duration.new(value + other, @parts.merge(seconds: seconds))
|
247
249
|
end
|
248
250
|
end
|
@@ -256,9 +258,9 @@ module ActiveSupport
|
|
256
258
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
257
259
|
def *(other)
|
258
260
|
if Scalar === other || Duration === other
|
259
|
-
Duration.new(value * other.value, parts.
|
261
|
+
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
260
262
|
elsif Numeric === other
|
261
|
-
Duration.new(value * other, parts.
|
263
|
+
Duration.new(value * other, parts.transform_values { |number| number * other })
|
262
264
|
else
|
263
265
|
raise_type_error(other)
|
264
266
|
end
|
@@ -267,11 +269,11 @@ module ActiveSupport
|
|
267
269
|
# Divides this Duration by a Numeric and returns a new Duration.
|
268
270
|
def /(other)
|
269
271
|
if Scalar === other
|
270
|
-
Duration.new(value / other.value, parts.
|
272
|
+
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
271
273
|
elsif Duration === other
|
272
274
|
value / other.value
|
273
275
|
elsif Numeric === other
|
274
|
-
Duration.new(value / other, parts.
|
276
|
+
Duration.new(value / other, parts.transform_values { |number| number / other })
|
275
277
|
else
|
276
278
|
raise_type_error(other)
|
277
279
|
end
|
@@ -290,7 +292,11 @@ module ActiveSupport
|
|
290
292
|
end
|
291
293
|
|
292
294
|
def -@ #:nodoc:
|
293
|
-
Duration.new(-value, parts.
|
295
|
+
Duration.new(-value, parts.transform_values(&:-@))
|
296
|
+
end
|
297
|
+
|
298
|
+
def +@ #:nodoc:
|
299
|
+
self
|
294
300
|
end
|
295
301
|
|
296
302
|
def is_a?(klass) #:nodoc:
|
@@ -343,6 +349,49 @@ module ActiveSupport
|
|
343
349
|
def to_i
|
344
350
|
@value.to_i
|
345
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
|
346
395
|
|
347
396
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
348
397
|
# same parts as this one.
|
@@ -6,6 +6,8 @@ module ActiveSupport
|
|
6
6
|
class Duration
|
7
7
|
# Serializes duration to string according to ISO 8601 Duration format.
|
8
8
|
class ISO8601Serializer # :nodoc:
|
9
|
+
DATE_COMPONENTS = %i(years months days)
|
10
|
+
|
9
11
|
def initialize(duration, precision: nil)
|
10
12
|
@duration = duration
|
11
13
|
@precision = precision
|
@@ -13,14 +15,14 @@ module ActiveSupport
|
|
13
15
|
|
14
16
|
# Builds and returns output string.
|
15
17
|
def serialize
|
16
|
-
parts
|
18
|
+
parts = normalize
|
17
19
|
return "PT0S" if parts.empty?
|
18
20
|
|
19
21
|
output = +"P"
|
20
22
|
output << "#{parts[:years]}Y" if parts.key?(:years)
|
21
23
|
output << "#{parts[:months]}M" if parts.key?(:months)
|
22
|
-
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
|
23
24
|
output << "#{parts[:days]}D" if parts.key?(:days)
|
25
|
+
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
|
24
26
|
time = +""
|
25
27
|
time << "#{parts[:hours]}H" if parts.key?(:hours)
|
26
28
|
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
|
@@ -28,7 +30,7 @@ module ActiveSupport
|
|
28
30
|
time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
|
29
31
|
end
|
30
32
|
output << "T#{time}" unless time.empty?
|
31
|
-
|
33
|
+
output
|
32
34
|
end
|
33
35
|
|
34
36
|
private
|
@@ -40,13 +42,17 @@ module ActiveSupport
|
|
40
42
|
parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
|
41
43
|
p[k] += v unless v.zero?
|
42
44
|
end
|
43
|
-
|
44
|
-
|
45
|
-
if parts
|
46
|
-
|
47
|
-
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
|
48
49
|
end
|
49
|
-
|
50
|
+
|
51
|
+
parts
|
52
|
+
end
|
53
|
+
|
54
|
+
def week_mixed_with_date?(parts)
|
55
|
+
parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
|
50
56
|
end
|
51
57
|
end
|
52
58
|
end
|
@@ -20,17 +20,28 @@ module ActiveSupport
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
class InvalidKeyLengthError < RuntimeError
|
24
|
+
def initialize
|
25
|
+
super "Encryption key must be exactly #{EncryptedFile.expected_key_length} characters."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
23
29
|
CIPHER = "aes-128-gcm"
|
24
30
|
|
25
31
|
def self.generate_key
|
26
32
|
SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
|
27
33
|
end
|
28
34
|
|
35
|
+
def self.expected_key_length # :nodoc:
|
36
|
+
@expected_key_length ||= generate_key.length
|
37
|
+
end
|
38
|
+
|
29
39
|
|
30
40
|
attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
|
31
41
|
|
32
42
|
def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
|
33
|
-
@content_path
|
43
|
+
@content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path }
|
44
|
+
@key_path = Pathname.new(key_path)
|
34
45
|
@env_key, @raise_if_missing_key = env_key, raise_if_missing_key
|
35
46
|
end
|
36
47
|
|
@@ -73,6 +84,7 @@ module ActiveSupport
|
|
73
84
|
|
74
85
|
|
75
86
|
def encrypt(contents)
|
87
|
+
check_key_length
|
76
88
|
encryptor.encrypt_and_sign contents
|
77
89
|
end
|
78
90
|
|
@@ -90,11 +102,16 @@ module ActiveSupport
|
|
90
102
|
end
|
91
103
|
|
92
104
|
def read_key_file
|
93
|
-
|
105
|
+
return @key_file_contents if defined?(@key_file_contents)
|
106
|
+
@key_file_contents = (key_path.binread.strip if key_path.exist?)
|
94
107
|
end
|
95
108
|
|
96
109
|
def handle_missing_key
|
97
110
|
raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
|
98
111
|
end
|
112
|
+
|
113
|
+
def check_key_length
|
114
|
+
raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
|
115
|
+
end
|
99
116
|
end
|
100
117
|
end
|