activesupport 6.0.3.4 → 6.1.7.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 +456 -398
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -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/file_store.rb +5 -4
- data/lib/active_support/cache/mem_cache_store.rb +29 -18
- 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/cache.rb +92 -45
- 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 +51 -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/digest/uuid.rb +1 -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 +13 -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 +38 -10
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +22 -1
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +5 -1
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +9 -2
- data/lib/active_support/dependencies/zeitwerk_integration.rb +4 -1
- data/lib/active_support/dependencies.rb +43 -19
- 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/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +6 -2
- data/lib/active_support/digest.rb +2 -0
- data/lib/active_support/duration/iso8601_serializer.rb +15 -9
- data/lib/active_support/duration.rb +75 -25
- 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/execution_wrapper.rb +16 -13
- data/lib/active_support/fork_tracker.rb +64 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +48 -24
- 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 +36 -33
- 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/fanout.rb +23 -8
- data/lib/active_support/notifications/instrumenter.rb +6 -15
- data/lib/active_support/notifications.rb +32 -5
- 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 +9 -5
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/number_helper.rb +29 -14
- 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 +16 -11
- data/lib/active_support/per_thread_registry.rb +2 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +1 -1
- 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 -2
- data/lib/active_support/subscriber.rb +12 -7
- data/lib/active_support/tagged_logging.rb +30 -5
- data/lib/active_support/testing/assertions.rb +18 -11
- 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/parallelization.rb +12 -95
- data/lib/active_support/testing/time_helpers.rb +40 -3
- data/lib/active_support/time_with_zone.rb +67 -43
- data/lib/active_support/values/time_zone.rb +22 -10
- data/lib/active_support/xml_mini/rexml.rb +8 -1
- data/lib/active_support.rb +13 -1
- metadata +35 -36
- 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
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
class Deprecation
|
5
|
+
module Disallowed
|
6
|
+
# Sets the criteria used to identify deprecation messages which should be
|
7
|
+
# disallowed. Can be an array containing strings, symbols, or regular
|
8
|
+
# expressions. (Symbols are treated as strings). These are compared against
|
9
|
+
# the text of the generated deprecation warning.
|
10
|
+
#
|
11
|
+
# Additionally the scalar symbol +:all+ may be used to treat all
|
12
|
+
# deprecations as disallowed.
|
13
|
+
#
|
14
|
+
# Deprecations matching a substring or regular expression will be handled
|
15
|
+
# using the configured +ActiveSupport::Deprecation.disallowed_behavior+
|
16
|
+
# rather than +ActiveSupport::Deprecation.behavior+
|
17
|
+
attr_writer :disallowed_warnings
|
18
|
+
|
19
|
+
# Returns the configured criteria used to identify deprecation messages
|
20
|
+
# which should be treated as disallowed.
|
21
|
+
def disallowed_warnings
|
22
|
+
@disallowed_warnings ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def deprecation_disallowed?(message)
|
27
|
+
disallowed = ActiveSupport::Deprecation.disallowed_warnings
|
28
|
+
return false if explicitly_allowed?(message)
|
29
|
+
return true if disallowed == :all
|
30
|
+
disallowed.any? do |rule|
|
31
|
+
case rule
|
32
|
+
when String, Symbol
|
33
|
+
message.include?(rule.to_s)
|
34
|
+
when Regexp
|
35
|
+
rule.match?(message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def explicitly_allowed?(message)
|
41
|
+
allowances = @explicitly_allowed_warnings.value
|
42
|
+
return false unless allowances
|
43
|
+
return true if allowances == :all
|
44
|
+
allowances = [allowances] unless allowances.kind_of?(Array)
|
45
|
+
allowances.any? do |rule|
|
46
|
+
case rule
|
47
|
+
when String, Symbol
|
48
|
+
message.include?(rule.to_s)
|
49
|
+
when Regexp
|
50
|
+
rule.match?(message)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -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)
|
@@ -17,15 +17,18 @@ module ActiveSupport
|
|
17
17
|
require "active_support/deprecation/instance_delegator"
|
18
18
|
require "active_support/deprecation/behaviors"
|
19
19
|
require "active_support/deprecation/reporting"
|
20
|
+
require "active_support/deprecation/disallowed"
|
20
21
|
require "active_support/deprecation/constant_accessor"
|
21
22
|
require "active_support/deprecation/method_wrappers"
|
22
23
|
require "active_support/deprecation/proxy_wrappers"
|
23
24
|
require "active_support/core_ext/module/deprecation"
|
25
|
+
require "concurrent/atomic/thread_local_var"
|
24
26
|
|
25
27
|
include Singleton
|
26
28
|
include InstanceDelegator
|
27
29
|
include Behavior
|
28
30
|
include Reporting
|
31
|
+
include Disallowed
|
29
32
|
include MethodWrapper
|
30
33
|
|
31
34
|
# The version number in which the deprecated behavior will be removed, by default.
|
@@ -35,12 +38,14 @@ module ActiveSupport
|
|
35
38
|
# and the second is a library name.
|
36
39
|
#
|
37
40
|
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
|
38
|
-
def initialize(deprecation_horizon = "
|
41
|
+
def initialize(deprecation_horizon = "7.0", gem_name = "Rails")
|
39
42
|
self.gem_name = gem_name
|
40
43
|
self.deprecation_horizon = deprecation_horizon
|
41
44
|
# By default, warnings are not silenced and debugging is off.
|
42
45
|
self.silenced = false
|
43
46
|
self.debug = false
|
47
|
+
@silenced_thread = Concurrent::ThreadLocalVar.new(false)
|
48
|
+
@explicitly_allowed_warnings = Concurrent::ThreadLocalVar.new(nil)
|
44
49
|
end
|
45
50
|
end
|
46
51
|
end
|
@@ -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
|
@@ -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
|
@@ -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,18 +181,23 @@ 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_sign = value <=> 0
|
190
|
+
remainder = value.round(9).abs
|
186
191
|
|
187
192
|
PARTS.each do |part|
|
188
193
|
unless part == :seconds
|
189
194
|
part_in_seconds = PARTS_IN_SECONDS[part]
|
190
|
-
parts[part] = remainder.div(part_in_seconds)
|
195
|
+
parts[part] = remainder.div(part_in_seconds) * remainder_sign
|
191
196
|
remainder %= part_in_seconds
|
192
197
|
end
|
193
198
|
end unless value == 0
|
194
199
|
|
195
|
-
parts[:seconds] = remainder
|
200
|
+
parts[:seconds] = remainder * remainder_sign
|
196
201
|
|
197
202
|
new(value, parts)
|
198
203
|
end
|
@@ -206,8 +211,7 @@ module ActiveSupport
|
|
206
211
|
end
|
207
212
|
|
208
213
|
def initialize(value, parts) #:nodoc:
|
209
|
-
@value, @parts = value, parts
|
210
|
-
@parts.default = 0
|
214
|
+
@value, @parts = value, parts
|
211
215
|
@parts.reject! { |k, v| v.zero? } unless value == 0
|
212
216
|
end
|
213
217
|
|
@@ -236,13 +240,12 @@ module ActiveSupport
|
|
236
240
|
# are treated as seconds.
|
237
241
|
def +(other)
|
238
242
|
if Duration === other
|
239
|
-
parts = @parts.
|
240
|
-
|
241
|
-
parts[key] += value
|
243
|
+
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
244
|
+
value + other_value
|
242
245
|
end
|
243
246
|
Duration.new(value + other.value, parts)
|
244
247
|
else
|
245
|
-
seconds = @parts
|
248
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
246
249
|
Duration.new(value + other, @parts.merge(seconds: seconds))
|
247
250
|
end
|
248
251
|
end
|
@@ -256,9 +259,9 @@ module ActiveSupport
|
|
256
259
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
257
260
|
def *(other)
|
258
261
|
if Scalar === other || Duration === other
|
259
|
-
Duration.new(value * other.value, parts.
|
262
|
+
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
260
263
|
elsif Numeric === other
|
261
|
-
Duration.new(value * other, parts.
|
264
|
+
Duration.new(value * other, parts.transform_values { |number| number * other })
|
262
265
|
else
|
263
266
|
raise_type_error(other)
|
264
267
|
end
|
@@ -267,11 +270,11 @@ module ActiveSupport
|
|
267
270
|
# Divides this Duration by a Numeric and returns a new Duration.
|
268
271
|
def /(other)
|
269
272
|
if Scalar === other
|
270
|
-
Duration.new(value / other.value, parts.
|
273
|
+
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
271
274
|
elsif Duration === other
|
272
275
|
value / other.value
|
273
276
|
elsif Numeric === other
|
274
|
-
Duration.new(value / other, parts.
|
277
|
+
Duration.new(value / other, parts.transform_values { |number| number / other })
|
275
278
|
else
|
276
279
|
raise_type_error(other)
|
277
280
|
end
|
@@ -290,7 +293,11 @@ module ActiveSupport
|
|
290
293
|
end
|
291
294
|
|
292
295
|
def -@ #:nodoc:
|
293
|
-
Duration.new(-value, parts.
|
296
|
+
Duration.new(-value, parts.transform_values(&:-@))
|
297
|
+
end
|
298
|
+
|
299
|
+
def +@ #:nodoc:
|
300
|
+
self
|
294
301
|
end
|
295
302
|
|
296
303
|
def is_a?(klass) #:nodoc:
|
@@ -343,6 +350,49 @@ module ActiveSupport
|
|
343
350
|
def to_i
|
344
351
|
@value.to_i
|
345
352
|
end
|
353
|
+
alias :in_seconds :to_i
|
354
|
+
|
355
|
+
# Returns the amount of minutes a duration covers as a float
|
356
|
+
#
|
357
|
+
# 1.day.in_minutes # => 1440.0
|
358
|
+
def in_minutes
|
359
|
+
in_seconds / SECONDS_PER_MINUTE.to_f
|
360
|
+
end
|
361
|
+
|
362
|
+
# Returns the amount of hours a duration covers as a float
|
363
|
+
#
|
364
|
+
# 1.day.in_hours # => 24.0
|
365
|
+
def in_hours
|
366
|
+
in_seconds / SECONDS_PER_HOUR.to_f
|
367
|
+
end
|
368
|
+
|
369
|
+
# Returns the amount of days a duration covers as a float
|
370
|
+
#
|
371
|
+
# 12.hours.in_days # => 0.5
|
372
|
+
def in_days
|
373
|
+
in_seconds / SECONDS_PER_DAY.to_f
|
374
|
+
end
|
375
|
+
|
376
|
+
# Returns the amount of weeks a duration covers as a float
|
377
|
+
#
|
378
|
+
# 2.months.in_weeks # => 8.696
|
379
|
+
def in_weeks
|
380
|
+
in_seconds / SECONDS_PER_WEEK.to_f
|
381
|
+
end
|
382
|
+
|
383
|
+
# Returns the amount of months a duration covers as a float
|
384
|
+
#
|
385
|
+
# 9.weeks.in_months # => 2.07
|
386
|
+
def in_months
|
387
|
+
in_seconds / SECONDS_PER_MONTH.to_f
|
388
|
+
end
|
389
|
+
|
390
|
+
# Returns the amount of years a duration covers as a float
|
391
|
+
#
|
392
|
+
# 30.days.in_years # => 0.082
|
393
|
+
def in_years
|
394
|
+
in_seconds / SECONDS_PER_YEAR.to_f
|
395
|
+
end
|
346
396
|
|
347
397
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
348
398
|
# same parts as this one.
|
@@ -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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/string_inquirer"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
class EnvironmentInquirer < StringInquirer #:nodoc:
|
7
|
+
DEFAULT_ENVIRONMENTS = ["development", "test", "production"]
|
8
|
+
def initialize(env)
|
9
|
+
super(env)
|
10
|
+
|
11
|
+
DEFAULT_ENVIRONMENTS.each do |default|
|
12
|
+
instance_variable_set :"@#{default}", env == default
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
DEFAULT_ENVIRONMENTS.each do |env|
|
17
|
+
class_eval "def #{env}?; @#{env}; end"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|