activesupport 5.1.7 → 7.0.4.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 +259 -585
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -5
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +2 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +33 -5
- data/lib/active_support/benchmarkable.rb +5 -3
- data/lib/active_support/builder.rb +2 -0
- data/lib/active_support/cache/file_store.rb +50 -43
- data/lib/active_support/cache/mem_cache_store.rb +194 -67
- data/lib/active_support/cache/memory_store.rb +70 -34
- data/lib/active_support/cache/null_store.rb +18 -3
- data/lib/active_support/cache/redis_cache_store.rb +474 -0
- data/lib/active_support/cache/strategy/local_cache.rb +73 -50
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
- data/lib/active_support/cache.rb +556 -220
- data/lib/active_support/callbacks.rb +264 -159
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +81 -8
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
- data/lib/active_support/concurrency/share_lock.rb +4 -3
- data/lib/active_support/configurable.rb +17 -16
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +18 -8
- data/lib/active_support/core_ext/array/conversions.rb +20 -17
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- 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 +8 -6
- data/lib/active_support/core_ext/array/inquiry.rb +4 -2
- data/lib/active_support/core_ext/array/wrap.rb +2 -0
- data/lib/active_support/core_ext/array.rb +4 -1
- data/lib/active_support/core_ext/benchmark.rb +4 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +3 -1
- 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 +10 -24
- 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 +3 -1
- data/lib/active_support/core_ext/date/calculations.rb +17 -14
- data/lib/active_support/core_ext/date/conversions.rb +24 -22
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date/zones.rb +2 -0
- data/lib/active_support/core_ext/date.rb +3 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +65 -41
- 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 +3 -1
- 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 +15 -14
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +3 -0
- data/lib/active_support/core_ext/digest/uuid.rb +42 -14
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +244 -72
- data/lib/active_support/core_ext/file/atomic.rb +6 -2
- data/lib/active_support/core_ext/file.rb +2 -0
- data/lib/active_support/core_ext/hash/conversions.rb +7 -6
- 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 +5 -3
- data/lib/active_support/core_ext/hash/keys.rb +4 -31
- 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 +6 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +3 -1
- 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/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 +46 -56
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +36 -27
- data/lib/active_support/core_ext/module/concerning.rb +15 -10
- data/lib/active_support/core_ext/module/delegation.rb +97 -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 +30 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +134 -129
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric/time.rb +7 -15
- data/lib/active_support/core_ext/numeric.rb +3 -1
- data/lib/active_support/core_ext/object/acts_like.rb +41 -6
- data/lib/active_support/core_ext/object/blank.rb +15 -5
- 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 +16 -110
- 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 +51 -26
- data/lib/active_support/core_ext/object/to_param.rb +2 -0
- data/lib/active_support/core_ext/object/to_query.rb +4 -2
- data/lib/active_support/core_ext/object/try.rb +26 -14
- data/lib/active_support/core_ext/object/with_options.rb +24 -3
- data/lib/active_support/core_ext/object.rb +2 -0
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +35 -25
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +6 -3
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
- data/lib/active_support/core_ext/range/overlaps.rb +3 -1
- 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 +5 -2
- 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 +4 -1
- data/lib/active_support/core_ext/string/multibyte.rb +9 -4
- data/lib/active_support/core_ext/string/output_safety.rb +135 -27
- 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 +6 -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 +81 -24
- data/lib/active_support/core_ext/time/compatibility.rb +4 -2
- data/lib/active_support/core_ext/time/conversions.rb +17 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +12 -25
- data/lib/active_support/core_ext/time.rb +3 -0
- data/lib/active_support/core_ext/uri.rb +4 -23
- data/lib/active_support/core_ext.rb +4 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +226 -0
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +12 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +59 -715
- data/lib/active_support/deprecation/behaviors.rb +48 -13
- 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 +29 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -8
- data/lib/active_support/deprecation/reporting.rb +54 -9
- data/lib/active_support/deprecation.rb +10 -3
- data/lib/active_support/descendants_tracker.rb +192 -34
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +9 -9
- data/lib/active_support/duration/iso8601_serializer.rb +29 -15
- data/lib/active_support/duration.rb +158 -72
- data/lib/active_support/encrypted_configuration.rb +56 -0
- data/lib/active_support/encrypted_file.rb +129 -0
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +87 -122
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +46 -21
- data/lib/active_support/executor/test_helper.rb +7 -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 +71 -0
- data/lib/active_support/gem_version.rb +7 -5
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +126 -42
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +5 -1
- data/lib/active_support/i18n_railtie.rb +19 -14
- data/lib/active_support/inflections.rb +2 -0
- data/lib/active_support/inflector/inflections.rb +41 -14
- data/lib/active_support/inflector/methods.rb +73 -87
- data/lib/active_support/inflector/transliterate.rb +56 -18
- data/lib/active_support/inflector.rb +2 -0
- data/lib/active_support/isolated_execution_state.rb +72 -0
- data/lib/active_support/json/decoding.rb +27 -26
- data/lib/active_support/json/encoding.rb +16 -6
- data/lib/active_support/json.rb +2 -0
- data/lib/active_support/key_generator.rb +25 -38
- data/lib/active_support/lazy_load_hooks.rb +35 -6
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber/test_helper.rb +4 -2
- data/lib/active_support/log_subscriber.rb +54 -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 +48 -10
- data/lib/active_support/message_encryptor.rb +111 -37
- data/lib/active_support/message_verifier.rb +124 -21
- 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 +19 -76
- data/lib/active_support/multibyte/unicode.rb +9 -331
- data/lib/active_support/multibyte.rb +3 -1
- data/lib/active_support/notifications/fanout.rb +165 -37
- data/lib/active_support/notifications/instrumenter.rb +92 -11
- data/lib/active_support/notifications.rb +96 -30
- data/lib/active_support/number_helper/number_converter.rb +8 -9
- data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -12
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +6 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +7 -4
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +6 -3
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
- data/lib/active_support/number_helper/rounding_helper.rb +16 -34
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/option_merger.rb +19 -6
- data/lib/active_support/ordered_hash.rb +4 -2
- data/lib/active_support/ordered_options.rb +18 -6
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +8 -1
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +3 -10
- data/lib/active_support/railtie.rb +112 -11
- data/lib/active_support/reloader.rb +12 -11
- data/lib/active_support/rescuable.rb +19 -18
- data/lib/active_support/ruby_features.rb +7 -0
- 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 +4 -3
- data/lib/active_support/subscriber.rb +81 -42
- data/lib/active_support/tagged_logging.rb +45 -9
- data/lib/active_support/test_case.rb +86 -2
- data/lib/active_support/testing/assertions.rb +89 -21
- data/lib/active_support/testing/autorun.rb +2 -0
- 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 +54 -2
- data/lib/active_support/testing/file_fixtures.rb +4 -0
- data/lib/active_support/testing/isolation.rb +6 -4
- data/lib/active_support/testing/method_call_assertions.rb +34 -5
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +55 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/setup_and_teardown.rb +12 -7
- data/lib/active_support/testing/stream.rb +6 -7
- data/lib/active_support/testing/tagged_logging.rb +3 -1
- data/lib/active_support/testing/time_helpers.rb +91 -15
- data/lib/active_support/time.rb +2 -0
- data/lib/active_support/time_with_zone.rb +168 -56
- data/lib/active_support/values/time_zone.rb +85 -37
- data/lib/active_support/version.rb +3 -1
- data/lib/active_support/xml_mini/jdom.rb +6 -5
- data/lib/active_support/xml_mini/libxml.rb +9 -7
- data/lib/active_support/xml_mini/libxmlsax.rb +7 -5
- data/lib/active_support/xml_mini/nokogiri.rb +8 -6
- data/lib/active_support/xml_mini/nokogirisax.rb +6 -4
- data/lib/active_support/xml_mini/rexml.rb +13 -4
- data/lib/active_support/xml_mini.rb +10 -15
- data/lib/active_support.rb +30 -9
- metadata +76 -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/marshal.rb +0 -22
- 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
|
@@ -10,7 +11,7 @@ module ActiveSupport
|
|
10
11
|
#
|
11
12
|
# 1.month.ago # equivalent to Time.now.advance(months: -1)
|
12
13
|
class Duration
|
13
|
-
class Scalar < Numeric
|
14
|
+
class Scalar < Numeric # :nodoc:
|
14
15
|
attr_reader :value
|
15
16
|
delegate :to_i, :to_f, :to_s, to: :value
|
16
17
|
|
@@ -38,11 +39,11 @@ module ActiveSupport
|
|
38
39
|
|
39
40
|
def +(other)
|
40
41
|
if Duration === other
|
41
|
-
seconds = value + other.
|
42
|
-
new_parts = other.
|
42
|
+
seconds = value + other._parts.fetch(:seconds, 0)
|
43
|
+
new_parts = other._parts.merge(seconds: seconds)
|
43
44
|
new_value = value + other.value
|
44
45
|
|
45
|
-
Duration.new(new_value, new_parts)
|
46
|
+
Duration.new(new_value, new_parts, other.variable?)
|
46
47
|
else
|
47
48
|
calculate(:+, other)
|
48
49
|
end
|
@@ -50,12 +51,12 @@ module ActiveSupport
|
|
50
51
|
|
51
52
|
def -(other)
|
52
53
|
if Duration === other
|
53
|
-
seconds = value - other.
|
54
|
-
new_parts = other.
|
54
|
+
seconds = value - other._parts.fetch(:seconds, 0)
|
55
|
+
new_parts = other._parts.transform_values(&:-@)
|
55
56
|
new_parts = new_parts.merge(seconds: seconds)
|
56
57
|
new_value = value - other.value
|
57
58
|
|
58
|
-
Duration.new(new_value, new_parts)
|
59
|
+
Duration.new(new_value, new_parts, other.variable?)
|
59
60
|
else
|
60
61
|
calculate(:-, other)
|
61
62
|
end
|
@@ -63,10 +64,10 @@ module ActiveSupport
|
|
63
64
|
|
64
65
|
def *(other)
|
65
66
|
if Duration === other
|
66
|
-
new_parts = other.
|
67
|
+
new_parts = other._parts.transform_values { |other_value| value * other_value }
|
67
68
|
new_value = value * other.value
|
68
69
|
|
69
|
-
Duration.new(new_value, new_parts)
|
70
|
+
Duration.new(new_value, new_parts, other.variable?)
|
70
71
|
else
|
71
72
|
calculate(:*, other)
|
72
73
|
end
|
@@ -88,6 +89,10 @@ module ActiveSupport
|
|
88
89
|
end
|
89
90
|
end
|
90
91
|
|
92
|
+
def variable? # :nodoc:
|
93
|
+
false
|
94
|
+
end
|
95
|
+
|
91
96
|
private
|
92
97
|
def calculate(op, other)
|
93
98
|
if Scalar === other
|
@@ -122,8 +127,9 @@ module ActiveSupport
|
|
122
127
|
}.freeze
|
123
128
|
|
124
129
|
PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
|
130
|
+
VARIABLE_PARTS = [:years, :months, :weeks, :days].freeze
|
125
131
|
|
126
|
-
|
132
|
+
attr_reader :value
|
127
133
|
|
128
134
|
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
|
129
135
|
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
|
@@ -131,7 +137,7 @@ module ActiveSupport
|
|
131
137
|
class << self
|
132
138
|
# Creates a new Duration from string formatted according to ISO 8601 Duration.
|
133
139
|
#
|
134
|
-
# See {ISO 8601}[
|
140
|
+
# See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
|
135
141
|
# This method allows negative parts to be present in pattern.
|
136
142
|
# If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
|
137
143
|
def parse(iso8601duration)
|
@@ -139,38 +145,38 @@ module ActiveSupport
|
|
139
145
|
new(calculate_total_seconds(parts), parts)
|
140
146
|
end
|
141
147
|
|
142
|
-
def ===(other)
|
148
|
+
def ===(other) # :nodoc:
|
143
149
|
other.is_a?(Duration)
|
144
150
|
rescue ::NoMethodError
|
145
151
|
false
|
146
152
|
end
|
147
153
|
|
148
|
-
def seconds(value)
|
149
|
-
new(value,
|
154
|
+
def seconds(value) # :nodoc:
|
155
|
+
new(value, { seconds: value }, false)
|
150
156
|
end
|
151
157
|
|
152
|
-
def minutes(value)
|
153
|
-
new(value * SECONDS_PER_MINUTE,
|
158
|
+
def minutes(value) # :nodoc:
|
159
|
+
new(value * SECONDS_PER_MINUTE, { minutes: value }, false)
|
154
160
|
end
|
155
161
|
|
156
|
-
def hours(value)
|
157
|
-
new(value * SECONDS_PER_HOUR,
|
162
|
+
def hours(value) # :nodoc:
|
163
|
+
new(value * SECONDS_PER_HOUR, { hours: value }, false)
|
158
164
|
end
|
159
165
|
|
160
|
-
def days(value)
|
161
|
-
new(value * SECONDS_PER_DAY,
|
166
|
+
def days(value) # :nodoc:
|
167
|
+
new(value * SECONDS_PER_DAY, { days: value }, true)
|
162
168
|
end
|
163
169
|
|
164
|
-
def weeks(value)
|
165
|
-
new(value * SECONDS_PER_WEEK,
|
170
|
+
def weeks(value) # :nodoc:
|
171
|
+
new(value * SECONDS_PER_WEEK, { weeks: value }, true)
|
166
172
|
end
|
167
173
|
|
168
|
-
def months(value)
|
169
|
-
new(value * SECONDS_PER_MONTH,
|
174
|
+
def months(value) # :nodoc:
|
175
|
+
new(value * SECONDS_PER_MONTH, { months: value }, true)
|
170
176
|
end
|
171
177
|
|
172
|
-
def years(value)
|
173
|
-
new(value * SECONDS_PER_YEAR,
|
178
|
+
def years(value) # :nodoc:
|
179
|
+
new(value * SECONDS_PER_YEAR, { years: value }, true)
|
174
180
|
end
|
175
181
|
|
176
182
|
# Creates a new Duration from a seconds value that is converted
|
@@ -180,25 +186,33 @@ module ActiveSupport
|
|
180
186
|
# ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
|
181
187
|
#
|
182
188
|
def build(value)
|
189
|
+
unless value.is_a?(::Numeric)
|
190
|
+
raise TypeError, "can't build an #{self.name} from a #{value.class.name}"
|
191
|
+
end
|
192
|
+
|
183
193
|
parts = {}
|
184
|
-
|
194
|
+
remainder_sign = value <=> 0
|
195
|
+
remainder = value.round(9).abs
|
196
|
+
variable = false
|
185
197
|
|
186
198
|
PARTS.each do |part|
|
187
199
|
unless part == :seconds
|
188
200
|
part_in_seconds = PARTS_IN_SECONDS[part]
|
189
|
-
parts[part] = remainder.div(part_in_seconds)
|
190
|
-
remainder
|
201
|
+
parts[part] = remainder.div(part_in_seconds) * remainder_sign
|
202
|
+
remainder %= part_in_seconds
|
203
|
+
|
204
|
+
unless parts[part].zero?
|
205
|
+
variable ||= VARIABLE_PARTS.include?(part)
|
206
|
+
end
|
191
207
|
end
|
192
|
-
end
|
208
|
+
end unless value == 0
|
193
209
|
|
194
|
-
parts[:seconds] = remainder
|
195
|
-
parts.reject! { |k, v| v.zero? }
|
210
|
+
parts[:seconds] = remainder * remainder_sign
|
196
211
|
|
197
|
-
new(value, parts)
|
212
|
+
new(value, parts, variable)
|
198
213
|
end
|
199
214
|
|
200
215
|
private
|
201
|
-
|
202
216
|
def calculate_total_seconds(parts)
|
203
217
|
parts.inject(0) do |total, (part, value)|
|
204
218
|
total + value * PARTS_IN_SECONDS[part]
|
@@ -206,14 +220,28 @@ module ActiveSupport
|
|
206
220
|
end
|
207
221
|
end
|
208
222
|
|
209
|
-
def initialize(value, parts)
|
210
|
-
@value, @parts = value, parts
|
211
|
-
@parts.
|
223
|
+
def initialize(value, parts, variable = nil) # :nodoc:
|
224
|
+
@value, @parts = value, parts
|
225
|
+
@parts.reject! { |k, v| v.zero? } unless value == 0
|
226
|
+
@parts.freeze
|
227
|
+
@variable = variable
|
228
|
+
|
229
|
+
if @variable.nil?
|
230
|
+
@variable = @parts.any? { |part, _| VARIABLE_PARTS.include?(part) }
|
231
|
+
end
|
212
232
|
end
|
213
233
|
|
214
|
-
|
215
|
-
|
234
|
+
# Returns a copy of the parts hash that defines the duration
|
235
|
+
def parts
|
236
|
+
@parts.dup
|
237
|
+
end
|
238
|
+
|
239
|
+
def coerce(other) # :nodoc:
|
240
|
+
case other
|
241
|
+
when Scalar
|
216
242
|
[other, self]
|
243
|
+
when Duration
|
244
|
+
[Scalar.new(other.value), self]
|
217
245
|
else
|
218
246
|
[Scalar.new(other), self]
|
219
247
|
end
|
@@ -233,14 +261,13 @@ module ActiveSupport
|
|
233
261
|
# are treated as seconds.
|
234
262
|
def +(other)
|
235
263
|
if Duration === other
|
236
|
-
parts = @parts.
|
237
|
-
|
238
|
-
parts[key] += value
|
264
|
+
parts = @parts.merge(other._parts) do |_key, value, other_value|
|
265
|
+
value + other_value
|
239
266
|
end
|
240
|
-
Duration.new(value + other.value, parts)
|
267
|
+
Duration.new(value + other.value, parts, @variable || other.variable?)
|
241
268
|
else
|
242
|
-
seconds = @parts
|
243
|
-
Duration.new(value + other, @parts.merge(seconds: seconds))
|
269
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
270
|
+
Duration.new(value + other, @parts.merge(seconds: seconds), @variable)
|
244
271
|
end
|
245
272
|
end
|
246
273
|
|
@@ -253,9 +280,9 @@ module ActiveSupport
|
|
253
280
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
254
281
|
def *(other)
|
255
282
|
if Scalar === other || Duration === other
|
256
|
-
Duration.new(value * other.value, parts.
|
283
|
+
Duration.new(value * other.value, @parts.transform_values { |number| number * other.value }, @variable || other.variable?)
|
257
284
|
elsif Numeric === other
|
258
|
-
Duration.new(value * other, parts.
|
285
|
+
Duration.new(value * other, @parts.transform_values { |number| number * other }, @variable)
|
259
286
|
else
|
260
287
|
raise_type_error(other)
|
261
288
|
end
|
@@ -264,11 +291,11 @@ module ActiveSupport
|
|
264
291
|
# Divides this Duration by a Numeric and returns a new Duration.
|
265
292
|
def /(other)
|
266
293
|
if Scalar === other
|
267
|
-
Duration.new(value / other.value, parts.
|
294
|
+
Duration.new(value / other.value, @parts.transform_values { |number| number / other.value }, @variable)
|
268
295
|
elsif Duration === other
|
269
296
|
value / other.value
|
270
297
|
elsif Numeric === other
|
271
|
-
Duration.new(value / other, parts.
|
298
|
+
Duration.new(value / other, @parts.transform_values { |number| number / other }, @variable)
|
272
299
|
else
|
273
300
|
raise_type_error(other)
|
274
301
|
end
|
@@ -286,11 +313,15 @@ module ActiveSupport
|
|
286
313
|
end
|
287
314
|
end
|
288
315
|
|
289
|
-
def -@
|
290
|
-
Duration.new(-value, parts.
|
316
|
+
def -@ # :nodoc:
|
317
|
+
Duration.new(-value, @parts.transform_values(&:-@), @variable)
|
318
|
+
end
|
319
|
+
|
320
|
+
def +@ # :nodoc:
|
321
|
+
self
|
291
322
|
end
|
292
323
|
|
293
|
-
def is_a?(klass)
|
324
|
+
def is_a?(klass) # :nodoc:
|
294
325
|
Duration == klass || value.is_a?(klass)
|
295
326
|
end
|
296
327
|
alias :kind_of? :is_a?
|
@@ -334,12 +365,55 @@ module ActiveSupport
|
|
334
365
|
# 1.year.to_i # => 31556952
|
335
366
|
#
|
336
367
|
# In such cases, Ruby's core
|
337
|
-
# Date[
|
338
|
-
# Time[
|
368
|
+
# Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
|
369
|
+
# Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
|
339
370
|
# date and time arithmetic.
|
340
371
|
def to_i
|
341
372
|
@value.to_i
|
342
373
|
end
|
374
|
+
alias :in_seconds :to_i
|
375
|
+
|
376
|
+
# Returns the amount of minutes a duration covers as a float
|
377
|
+
#
|
378
|
+
# 1.day.in_minutes # => 1440.0
|
379
|
+
def in_minutes
|
380
|
+
in_seconds / SECONDS_PER_MINUTE.to_f
|
381
|
+
end
|
382
|
+
|
383
|
+
# Returns the amount of hours a duration covers as a float
|
384
|
+
#
|
385
|
+
# 1.day.in_hours # => 24.0
|
386
|
+
def in_hours
|
387
|
+
in_seconds / SECONDS_PER_HOUR.to_f
|
388
|
+
end
|
389
|
+
|
390
|
+
# Returns the amount of days a duration covers as a float
|
391
|
+
#
|
392
|
+
# 12.hours.in_days # => 0.5
|
393
|
+
def in_days
|
394
|
+
in_seconds / SECONDS_PER_DAY.to_f
|
395
|
+
end
|
396
|
+
|
397
|
+
# Returns the amount of weeks a duration covers as a float
|
398
|
+
#
|
399
|
+
# 2.months.in_weeks # => 8.696
|
400
|
+
def in_weeks
|
401
|
+
in_seconds / SECONDS_PER_WEEK.to_f
|
402
|
+
end
|
403
|
+
|
404
|
+
# Returns the amount of months a duration covers as a float
|
405
|
+
#
|
406
|
+
# 9.weeks.in_months # => 2.07
|
407
|
+
def in_months
|
408
|
+
in_seconds / SECONDS_PER_MONTH.to_f
|
409
|
+
end
|
410
|
+
|
411
|
+
# Returns the amount of years a duration covers as a float
|
412
|
+
#
|
413
|
+
# 30.days.in_years # => 0.082
|
414
|
+
def in_years
|
415
|
+
in_seconds / SECONDS_PER_YEAR.to_f
|
416
|
+
end
|
343
417
|
|
344
418
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
345
419
|
# same parts as this one.
|
@@ -367,27 +441,24 @@ module ActiveSupport
|
|
367
441
|
alias :until :ago
|
368
442
|
alias :before :ago
|
369
443
|
|
370
|
-
def inspect
|
371
|
-
parts.
|
372
|
-
|
444
|
+
def inspect # :nodoc:
|
445
|
+
return "#{value} seconds" if @parts.empty?
|
446
|
+
|
447
|
+
@parts.
|
373
448
|
sort_by { |unit, _ | PARTS.index(unit) }.
|
374
449
|
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
375
|
-
to_sentence(locale:
|
450
|
+
to_sentence(locale: false)
|
376
451
|
end
|
377
452
|
|
378
|
-
def as_json(options = nil)
|
453
|
+
def as_json(options = nil) # :nodoc:
|
379
454
|
to_i
|
380
455
|
end
|
381
456
|
|
382
|
-
def
|
383
|
-
@value.respond_to?(method, include_private)
|
384
|
-
end
|
385
|
-
|
386
|
-
def init_with(coder) #:nodoc:
|
457
|
+
def init_with(coder) # :nodoc:
|
387
458
|
initialize(coder["value"], coder["parts"])
|
388
459
|
end
|
389
460
|
|
390
|
-
def encode_with(coder)
|
461
|
+
def encode_with(coder) # :nodoc:
|
391
462
|
coder.map = { "value" => @value, "parts" => @parts }
|
392
463
|
end
|
393
464
|
|
@@ -397,11 +468,24 @@ module ActiveSupport
|
|
397
468
|
ISO8601Serializer.new(self, precision: precision).serialize
|
398
469
|
end
|
399
470
|
|
400
|
-
|
471
|
+
def variable? # :nodoc:
|
472
|
+
@variable
|
473
|
+
end
|
401
474
|
|
475
|
+
def _parts # :nodoc:
|
476
|
+
@parts
|
477
|
+
end
|
478
|
+
|
479
|
+
private
|
402
480
|
def sum(sign, time = ::Time.current)
|
403
|
-
|
404
|
-
|
481
|
+
unless time.acts_like?(:time) || time.acts_like?(:date)
|
482
|
+
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
483
|
+
end
|
484
|
+
|
485
|
+
if @parts.empty?
|
486
|
+
time.since(sign * value)
|
487
|
+
else
|
488
|
+
@parts.inject(time) do |t, (type, number)|
|
405
489
|
if type == :seconds
|
406
490
|
t.since(sign * number)
|
407
491
|
elsif type == :minutes
|
@@ -411,14 +495,16 @@ module ActiveSupport
|
|
411
495
|
else
|
412
496
|
t.advance(type => sign * number)
|
413
497
|
end
|
414
|
-
else
|
415
|
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
416
498
|
end
|
417
499
|
end
|
418
500
|
end
|
419
501
|
|
502
|
+
def respond_to_missing?(method, _)
|
503
|
+
value.respond_to?(method)
|
504
|
+
end
|
505
|
+
|
420
506
|
def method_missing(method, *args, &block)
|
421
|
-
value.
|
507
|
+
value.public_send(method, *args, &block)
|
422
508
|
end
|
423
509
|
|
424
510
|
def raise_type_error(other)
|
@@ -0,0 +1,56 @@
|
|
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 deep_transform(hash)
|
38
|
+
return hash unless hash.is_a?(Hash)
|
39
|
+
|
40
|
+
h = ActiveSupport::InheritableOptions.new
|
41
|
+
hash.each do |k, v|
|
42
|
+
h[k] = deep_transform(v)
|
43
|
+
end
|
44
|
+
h
|
45
|
+
end
|
46
|
+
|
47
|
+
def options
|
48
|
+
@options ||= ActiveSupport::InheritableOptions.new(deep_transform(config))
|
49
|
+
end
|
50
|
+
|
51
|
+
def deserialize(config)
|
52
|
+
doc = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(config) : YAML.load(config)
|
53
|
+
doc.presence || {}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,129 @@
|
|
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
|
+
# Returns the encryption key, first trying the environment variable
|
49
|
+
# specified by +env_key+, then trying the key file specified by +key_path+.
|
50
|
+
# If +raise_if_missing_key+ is true, raises MissingKeyError if the
|
51
|
+
# environment variable is not set and the key file does not exist.
|
52
|
+
def key
|
53
|
+
read_env_key || read_key_file || handle_missing_key
|
54
|
+
end
|
55
|
+
|
56
|
+
# Reads the file and returns the decrypted content.
|
57
|
+
#
|
58
|
+
# Raises:
|
59
|
+
# - MissingKeyError if the key is missing and +raise_if_missing_key+ is true.
|
60
|
+
# - MissingContentError if the encrypted file does not exist or otherwise
|
61
|
+
# if the key is missing.
|
62
|
+
# - ActiveSupport::MessageEncryptor::InvalidMessage if the content cannot be
|
63
|
+
# decrypted or verified.
|
64
|
+
def read
|
65
|
+
if !key.nil? && content_path.exist?
|
66
|
+
decrypt content_path.binread
|
67
|
+
else
|
68
|
+
raise MissingContentError, content_path
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def write(contents)
|
73
|
+
IO.binwrite "#{content_path}.tmp", encrypt(contents)
|
74
|
+
FileUtils.mv "#{content_path}.tmp", content_path
|
75
|
+
end
|
76
|
+
|
77
|
+
def change(&block)
|
78
|
+
writing read, &block
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
private
|
83
|
+
def writing(contents)
|
84
|
+
tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
|
85
|
+
tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
|
86
|
+
tmp_path.binwrite contents
|
87
|
+
|
88
|
+
yield tmp_path
|
89
|
+
|
90
|
+
updated_contents = tmp_path.binread
|
91
|
+
|
92
|
+
write(updated_contents) if updated_contents != contents
|
93
|
+
ensure
|
94
|
+
FileUtils.rm(tmp_path) if tmp_path&.exist?
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def encrypt(contents)
|
99
|
+
check_key_length
|
100
|
+
encryptor.encrypt_and_sign contents
|
101
|
+
end
|
102
|
+
|
103
|
+
def decrypt(contents)
|
104
|
+
encryptor.decrypt_and_verify contents
|
105
|
+
end
|
106
|
+
|
107
|
+
def encryptor
|
108
|
+
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def read_env_key
|
113
|
+
ENV[env_key].presence
|
114
|
+
end
|
115
|
+
|
116
|
+
def read_key_file
|
117
|
+
return @key_file_contents if defined?(@key_file_contents)
|
118
|
+
@key_file_contents = (key_path.binread.strip if key_path.exist?)
|
119
|
+
end
|
120
|
+
|
121
|
+
def handle_missing_key
|
122
|
+
raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
|
123
|
+
end
|
124
|
+
|
125
|
+
def check_key_length
|
126
|
+
raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
|
127
|
+
end
|
128
|
+
end
|
129
|
+
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
|