activesupport 4.2.11.1 → 6.1.7.3
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 +464 -391
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -7
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +50 -0
- data/lib/active_support/backtrace_cleaner.rb +34 -6
- data/lib/active_support/benchmarkable.rb +6 -4
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache/file_store.rb +61 -55
- data/lib/active_support/cache/mem_cache_store.rb +115 -100
- data/lib/active_support/cache/memory_store.rb +81 -58
- data/lib/active_support/cache/null_store.rb +11 -7
- data/lib/active_support/cache/redis_cache_store.rb +493 -0
- data/lib/active_support/cache/strategy/local_cache.rb +90 -42
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
- data/lib/active_support/cache.rb +386 -225
- data/lib/active_support/callbacks.rb +661 -594
- data/lib/active_support/concern.rb +80 -7
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +16 -17
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +41 -1
- data/lib/active_support/core_ext/array/conversions.rb +24 -20
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +11 -18
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +7 -4
- data/lib/active_support/core_ext/array.rb +9 -6
- data/lib/active_support/core_ext/benchmark.rb +5 -3
- data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/class/attribute.rb +52 -48
- data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
- data/lib/active_support/core_ext/class/subclasses.rb +18 -25
- data/lib/active_support/core_ext/class.rb +4 -3
- data/lib/active_support/core_ext/date/acts_like.rb +3 -1
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +17 -14
- data/lib/active_support/core_ext/date/conversions.rb +27 -24
- data/lib/active_support/core_ext/date/zones.rb +4 -2
- data/lib/active_support/core_ext/date.rb +6 -4
- data/lib/active_support/core_ext/date_and_time/calculations.rb +167 -65
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +19 -3
- data/lib/active_support/core_ext/date_and_time/zones.rb +12 -13
- data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +37 -19
- data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
- data/lib/active_support/core_ext/date_time/conversions.rb +16 -13
- data/lib/active_support/core_ext/date_time.rb +7 -5
- data/lib/active_support/core_ext/digest/uuid.rb +8 -5
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +186 -22
- data/lib/active_support/core_ext/file/atomic.rb +38 -31
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +62 -41
- data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +13 -10
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
- data/lib/active_support/core_ext/hash/keys.rb +20 -43
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -29
- data/lib/active_support/core_ext/hash.rb +10 -9
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +3 -1
- data/lib/active_support/core_ext/integer/time.rb +11 -18
- data/lib/active_support/core_ext/integer.rb +5 -3
- data/lib/active_support/core_ext/kernel/concern.rb +5 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +5 -5
- data/lib/active_support/core_ext/load_error.rb +3 -22
- data/lib/active_support/core_ext/marshal.rb +10 -8
- data/lib/active_support/core_ext/module/aliasing.rb +6 -44
- data/lib/active_support/core_ext/module/anonymous.rb +12 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
- data/lib/active_support/core_ext/module/attribute_accessors.rb +63 -69
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +148 -0
- data/lib/active_support/core_ext/module/concerning.rb +19 -14
- data/lib/active_support/core_ext/module/delegation.rb +164 -51
- data/lib/active_support/core_ext/module/deprecation.rb +4 -2
- data/lib/active_support/core_ext/module/introspection.rb +23 -22
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/module.rb +13 -11
- data/lib/active_support/core_ext/name_error.rb +51 -4
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +133 -136
- data/lib/active_support/core_ext/numeric/time.rb +35 -23
- data/lib/active_support/core_ext/numeric.rb +5 -3
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +27 -3
- data/lib/active_support/core_ext/object/conversions.rb +6 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
- data/lib/active_support/core_ext/object/duplicable.rb +13 -93
- data/lib/active_support/core_ext/object/inclusion.rb +5 -3
- data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
- data/lib/active_support/core_ext/object/json.rb +63 -21
- data/lib/active_support/core_ext/object/to_param.rb +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +10 -5
- data/lib/active_support/core_ext/object/try.rb +81 -23
- data/lib/active_support/core_ext/object/with_options.rb +16 -3
- data/lib/active_support/core_ext/object.rb +14 -13
- data/lib/active_support/core_ext/range/compare_range.rb +82 -0
- data/lib/active_support/core_ext/range/conversions.rb +37 -15
- data/lib/active_support/core_ext/range/each.rb +18 -17
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/range.rb +7 -4
- data/lib/active_support/core_ext/regexp.rb +10 -1
- data/lib/active_support/core_ext/securerandom.rb +45 -0
- data/lib/active_support/core_ext/string/access.rb +9 -18
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +8 -4
- data/lib/active_support/core_ext/string/exclude.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +48 -6
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +102 -26
- data/lib/active_support/core_ext/string/inquiry.rb +4 -1
- data/lib/active_support/core_ext/string/multibyte.rb +18 -9
- data/lib/active_support/core_ext/string/output_safety.rb +125 -40
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
- data/lib/active_support/core_ext/string/strip.rb +6 -5
- data/lib/active_support/core_ext/string/zones.rb +4 -2
- data/lib/active_support/core_ext/string.rb +15 -13
- 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/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +137 -53
- data/lib/active_support/core_ext/time/compatibility.rb +4 -2
- data/lib/active_support/core_ext/time/conversions.rb +22 -13
- data/lib/active_support/core_ext/time/zones.rb +41 -7
- data/lib/active_support/core_ext/time.rb +7 -6
- data/lib/active_support/core_ext/uri.rb +11 -8
- data/lib/active_support/core_ext.rb +3 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +210 -0
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +57 -0
- data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
- data/lib/active_support/dependencies.rb +241 -175
- data/lib/active_support/deprecation/behaviors.rb +58 -12
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +16 -2
- data/lib/active_support/deprecation/method_wrappers.rb +62 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
- data/lib/active_support/deprecation/reporting.rb +81 -18
- data/lib/active_support/deprecation.rb +17 -9
- data/lib/active_support/descendants_tracker.rb +61 -9
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +59 -0
- data/lib/active_support/duration.rb +364 -39
- data/lib/active_support/encrypted_configuration.rb +45 -0
- data/lib/active_support/encrypted_file.rb +117 -0
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +170 -0
- data/lib/active_support/execution_wrapper.rb +132 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +62 -37
- data/lib/active_support/fork_tracker.rb +64 -0
- data/lib/active_support/gem_version.rb +7 -5
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +171 -48
- data/lib/active_support/i18n.rb +9 -6
- data/lib/active_support/i18n_railtie.rb +47 -16
- data/lib/active_support/inflections.rb +13 -11
- data/lib/active_support/inflector/inflections.rb +58 -14
- data/lib/active_support/inflector/methods.rb +186 -169
- data/lib/active_support/inflector/transliterate.rb +83 -33
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/json/decoding.rb +32 -30
- data/lib/active_support/json/encoding.rb +22 -61
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/key_generator.rb +11 -43
- data/lib/active_support/lazy_load_hooks.rb +53 -20
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +9 -3
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/log_subscriber.rb +52 -19
- data/lib/active_support/logger.rb +10 -24
- data/lib/active_support/logger_silence.rb +14 -20
- data/lib/active_support/logger_thread_safe_level.rb +56 -10
- data/lib/active_support/message_encryptor.rb +167 -57
- data/lib/active_support/message_verifier.rb +151 -18
- data/lib/active_support/messages/metadata.rb +80 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotator.rb +57 -0
- data/lib/active_support/multibyte/chars.rb +35 -80
- data/lib/active_support/multibyte/unicode.rb +22 -330
- data/lib/active_support/multibyte.rb +4 -2
- data/lib/active_support/notifications/fanout.rb +125 -23
- data/lib/active_support/notifications/instrumenter.rb +98 -16
- data/lib/active_support/notifications.rb +82 -14
- data/lib/active_support/number_helper/number_converter.rb +17 -16
- data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -14
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
- data/lib/active_support/number_helper/number_to_human_converter.rb +14 -11
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +12 -10
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +15 -5
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +29 -57
- data/lib/active_support/number_helper/rounding_helper.rb +50 -0
- data/lib/active_support/number_helper.rb +123 -71
- data/lib/active_support/option_merger.rb +25 -4
- data/lib/active_support/ordered_hash.rb +7 -5
- data/lib/active_support/ordered_options.rb +35 -7
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +10 -4
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +10 -11
- data/lib/active_support/railtie.rb +66 -10
- data/lib/active_support/reloader.rb +130 -0
- data/lib/active_support/rescuable.rb +112 -57
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +26 -15
- data/lib/active_support/string_inquirer.rb +13 -4
- data/lib/active_support/subscriber.rb +80 -31
- data/lib/active_support/tagged_logging.rb +54 -17
- data/lib/active_support/test_case.rb +107 -44
- data/lib/active_support/testing/assertions.rb +158 -20
- data/lib/active_support/testing/autorun.rb +4 -2
- data/lib/active_support/testing/constant_lookup.rb +2 -1
- data/lib/active_support/testing/declarative.rb +3 -1
- data/lib/active_support/testing/deprecation.rb +13 -10
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +35 -26
- data/lib/active_support/testing/method_call_assertions.rb +70 -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/parallelization.rb +51 -0
- data/lib/active_support/testing/setup_and_teardown.rb +13 -8
- data/lib/active_support/testing/stream.rb +43 -0
- data/lib/active_support/testing/tagged_logging.rb +3 -1
- data/lib/active_support/testing/time_helpers.rb +121 -20
- data/lib/active_support/time.rb +14 -12
- data/lib/active_support/time_with_zone.rb +215 -51
- data/lib/active_support/values/time_zone.rb +223 -71
- data/lib/active_support/version.rb +3 -1
- data/lib/active_support/xml_mini/jdom.rb +116 -115
- data/lib/active_support/xml_mini/libxml.rb +16 -13
- data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
- data/lib/active_support/xml_mini/nokogiri.rb +14 -12
- data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
- data/lib/active_support/xml_mini/rexml.rb +18 -9
- data/lib/active_support/xml_mini.rb +38 -46
- data/lib/active_support.rb +25 -11
- metadata +100 -43
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
- data/lib/active_support/core_ext/date_time/zones.rb +0 -6
- data/lib/active_support/core_ext/hash/compact.rb +0 -24
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -23
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
- data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
- data/lib/active_support/core_ext/module/reachable.rb +0 -8
- data/lib/active_support/core_ext/object/itself.rb +0 -15
- data/lib/active_support/core_ext/range/include_range.rb +0 -23
- data/lib/active_support/core_ext/struct.rb +0 -6
- data/lib/active_support/core_ext/thread.rb +0 -86
- data/lib/active_support/core_ext/time/marshal.rb +0 -30
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,5 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/conversions"
|
4
|
+
require "active_support/core_ext/module/delegation"
|
5
|
+
require "active_support/core_ext/object/acts_like"
|
6
|
+
require "active_support/core_ext/string/filters"
|
3
7
|
|
4
8
|
module ActiveSupport
|
5
9
|
# Provides accurate date and time measurements using Date#advance and
|
@@ -7,19 +11,242 @@ module ActiveSupport
|
|
7
11
|
#
|
8
12
|
# 1.month.ago # equivalent to Time.now.advance(months: -1)
|
9
13
|
class Duration
|
14
|
+
class Scalar < Numeric #:nodoc:
|
15
|
+
attr_reader :value
|
16
|
+
delegate :to_i, :to_f, :to_s, to: :value
|
17
|
+
|
18
|
+
def initialize(value)
|
19
|
+
@value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def coerce(other)
|
23
|
+
[Scalar.new(other), self]
|
24
|
+
end
|
25
|
+
|
26
|
+
def -@
|
27
|
+
Scalar.new(-value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def <=>(other)
|
31
|
+
if Scalar === other || Duration === other
|
32
|
+
value <=> other.value
|
33
|
+
elsif Numeric === other
|
34
|
+
value <=> other
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def +(other)
|
41
|
+
if Duration === other
|
42
|
+
seconds = value + other.parts.fetch(:seconds, 0)
|
43
|
+
new_parts = other.parts.merge(seconds: seconds)
|
44
|
+
new_value = value + other.value
|
45
|
+
|
46
|
+
Duration.new(new_value, new_parts)
|
47
|
+
else
|
48
|
+
calculate(:+, other)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def -(other)
|
53
|
+
if Duration === other
|
54
|
+
seconds = value - other.parts.fetch(:seconds, 0)
|
55
|
+
new_parts = other.parts.transform_values(&:-@)
|
56
|
+
new_parts = new_parts.merge(seconds: seconds)
|
57
|
+
new_value = value - other.value
|
58
|
+
|
59
|
+
Duration.new(new_value, new_parts)
|
60
|
+
else
|
61
|
+
calculate(:-, other)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def *(other)
|
66
|
+
if Duration === other
|
67
|
+
new_parts = other.parts.transform_values { |other_value| value * other_value }
|
68
|
+
new_value = value * other.value
|
69
|
+
|
70
|
+
Duration.new(new_value, new_parts)
|
71
|
+
else
|
72
|
+
calculate(:*, other)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def /(other)
|
77
|
+
if Duration === other
|
78
|
+
value / other.value
|
79
|
+
else
|
80
|
+
calculate(:/, other)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def %(other)
|
85
|
+
if Duration === other
|
86
|
+
Duration.build(value % other.value)
|
87
|
+
else
|
88
|
+
calculate(:%, other)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def calculate(op, other)
|
94
|
+
if Scalar === other
|
95
|
+
Scalar.new(value.public_send(op, other.value))
|
96
|
+
elsif Numeric === other
|
97
|
+
Scalar.new(value.public_send(op, other))
|
98
|
+
else
|
99
|
+
raise_type_error(other)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def raise_type_error(other)
|
104
|
+
raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
SECONDS_PER_MINUTE = 60
|
109
|
+
SECONDS_PER_HOUR = 3600
|
110
|
+
SECONDS_PER_DAY = 86400
|
111
|
+
SECONDS_PER_WEEK = 604800
|
112
|
+
SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
|
113
|
+
SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days)
|
114
|
+
|
115
|
+
PARTS_IN_SECONDS = {
|
116
|
+
seconds: 1,
|
117
|
+
minutes: SECONDS_PER_MINUTE,
|
118
|
+
hours: SECONDS_PER_HOUR,
|
119
|
+
days: SECONDS_PER_DAY,
|
120
|
+
weeks: SECONDS_PER_WEEK,
|
121
|
+
months: SECONDS_PER_MONTH,
|
122
|
+
years: SECONDS_PER_YEAR
|
123
|
+
}.freeze
|
124
|
+
|
125
|
+
PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
|
126
|
+
|
10
127
|
attr_accessor :value, :parts
|
11
128
|
|
129
|
+
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
|
130
|
+
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
|
131
|
+
|
132
|
+
class << self
|
133
|
+
# Creates a new Duration from string formatted according to ISO 8601 Duration.
|
134
|
+
#
|
135
|
+
# See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
|
136
|
+
# This method allows negative parts to be present in pattern.
|
137
|
+
# If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
|
138
|
+
def parse(iso8601duration)
|
139
|
+
parts = ISO8601Parser.new(iso8601duration).parse!
|
140
|
+
new(calculate_total_seconds(parts), parts)
|
141
|
+
end
|
142
|
+
|
143
|
+
def ===(other) #:nodoc:
|
144
|
+
other.is_a?(Duration)
|
145
|
+
rescue ::NoMethodError
|
146
|
+
false
|
147
|
+
end
|
148
|
+
|
149
|
+
def seconds(value) #:nodoc:
|
150
|
+
new(value, seconds: value)
|
151
|
+
end
|
152
|
+
|
153
|
+
def minutes(value) #:nodoc:
|
154
|
+
new(value * SECONDS_PER_MINUTE, minutes: value)
|
155
|
+
end
|
156
|
+
|
157
|
+
def hours(value) #:nodoc:
|
158
|
+
new(value * SECONDS_PER_HOUR, hours: value)
|
159
|
+
end
|
160
|
+
|
161
|
+
def days(value) #:nodoc:
|
162
|
+
new(value * SECONDS_PER_DAY, days: value)
|
163
|
+
end
|
164
|
+
|
165
|
+
def weeks(value) #:nodoc:
|
166
|
+
new(value * SECONDS_PER_WEEK, weeks: value)
|
167
|
+
end
|
168
|
+
|
169
|
+
def months(value) #:nodoc:
|
170
|
+
new(value * SECONDS_PER_MONTH, months: value)
|
171
|
+
end
|
172
|
+
|
173
|
+
def years(value) #:nodoc:
|
174
|
+
new(value * SECONDS_PER_YEAR, years: value)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Creates a new Duration from a seconds value that is converted
|
178
|
+
# to the individual parts:
|
179
|
+
#
|
180
|
+
# ActiveSupport::Duration.build(31556952).parts # => {:years=>1}
|
181
|
+
# ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
|
182
|
+
#
|
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
|
+
|
188
|
+
parts = {}
|
189
|
+
remainder_sign = value <=> 0
|
190
|
+
remainder = value.round(9).abs
|
191
|
+
|
192
|
+
PARTS.each do |part|
|
193
|
+
unless part == :seconds
|
194
|
+
part_in_seconds = PARTS_IN_SECONDS[part]
|
195
|
+
parts[part] = remainder.div(part_in_seconds) * remainder_sign
|
196
|
+
remainder %= part_in_seconds
|
197
|
+
end
|
198
|
+
end unless value == 0
|
199
|
+
|
200
|
+
parts[:seconds] = remainder * remainder_sign
|
201
|
+
|
202
|
+
new(value, parts)
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
def calculate_total_seconds(parts)
|
207
|
+
parts.inject(0) do |total, (part, value)|
|
208
|
+
total + value * PARTS_IN_SECONDS[part]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
12
213
|
def initialize(value, parts) #:nodoc:
|
13
214
|
@value, @parts = value, parts
|
215
|
+
@parts.reject! { |k, v| v.zero? } unless value == 0
|
216
|
+
end
|
217
|
+
|
218
|
+
def coerce(other) #:nodoc:
|
219
|
+
case other
|
220
|
+
when Scalar
|
221
|
+
[other, self]
|
222
|
+
when Duration
|
223
|
+
[Scalar.new(other.value), self]
|
224
|
+
else
|
225
|
+
[Scalar.new(other), self]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Compares one Duration with another or a Numeric to this Duration.
|
230
|
+
# Numeric values are treated as seconds.
|
231
|
+
def <=>(other)
|
232
|
+
if Duration === other
|
233
|
+
value <=> other.value
|
234
|
+
elsif Numeric === other
|
235
|
+
value <=> other
|
236
|
+
end
|
14
237
|
end
|
15
238
|
|
16
239
|
# Adds another Duration or a Numeric to this Duration. Numeric values
|
17
240
|
# are treated as seconds.
|
18
241
|
def +(other)
|
19
242
|
if Duration === other
|
20
|
-
|
243
|
+
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
244
|
+
value + other_value
|
245
|
+
end
|
246
|
+
Duration.new(value + other.value, parts)
|
21
247
|
else
|
22
|
-
|
248
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
249
|
+
Duration.new(value + other, @parts.merge(seconds: seconds))
|
23
250
|
end
|
24
251
|
end
|
25
252
|
|
@@ -29,8 +256,48 @@ module ActiveSupport
|
|
29
256
|
self + (-other)
|
30
257
|
end
|
31
258
|
|
259
|
+
# Multiplies this Duration by a Numeric and returns a new Duration.
|
260
|
+
def *(other)
|
261
|
+
if Scalar === other || Duration === other
|
262
|
+
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
263
|
+
elsif Numeric === other
|
264
|
+
Duration.new(value * other, parts.transform_values { |number| number * other })
|
265
|
+
else
|
266
|
+
raise_type_error(other)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Divides this Duration by a Numeric and returns a new Duration.
|
271
|
+
def /(other)
|
272
|
+
if Scalar === other
|
273
|
+
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
274
|
+
elsif Duration === other
|
275
|
+
value / other.value
|
276
|
+
elsif Numeric === other
|
277
|
+
Duration.new(value / other, parts.transform_values { |number| number / other })
|
278
|
+
else
|
279
|
+
raise_type_error(other)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# Returns the modulo of this Duration by another Duration or Numeric.
|
284
|
+
# Numeric values are treated as seconds.
|
285
|
+
def %(other)
|
286
|
+
if Duration === other || Scalar === other
|
287
|
+
Duration.build(value % other.value)
|
288
|
+
elsif Numeric === other
|
289
|
+
Duration.build(value % other)
|
290
|
+
else
|
291
|
+
raise_type_error(other)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
32
295
|
def -@ #:nodoc:
|
33
|
-
Duration.new(-value, parts.
|
296
|
+
Duration.new(-value, parts.transform_values(&:-@))
|
297
|
+
end
|
298
|
+
|
299
|
+
def +@ #:nodoc:
|
300
|
+
self
|
34
301
|
end
|
35
302
|
|
36
303
|
def is_a?(klass) #:nodoc:
|
@@ -52,6 +319,10 @@ module ActiveSupport
|
|
52
319
|
end
|
53
320
|
end
|
54
321
|
|
322
|
+
# Returns the amount of seconds a duration covers as a string.
|
323
|
+
# For more information check to_i method.
|
324
|
+
#
|
325
|
+
# 1.day.to_s # => "86400"
|
55
326
|
def to_s
|
56
327
|
@value.to_s
|
57
328
|
end
|
@@ -63,22 +334,65 @@ module ActiveSupport
|
|
63
334
|
# 1.day.to_i # => 86400
|
64
335
|
#
|
65
336
|
# Note that this conversion makes some assumptions about the
|
66
|
-
# duration of some periods, e.g. months are always
|
67
|
-
# and years are 365.
|
337
|
+
# duration of some periods, e.g. months are always 1/12 of year
|
338
|
+
# and years are 365.2425 days:
|
68
339
|
#
|
69
|
-
# # equivalent to
|
70
|
-
# 1.month.to_i # =>
|
340
|
+
# # equivalent to (1.year / 12).to_i
|
341
|
+
# 1.month.to_i # => 2629746
|
71
342
|
#
|
72
|
-
# # equivalent to 365.
|
73
|
-
# 1.year.to_i # =>
|
343
|
+
# # equivalent to 365.2425.days.to_i
|
344
|
+
# 1.year.to_i # => 31556952
|
74
345
|
#
|
75
346
|
# In such cases, Ruby's core
|
76
|
-
# Date[
|
77
|
-
# Time[
|
347
|
+
# Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
|
348
|
+
# Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
|
78
349
|
# date and time arithmetic.
|
79
350
|
def to_i
|
80
351
|
@value.to_i
|
81
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
|
82
396
|
|
83
397
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
84
398
|
# same parts as this one.
|
@@ -90,18 +404,13 @@ module ActiveSupport
|
|
90
404
|
@value.hash
|
91
405
|
end
|
92
406
|
|
93
|
-
def self.===(other) #:nodoc:
|
94
|
-
other.is_a?(Duration)
|
95
|
-
rescue ::NoMethodError
|
96
|
-
false
|
97
|
-
end
|
98
|
-
|
99
407
|
# Calculates a new Time or Date that is as far in the future
|
100
408
|
# as this Duration represents.
|
101
409
|
def since(time = ::Time.current)
|
102
410
|
sum(1, time)
|
103
411
|
end
|
104
412
|
alias :from_now :since
|
413
|
+
alias :after :since
|
105
414
|
|
106
415
|
# Calculates a new Time or Date that is as far in the past
|
107
416
|
# as this Duration represents.
|
@@ -109,12 +418,14 @@ module ActiveSupport
|
|
109
418
|
sum(-1, time)
|
110
419
|
end
|
111
420
|
alias :until :ago
|
421
|
+
alias :before :ago
|
112
422
|
|
113
423
|
def inspect #:nodoc:
|
424
|
+
return "#{value} seconds" if parts.empty?
|
425
|
+
|
114
426
|
parts.
|
115
|
-
|
116
|
-
|
117
|
-
map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
|
427
|
+
sort_by { |unit, _ | PARTS.index(unit) }.
|
428
|
+
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
118
429
|
to_sentence(locale: ::I18n.default_locale)
|
119
430
|
end
|
120
431
|
|
@@ -122,39 +433,53 @@ module ActiveSupport
|
|
122
433
|
to_i
|
123
434
|
end
|
124
435
|
|
125
|
-
def
|
126
|
-
|
436
|
+
def init_with(coder) #:nodoc:
|
437
|
+
initialize(coder["value"], coder["parts"])
|
127
438
|
end
|
128
439
|
|
129
|
-
|
440
|
+
def encode_with(coder) #:nodoc:
|
441
|
+
coder.map = { "value" => @value, "parts" => @parts }
|
442
|
+
end
|
130
443
|
|
131
|
-
|
444
|
+
# Build ISO 8601 Duration string for this duration.
|
445
|
+
# The +precision+ parameter can be used to limit seconds' precision of duration.
|
446
|
+
def iso8601(precision: nil)
|
447
|
+
ISO8601Serializer.new(self, precision: precision).serialize
|
448
|
+
end
|
132
449
|
|
133
|
-
|
134
|
-
|
135
|
-
|
450
|
+
private
|
451
|
+
def sum(sign, time = ::Time.current)
|
452
|
+
unless time.acts_like?(:time) || time.acts_like?(:date)
|
453
|
+
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
454
|
+
end
|
455
|
+
|
456
|
+
if parts.empty?
|
457
|
+
time.since(sign * value)
|
458
|
+
else
|
459
|
+
parts.inject(time) do |t, (type, number)|
|
136
460
|
if type == :seconds
|
137
461
|
t.since(sign * number)
|
462
|
+
elsif type == :minutes
|
463
|
+
t.since(sign * number * 60)
|
464
|
+
elsif type == :hours
|
465
|
+
t.since(sign * number * 3600)
|
138
466
|
else
|
139
467
|
t.advance(type => sign * number)
|
140
468
|
end
|
141
|
-
else
|
142
|
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
143
469
|
end
|
144
470
|
end
|
145
471
|
end
|
146
472
|
|
147
|
-
|
473
|
+
def respond_to_missing?(method, _)
|
474
|
+
value.respond_to?(method)
|
475
|
+
end
|
148
476
|
|
149
|
-
|
150
|
-
|
151
|
-
# Remove it when we drop support for 2.0.0-p353.
|
152
|
-
def ===(other) #:nodoc:
|
153
|
-
value === other
|
477
|
+
def method_missing(method, *args, &block)
|
478
|
+
value.public_send(method, *args, &block)
|
154
479
|
end
|
155
480
|
|
156
|
-
def
|
157
|
-
|
481
|
+
def raise_type_error(other)
|
482
|
+
raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
|
158
483
|
end
|
159
484
|
end
|
160
485
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "active_support/encrypted_file"
|
5
|
+
require "active_support/ordered_options"
|
6
|
+
require "active_support/core_ext/object/inclusion"
|
7
|
+
require "active_support/core_ext/module/delegation"
|
8
|
+
|
9
|
+
module ActiveSupport
|
10
|
+
class EncryptedConfiguration < EncryptedFile
|
11
|
+
delegate :[], :fetch, to: :config
|
12
|
+
delegate_missing_to :options
|
13
|
+
|
14
|
+
def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
|
15
|
+
super content_path: config_path, key_path: key_path,
|
16
|
+
env_key: env_key, raise_if_missing_key: raise_if_missing_key
|
17
|
+
end
|
18
|
+
|
19
|
+
# Allow a config to be started without a file present
|
20
|
+
def read
|
21
|
+
super
|
22
|
+
rescue ActiveSupport::EncryptedFile::MissingContentError
|
23
|
+
""
|
24
|
+
end
|
25
|
+
|
26
|
+
def write(contents)
|
27
|
+
deserialize(contents)
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def config
|
33
|
+
@config ||= deserialize(read).deep_symbolize_keys
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def options
|
38
|
+
@options ||= ActiveSupport::InheritableOptions.new(config)
|
39
|
+
end
|
40
|
+
|
41
|
+
def deserialize(config)
|
42
|
+
YAML.load(config).presence || {}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "tmpdir"
|
5
|
+
require "active_support/message_encryptor"
|
6
|
+
|
7
|
+
module ActiveSupport
|
8
|
+
class EncryptedFile
|
9
|
+
class MissingContentError < RuntimeError
|
10
|
+
def initialize(content_path)
|
11
|
+
super "Missing encrypted content file in #{content_path}."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MissingKeyError < RuntimeError
|
16
|
+
def initialize(key_path:, env_key:)
|
17
|
+
super \
|
18
|
+
"Missing encryption key to decrypt file with. " +
|
19
|
+
"Ask your team for your master key and write it to #{key_path} or put it in the ENV['#{env_key}']."
|
20
|
+
end
|
21
|
+
end
|
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
|
+
|
29
|
+
CIPHER = "aes-128-gcm"
|
30
|
+
|
31
|
+
def self.generate_key
|
32
|
+
SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.expected_key_length # :nodoc:
|
36
|
+
@expected_key_length ||= generate_key.length
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
|
41
|
+
|
42
|
+
def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
|
43
|
+
@content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path }
|
44
|
+
@key_path = Pathname.new(key_path)
|
45
|
+
@env_key, @raise_if_missing_key = env_key, raise_if_missing_key
|
46
|
+
end
|
47
|
+
|
48
|
+
def key
|
49
|
+
read_env_key || read_key_file || handle_missing_key
|
50
|
+
end
|
51
|
+
|
52
|
+
def read
|
53
|
+
if !key.nil? && content_path.exist?
|
54
|
+
decrypt content_path.binread
|
55
|
+
else
|
56
|
+
raise MissingContentError, content_path
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def write(contents)
|
61
|
+
IO.binwrite "#{content_path}.tmp", encrypt(contents)
|
62
|
+
FileUtils.mv "#{content_path}.tmp", content_path
|
63
|
+
end
|
64
|
+
|
65
|
+
def change(&block)
|
66
|
+
writing read, &block
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
private
|
71
|
+
def writing(contents)
|
72
|
+
tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
|
73
|
+
tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
|
74
|
+
tmp_path.binwrite contents
|
75
|
+
|
76
|
+
yield tmp_path
|
77
|
+
|
78
|
+
updated_contents = tmp_path.binread
|
79
|
+
|
80
|
+
write(updated_contents) if updated_contents != contents
|
81
|
+
ensure
|
82
|
+
FileUtils.rm(tmp_path) if tmp_path&.exist?
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def encrypt(contents)
|
87
|
+
check_key_length
|
88
|
+
encryptor.encrypt_and_sign contents
|
89
|
+
end
|
90
|
+
|
91
|
+
def decrypt(contents)
|
92
|
+
encryptor.decrypt_and_verify contents
|
93
|
+
end
|
94
|
+
|
95
|
+
def encryptor
|
96
|
+
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def read_env_key
|
101
|
+
ENV[env_key]
|
102
|
+
end
|
103
|
+
|
104
|
+
def read_key_file
|
105
|
+
return @key_file_contents if defined?(@key_file_contents)
|
106
|
+
@key_file_contents = (key_path.binread.strip if key_path.exist?)
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_missing_key
|
110
|
+
raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
|
111
|
+
end
|
112
|
+
|
113
|
+
def check_key_length
|
114
|
+
raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
|
115
|
+
end
|
116
|
+
end
|
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
|