activesupport 5.1.1 → 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 +5 -5
- data/CHANGELOG.md +360 -442
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -4
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +2 -0
- data/lib/active_support/array_inquirer.rb +6 -2
- data/lib/active_support/backtrace_cleaner.rb +31 -3
- data/lib/active_support/benchmarkable.rb +3 -1
- data/lib/active_support/builder.rb +2 -0
- data/lib/active_support/cache/file_store.rb +37 -36
- data/lib/active_support/cache/mem_cache_store.rb +65 -53
- data/lib/active_support/cache/memory_store.rb +61 -33
- data/lib/active_support/cache/null_store.rb +10 -3
- data/lib/active_support/cache/redis_cache_store.rb +493 -0
- data/lib/active_support/cache/strategy/local_cache.rb +68 -22
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
- data/lib/active_support/cache.rb +305 -127
- data/lib/active_support/callbacks.rb +106 -98
- data/lib/active_support/concern.rb +79 -6
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -1
- data/lib/active_support/configurable.rb +12 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext/array/access.rb +21 -7
- data/lib/active_support/core_ext/array/conversions.rb +7 -5
- 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 +2 -0
- data/lib/active_support/core_ext/array/inquiry.rb +2 -0
- data/lib/active_support/core_ext/array/wrap.rb +2 -0
- data/lib/active_support/core_ext/array.rb +3 -1
- data/lib/active_support/core_ext/benchmark.rb +4 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +2 -0
- data/lib/active_support/core_ext/big_decimal.rb +2 -0
- data/lib/active_support/core_ext/class/attribute.rb +50 -47
- data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
- data/lib/active_support/core_ext/class/subclasses.rb +18 -40
- data/lib/active_support/core_ext/class.rb +2 -0
- data/lib/active_support/core_ext/date/acts_like.rb +2 -0
- data/lib/active_support/core_ext/date/blank.rb +2 -0
- data/lib/active_support/core_ext/date/calculations.rb +8 -5
- data/lib/active_support/core_ext/date/conversions.rb +12 -10
- data/lib/active_support/core_ext/date/zones.rb +2 -0
- data/lib/active_support/core_ext/date.rb +2 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +61 -37
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
- data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
- data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
- data/lib/active_support/core_ext/date_time/blank.rb +2 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
- data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_time.rb +2 -0
- data/lib/active_support/core_ext/digest/uuid.rb +3 -1
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +174 -71
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/file.rb +2 -0
- data/lib/active_support/core_ext/hash/conversions.rb +7 -5
- data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +4 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +2 -0
- data/lib/active_support/core_ext/hash/keys.rb +3 -30
- 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 +3 -2
- data/lib/active_support/core_ext/integer/inflections.rb +2 -0
- data/lib/active_support/core_ext/integer/multiple.rb +3 -1
- data/lib/active_support/core_ext/integer/time.rb +7 -14
- data/lib/active_support/core_ext/integer.rb +2 -0
- data/lib/active_support/core_ext/kernel/concern.rb +2 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +2 -0
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +2 -1
- data/lib/active_support/core_ext/load_error.rb +3 -8
- data/lib/active_support/core_ext/marshal.rb +4 -0
- data/lib/active_support/core_ext/module/aliasing.rb +2 -0
- data/lib/active_support/core_ext/module/anonymous.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +44 -56
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +18 -18
- data/lib/active_support/core_ext/module/concerning.rb +15 -10
- data/lib/active_support/core_ext/module/delegation.rb +103 -58
- data/lib/active_support/core_ext/module/deprecation.rb +2 -0
- data/lib/active_support/core_ext/module/introspection.rb +18 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +5 -23
- data/lib/active_support/core_ext/module.rb +3 -1
- data/lib/active_support/core_ext/name_error.rb +36 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +131 -129
- data/lib/active_support/core_ext/numeric/time.rb +7 -15
- data/lib/active_support/core_ext/numeric.rb +2 -1
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +13 -3
- data/lib/active_support/core_ext/object/conversions.rb +2 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
- data/lib/active_support/core_ext/object/duplicable.rb +6 -101
- data/lib/active_support/core_ext/object/inclusion.rb +2 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
- data/lib/active_support/core_ext/object/json.rb +22 -2
- data/lib/active_support/core_ext/object/to_param.rb +2 -0
- data/lib/active_support/core_ext/object/to_query.rb +7 -2
- data/lib/active_support/core_ext/object/try.rb +19 -7
- data/lib/active_support/core_ext/object/with_options.rb +4 -2
- data/lib/active_support/core_ext/object.rb +2 -0
- data/lib/active_support/core_ext/range/compare_range.rb +82 -0
- data/lib/active_support/core_ext/range/conversions.rb +35 -25
- data/lib/active_support/core_ext/range/each.rb +5 -2
- 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 +4 -1
- data/lib/active_support/core_ext/regexp.rb +10 -5
- data/lib/active_support/core_ext/securerandom.rb +25 -3
- data/lib/active_support/core_ext/string/access.rb +7 -16
- data/lib/active_support/core_ext/string/behavior.rb +2 -0
- data/lib/active_support/core_ext/string/conversions.rb +3 -0
- data/lib/active_support/core_ext/string/exclude.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +44 -1
- data/lib/active_support/core_ext/string/indent.rb +2 -0
- data/lib/active_support/core_ext/string/inflections.rb +69 -16
- data/lib/active_support/core_ext/string/inquiry.rb +3 -0
- data/lib/active_support/core_ext/string/multibyte.rb +9 -4
- data/lib/active_support/core_ext/string/output_safety.rb +76 -20
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
- data/lib/active_support/core_ext/string/strip.rb +5 -1
- data/lib/active_support/core_ext/string/zones.rb +2 -0
- data/lib/active_support/core_ext/string.rb +2 -0
- 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 +2 -0
- data/lib/active_support/core_ext/time/calculations.rb +73 -18
- data/lib/active_support/core_ext/time/compatibility.rb +4 -2
- data/lib/active_support/core_ext/time/conversions.rb +4 -0
- data/lib/active_support/core_ext/time/zones.rb +6 -4
- data/lib/active_support/core_ext/time.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +11 -6
- 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 +208 -0
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +2 -0
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/dependencies.rb +135 -60
- data/lib/active_support/deprecation/behaviors.rb +43 -11
- data/lib/active_support/deprecation/constant_accessor.rb +4 -2
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +2 -1
- data/lib/active_support/deprecation/method_wrappers.rb +30 -15
- data/lib/active_support/deprecation/proxy_wrappers.rb +32 -6
- data/lib/active_support/deprecation/reporting.rb +54 -9
- data/lib/active_support/deprecation.rb +9 -2
- data/lib/active_support/descendants_tracker.rb +61 -9
- data/lib/active_support/digest.rb +20 -0
- data/lib/active_support/duration/iso8601_parser.rb +6 -6
- data/lib/active_support/duration/iso8601_serializer.rb +20 -14
- data/lib/active_support/duration.rb +179 -41
- 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 +84 -117
- data/lib/active_support/execution_wrapper.rb +3 -0
- data/lib/active_support/executor.rb +2 -0
- data/lib/active_support/file_update_checker.rb +2 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +3 -1
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +134 -37
- data/lib/active_support/i18n.rb +4 -1
- data/lib/active_support/i18n_railtie.rb +20 -11
- data/lib/active_support/inflections.rb +2 -0
- data/lib/active_support/inflector/inflections.rb +19 -8
- data/lib/active_support/inflector/methods.rb +87 -77
- data/lib/active_support/inflector/transliterate.rb +56 -18
- data/lib/active_support/inflector.rb +2 -0
- data/lib/active_support/json/decoding.rb +27 -26
- data/lib/active_support/json/encoding.rb +13 -3
- data/lib/active_support/json.rb +2 -0
- data/lib/active_support/key_generator.rb +3 -33
- data/lib/active_support/lazy_load_hooks.rb +33 -10
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber/test_helper.rb +2 -0
- data/lib/active_support/log_subscriber.rb +46 -13
- data/lib/active_support/logger.rb +4 -17
- data/lib/active_support/logger_silence.rb +13 -20
- data/lib/active_support/logger_thread_safe_level.rb +54 -7
- data/lib/active_support/message_encryptor.rb +101 -33
- data/lib/active_support/message_verifier.rb +85 -14
- 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 +12 -68
- data/lib/active_support/multibyte/unicode.rb +17 -327
- data/lib/active_support/multibyte.rb +2 -0
- data/lib/active_support/notifications/fanout.rb +118 -16
- data/lib/active_support/notifications/instrumenter.rb +73 -9
- data/lib/active_support/notifications.rb +74 -8
- data/lib/active_support/number_helper/number_converter.rb +7 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +8 -7
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +16 -53
- data/lib/active_support/number_helper/rounding_helper.rb +50 -0
- data/lib/active_support/number_helper.rb +41 -12
- data/lib/active_support/option_merger.rb +24 -3
- data/lib/active_support/ordered_hash.rb +3 -1
- data/lib/active_support/ordered_options.rb +17 -5
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +3 -1
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +3 -10
- data/lib/active_support/railtie.rb +60 -9
- data/lib/active_support/reloader.rb +11 -10
- data/lib/active_support/rescuable.rb +7 -6
- 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 +6 -3
- data/lib/active_support/subscriber.rb +74 -24
- data/lib/active_support/tagged_logging.rb +44 -8
- data/lib/active_support/test_case.rb +94 -2
- data/lib/active_support/testing/assertions.rb +58 -20
- data/lib/active_support/testing/autorun.rb +2 -4
- data/lib/active_support/testing/constant_lookup.rb +2 -0
- data/lib/active_support/testing/declarative.rb +2 -0
- data/lib/active_support/testing/deprecation.rb +2 -1
- data/lib/active_support/testing/file_fixtures.rb +4 -0
- data/lib/active_support/testing/isolation.rb +8 -4
- data/lib/active_support/testing/method_call_assertions.rb +30 -1
- 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 +12 -7
- data/lib/active_support/testing/stream.rb +3 -2
- data/lib/active_support/testing/tagged_logging.rb +2 -0
- data/lib/active_support/testing/time_helpers.rb +78 -13
- data/lib/active_support/time.rb +2 -0
- data/lib/active_support/time_with_zone.rb +113 -41
- data/lib/active_support/values/time_zone.rb +55 -25
- data/lib/active_support/version.rb +2 -0
- data/lib/active_support/xml_mini/jdom.rb +5 -4
- data/lib/active_support/xml_mini/libxml.rb +4 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +6 -4
- data/lib/active_support/xml_mini/nokogiri.rb +4 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +5 -3
- data/lib/active_support/xml_mini/rexml.rb +12 -3
- data/lib/active_support/xml_mini.rb +5 -11
- data/lib/active_support.rb +18 -13
- metadata +81 -35
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
- data/lib/active_support/core_ext/hash/compact.rb +0 -27
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
- data/lib/active_support/core_ext/module/reachable.rb +0 -8
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
- data/lib/active_support/core_ext/range/include_range.rb +0 -23
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/array/conversions"
|
2
4
|
require "active_support/core_ext/module/delegation"
|
3
5
|
require "active_support/core_ext/object/acts_like"
|
4
6
|
require "active_support/core_ext/string/filters"
|
5
|
-
require "active_support/deprecation"
|
6
7
|
|
7
8
|
module ActiveSupport
|
8
9
|
# Provides accurate date and time measurements using Date#advance and
|
@@ -37,27 +38,61 @@ module ActiveSupport
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def +(other)
|
40
|
-
|
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
|
41
50
|
end
|
42
51
|
|
43
52
|
def -(other)
|
44
|
-
|
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
|
45
63
|
end
|
46
64
|
|
47
65
|
def *(other)
|
48
|
-
|
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
|
49
74
|
end
|
50
75
|
|
51
76
|
def /(other)
|
52
|
-
|
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
|
53
90
|
end
|
54
91
|
|
55
92
|
private
|
56
93
|
def calculate(op, other)
|
57
94
|
if Scalar === other
|
58
95
|
Scalar.new(value.public_send(op, other.value))
|
59
|
-
elsif Duration === other
|
60
|
-
Duration.seconds(value).public_send(op, other)
|
61
96
|
elsif Numeric === other
|
62
97
|
Scalar.new(value.public_send(op, other))
|
63
98
|
else
|
@@ -87,6 +122,8 @@ module ActiveSupport
|
|
87
122
|
years: SECONDS_PER_YEAR
|
88
123
|
}.freeze
|
89
124
|
|
125
|
+
PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
|
126
|
+
|
90
127
|
attr_accessor :value, :parts
|
91
128
|
|
92
129
|
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
|
@@ -95,7 +132,7 @@ module ActiveSupport
|
|
95
132
|
class << self
|
96
133
|
# Creates a new Duration from string formatted according to ISO 8601 Duration.
|
97
134
|
#
|
98
|
-
# See {ISO 8601}[
|
135
|
+
# See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
|
99
136
|
# This method allows negative parts to be present in pattern.
|
100
137
|
# If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
|
101
138
|
def parse(iso8601duration)
|
@@ -110,35 +147,61 @@ module ActiveSupport
|
|
110
147
|
end
|
111
148
|
|
112
149
|
def seconds(value) #:nodoc:
|
113
|
-
new(value,
|
150
|
+
new(value, seconds: value)
|
114
151
|
end
|
115
152
|
|
116
153
|
def minutes(value) #:nodoc:
|
117
|
-
new(value * SECONDS_PER_MINUTE,
|
154
|
+
new(value * SECONDS_PER_MINUTE, minutes: value)
|
118
155
|
end
|
119
156
|
|
120
157
|
def hours(value) #:nodoc:
|
121
|
-
new(value * SECONDS_PER_HOUR,
|
158
|
+
new(value * SECONDS_PER_HOUR, hours: value)
|
122
159
|
end
|
123
160
|
|
124
161
|
def days(value) #:nodoc:
|
125
|
-
new(value * SECONDS_PER_DAY,
|
162
|
+
new(value * SECONDS_PER_DAY, days: value)
|
126
163
|
end
|
127
164
|
|
128
165
|
def weeks(value) #:nodoc:
|
129
|
-
new(value * SECONDS_PER_WEEK,
|
166
|
+
new(value * SECONDS_PER_WEEK, weeks: value)
|
130
167
|
end
|
131
168
|
|
132
169
|
def months(value) #:nodoc:
|
133
|
-
new(value * SECONDS_PER_MONTH,
|
170
|
+
new(value * SECONDS_PER_MONTH, months: value)
|
134
171
|
end
|
135
172
|
|
136
173
|
def years(value) #:nodoc:
|
137
|
-
new(value * SECONDS_PER_YEAR,
|
174
|
+
new(value * SECONDS_PER_YEAR, years: value)
|
138
175
|
end
|
139
176
|
|
140
|
-
|
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 = value.round(9)
|
141
190
|
|
191
|
+
PARTS.each do |part|
|
192
|
+
unless part == :seconds
|
193
|
+
part_in_seconds = PARTS_IN_SECONDS[part]
|
194
|
+
parts[part] = remainder.div(part_in_seconds)
|
195
|
+
remainder %= part_in_seconds
|
196
|
+
end
|
197
|
+
end unless value == 0
|
198
|
+
|
199
|
+
parts[:seconds] = remainder
|
200
|
+
|
201
|
+
new(value, parts)
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
142
205
|
def calculate_total_seconds(parts)
|
143
206
|
parts.inject(0) do |total, (part, value)|
|
144
207
|
total + value * PARTS_IN_SECONDS[part]
|
@@ -147,13 +210,16 @@ module ActiveSupport
|
|
147
210
|
end
|
148
211
|
|
149
212
|
def initialize(value, parts) #:nodoc:
|
150
|
-
@value, @parts = value, parts
|
151
|
-
@parts.
|
213
|
+
@value, @parts = value, parts
|
214
|
+
@parts.reject! { |k, v| v.zero? } unless value == 0
|
152
215
|
end
|
153
216
|
|
154
217
|
def coerce(other) #:nodoc:
|
155
|
-
|
218
|
+
case other
|
219
|
+
when Scalar
|
156
220
|
[other, self]
|
221
|
+
when Duration
|
222
|
+
[Scalar.new(other.value), self]
|
157
223
|
else
|
158
224
|
[Scalar.new(other), self]
|
159
225
|
end
|
@@ -173,13 +239,12 @@ module ActiveSupport
|
|
173
239
|
# are treated as seconds.
|
174
240
|
def +(other)
|
175
241
|
if Duration === other
|
176
|
-
parts = @parts.
|
177
|
-
|
178
|
-
parts[key] += value
|
242
|
+
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
243
|
+
value + other_value
|
179
244
|
end
|
180
245
|
Duration.new(value + other.value, parts)
|
181
246
|
else
|
182
|
-
seconds = @parts
|
247
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
183
248
|
Duration.new(value + other, @parts.merge(seconds: seconds))
|
184
249
|
end
|
185
250
|
end
|
@@ -193,9 +258,9 @@ module ActiveSupport
|
|
193
258
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
194
259
|
def *(other)
|
195
260
|
if Scalar === other || Duration === other
|
196
|
-
Duration.new(value * other.value, parts.
|
261
|
+
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
197
262
|
elsif Numeric === other
|
198
|
-
Duration.new(value * other, parts.
|
263
|
+
Duration.new(value * other, parts.transform_values { |number| number * other })
|
199
264
|
else
|
200
265
|
raise_type_error(other)
|
201
266
|
end
|
@@ -203,17 +268,35 @@ module ActiveSupport
|
|
203
268
|
|
204
269
|
# Divides this Duration by a Numeric and returns a new Duration.
|
205
270
|
def /(other)
|
206
|
-
if Scalar === other
|
207
|
-
Duration.new(value / other.value, parts.
|
271
|
+
if Scalar === other
|
272
|
+
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
273
|
+
elsif Duration === other
|
274
|
+
value / other.value
|
275
|
+
elsif Numeric === other
|
276
|
+
Duration.new(value / other, parts.transform_values { |number| number / other })
|
277
|
+
else
|
278
|
+
raise_type_error(other)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Returns the modulo of this Duration by another Duration or Numeric.
|
283
|
+
# Numeric values are treated as seconds.
|
284
|
+
def %(other)
|
285
|
+
if Duration === other || Scalar === other
|
286
|
+
Duration.build(value % other.value)
|
208
287
|
elsif Numeric === other
|
209
|
-
Duration.
|
288
|
+
Duration.build(value % other)
|
210
289
|
else
|
211
290
|
raise_type_error(other)
|
212
291
|
end
|
213
292
|
end
|
214
293
|
|
215
294
|
def -@ #:nodoc:
|
216
|
-
Duration.new(-value, parts.
|
295
|
+
Duration.new(-value, parts.transform_values(&:-@))
|
296
|
+
end
|
297
|
+
|
298
|
+
def +@ #:nodoc:
|
299
|
+
self
|
217
300
|
end
|
218
301
|
|
219
302
|
def is_a?(klass) #:nodoc:
|
@@ -260,12 +343,55 @@ module ActiveSupport
|
|
260
343
|
# 1.year.to_i # => 31556952
|
261
344
|
#
|
262
345
|
# In such cases, Ruby's core
|
263
|
-
# Date[
|
264
|
-
# 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
|
265
348
|
# date and time arithmetic.
|
266
349
|
def to_i
|
267
350
|
@value.to_i
|
268
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
|
269
395
|
|
270
396
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
271
397
|
# same parts as this one.
|
@@ -294,9 +420,10 @@ module ActiveSupport
|
|
294
420
|
alias :before :ago
|
295
421
|
|
296
422
|
def inspect #:nodoc:
|
423
|
+
return "#{value} seconds" if parts.empty?
|
424
|
+
|
297
425
|
parts.
|
298
|
-
|
299
|
-
sort_by { |unit, _ | [:years, :months, :weeks, :days, :hours, :minutes, :seconds].index(unit) }.
|
426
|
+
sort_by { |unit, _ | PARTS.index(unit) }.
|
300
427
|
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
301
428
|
to_sentence(locale: ::I18n.default_locale)
|
302
429
|
end
|
@@ -305,8 +432,12 @@ module ActiveSupport
|
|
305
432
|
to_i
|
306
433
|
end
|
307
434
|
|
308
|
-
def
|
309
|
-
|
435
|
+
def init_with(coder) #:nodoc:
|
436
|
+
initialize(coder["value"], coder["parts"])
|
437
|
+
end
|
438
|
+
|
439
|
+
def encode_with(coder) #:nodoc:
|
440
|
+
coder.map = { "value" => @value, "parts" => @parts }
|
310
441
|
end
|
311
442
|
|
312
443
|
# Build ISO 8601 Duration string for this duration.
|
@@ -316,10 +447,15 @@ module ActiveSupport
|
|
316
447
|
end
|
317
448
|
|
318
449
|
private
|
319
|
-
|
320
450
|
def sum(sign, time = ::Time.current)
|
321
|
-
|
322
|
-
|
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)|
|
323
459
|
if type == :seconds
|
324
460
|
t.since(sign * number)
|
325
461
|
elsif type == :minutes
|
@@ -329,14 +465,16 @@ module ActiveSupport
|
|
329
465
|
else
|
330
466
|
t.advance(type => sign * number)
|
331
467
|
end
|
332
|
-
else
|
333
|
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
334
468
|
end
|
335
469
|
end
|
336
470
|
end
|
337
471
|
|
472
|
+
def respond_to_missing?(method, _)
|
473
|
+
value.respond_to?(method)
|
474
|
+
end
|
475
|
+
|
338
476
|
def method_missing(method, *args, &block)
|
339
|
-
value.
|
477
|
+
value.public_send(method, *args, &block)
|
340
478
|
end
|
341
479
|
|
342
480
|
def raise_type_error(other)
|
@@ -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
|