activesupport 6.1.0 → 7.1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1075 -325
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -7
- data/lib/active_support/actionable_error.rb +4 -2
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +32 -7
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +53 -20
- data/lib/active_support/cache/mem_cache_store.rb +201 -62
- data/lib/active_support/cache/memory_store.rb +86 -24
- data/lib/active_support/cache/null_store.rb +16 -2
- data/lib/active_support/cache/redis_cache_store.rb +186 -193
- data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
- data/lib/active_support/cache/strategy/local_cache.rb +63 -71
- data/lib/active_support/cache.rb +487 -249
- data/lib/active_support/callbacks.rb +227 -105
- data/lib/active_support/code_generator.rb +70 -0
- data/lib/active_support/concern.rb +9 -7
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +18 -5
- data/lib/active_support/configuration_file.rb +7 -2
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +15 -13
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/subclasses.rb +37 -26
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +24 -9
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
- data/lib/active_support/core_ext/digest/uuid.rb +30 -13
- data/lib/active_support/core_ext/enumerable.rb +85 -83
- data/lib/active_support/core_ext/erb/util.rb +196 -0
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +1 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +81 -43
- data/lib/active_support/core_ext/module/deprecation.rb +15 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +2 -8
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
- data/lib/active_support/core_ext/object/duplicable.rb +31 -11
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
- data/lib/active_support/core_ext/object/json.rb +49 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -4
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with.rb +44 -0
- data/lib/active_support/core_ext/object/with_options.rb +25 -6
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +16 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +0 -25
- data/lib/active_support/core_ext/range/conversions.rb +34 -13
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +25 -13
- data/lib/active_support/core_ext/string/conversions.rb +2 -2
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +17 -10
- data/lib/active_support/core_ext/string/inquiry.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +85 -165
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +30 -8
- data/lib/active_support/core_ext/time/conversions.rb +15 -13
- data/lib/active_support/core_ext/time/zones.rb +12 -28
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes.rb +47 -20
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/dependencies/autoload.rb +17 -12
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -788
- data/lib/active_support/deprecation/behaviors.rb +66 -40
- data/lib/active_support/deprecation/constant_accessor.rb +5 -4
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +6 -8
- data/lib/active_support/deprecation/instance_delegator.rb +31 -4
- data/lib/active_support/deprecation/method_wrappers.rb +9 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
- data/lib/active_support/deprecation/reporting.rb +43 -26
- data/lib/active_support/deprecation.rb +32 -5
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +150 -72
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +9 -3
- data/lib/active_support/duration.rb +83 -52
- data/lib/active_support/encrypted_configuration.rb +72 -9
- data/lib/active_support/encrypted_file.rb +29 -13
- data/lib/active_support/environment_inquirer.rb +23 -3
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +203 -0
- data/lib/active_support/evented_file_update_checker.rb +20 -7
- 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 +44 -22
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +4 -2
- data/lib/active_support/fork_tracker.rb +28 -11
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +44 -19
- data/lib/active_support/html_safe_translation.rb +53 -0
- data/lib/active_support/i18n.rb +2 -1
- data/lib/active_support/i18n_railtie.rb +21 -14
- data/lib/active_support/inflector/inflections.rb +25 -7
- data/lib/active_support/inflector/methods.rb +50 -64
- data/lib/active_support/inflector/transliterate.rb +4 -2
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +27 -45
- data/lib/active_support/key_generator.rb +31 -6
- data/lib/active_support/lazy_load_hooks.rb +33 -7
- data/lib/active_support/locale/en.yml +4 -2
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +97 -35
- data/lib/active_support/logger.rb +9 -60
- data/lib/active_support/logger_thread_safe_level.rb +11 -34
- data/lib/active_support/message_encryptor.rb +206 -56
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +292 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +235 -84
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +112 -46
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +12 -11
- data/lib/active_support/multibyte/unicode.rb +9 -49
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +304 -114
- data/lib/active_support/notifications/instrumenter.rb +117 -35
- data/lib/active_support/notifications.rb +25 -25
- data/lib/active_support/number_helper/number_converter.rb +14 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
- data/lib/active_support/number_helper/rounding_helper.rb +2 -6
- data/lib/active_support/number_helper.rb +379 -319
- data/lib/active_support/option_merger.rb +10 -18
- data/lib/active_support/ordered_hash.rb +4 -4
- data/lib/active_support/ordered_options.rb +15 -1
- data/lib/active_support/parameter_filter.rb +105 -81
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/railtie.rb +83 -21
- data/lib/active_support/reloader.rb +13 -5
- data/lib/active_support/rescuable.rb +18 -16
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +18 -11
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/string_inquirer.rb +3 -3
- data/lib/active_support/subscriber.rb +11 -40
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +65 -25
- data/lib/active_support/test_case.rb +166 -27
- data/lib/active_support/testing/assertions.rb +61 -15
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +32 -0
- data/lib/active_support/testing/deprecation.rb +53 -2
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +30 -29
- data/lib/active_support/testing/method_call_assertions.rb +24 -11
- data/lib/active_support/testing/parallelization/server.rb +4 -0
- data/lib/active_support/testing/parallelization/worker.rb +3 -0
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +4 -6
- data/lib/active_support/testing/strict_warnings.rb +39 -0
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +49 -16
- data/lib/active_support/time_with_zone.rb +39 -28
- data/lib/active_support/values/time_zone.rb +50 -18
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +4 -11
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +5 -5
- data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- data/lib/active_support/xml_mini.rb +7 -6
- data/lib/active_support.rb +28 -1
- metadata +150 -18
- data/lib/active_support/core_ext/marshal.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/uri.rb +0 -29
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
- data/lib/active_support/per_thread_registry.rb +0 -60
@@ -3,15 +3,16 @@
|
|
3
3
|
require "active_support/core_ext/array/conversions"
|
4
4
|
require "active_support/core_ext/module/delegation"
|
5
5
|
require "active_support/core_ext/object/acts_like"
|
6
|
-
require "active_support/core_ext/string/filters"
|
7
6
|
|
8
7
|
module ActiveSupport
|
8
|
+
# = Active Support \Duration
|
9
|
+
#
|
9
10
|
# Provides accurate date and time measurements using Date#advance and
|
10
11
|
# Time#advance, respectively. It mainly supports the methods on Numeric.
|
11
12
|
#
|
12
13
|
# 1.month.ago # equivalent to Time.now.advance(months: -1)
|
13
14
|
class Duration
|
14
|
-
class Scalar < Numeric
|
15
|
+
class Scalar < Numeric # :nodoc:
|
15
16
|
attr_reader :value
|
16
17
|
delegate :to_i, :to_f, :to_s, to: :value
|
17
18
|
|
@@ -39,11 +40,11 @@ module ActiveSupport
|
|
39
40
|
|
40
41
|
def +(other)
|
41
42
|
if Duration === other
|
42
|
-
seconds = value + other.
|
43
|
-
new_parts = other.
|
43
|
+
seconds = value + other._parts.fetch(:seconds, 0)
|
44
|
+
new_parts = other._parts.merge(seconds: seconds)
|
44
45
|
new_value = value + other.value
|
45
46
|
|
46
|
-
Duration.new(new_value, new_parts)
|
47
|
+
Duration.new(new_value, new_parts, other.variable?)
|
47
48
|
else
|
48
49
|
calculate(:+, other)
|
49
50
|
end
|
@@ -51,12 +52,12 @@ module ActiveSupport
|
|
51
52
|
|
52
53
|
def -(other)
|
53
54
|
if Duration === other
|
54
|
-
seconds = value - other.
|
55
|
-
new_parts = other.
|
55
|
+
seconds = value - other._parts.fetch(:seconds, 0)
|
56
|
+
new_parts = other._parts.transform_values(&:-@)
|
56
57
|
new_parts = new_parts.merge(seconds: seconds)
|
57
58
|
new_value = value - other.value
|
58
59
|
|
59
|
-
Duration.new(new_value, new_parts)
|
60
|
+
Duration.new(new_value, new_parts, other.variable?)
|
60
61
|
else
|
61
62
|
calculate(:-, other)
|
62
63
|
end
|
@@ -64,10 +65,10 @@ module ActiveSupport
|
|
64
65
|
|
65
66
|
def *(other)
|
66
67
|
if Duration === other
|
67
|
-
new_parts = other.
|
68
|
+
new_parts = other._parts.transform_values { |other_value| value * other_value }
|
68
69
|
new_value = value * other.value
|
69
70
|
|
70
|
-
Duration.new(new_value, new_parts)
|
71
|
+
Duration.new(new_value, new_parts, other.variable?)
|
71
72
|
else
|
72
73
|
calculate(:*, other)
|
73
74
|
end
|
@@ -89,6 +90,10 @@ module ActiveSupport
|
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
93
|
+
def variable? # :nodoc:
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
92
97
|
private
|
93
98
|
def calculate(op, other)
|
94
99
|
if Scalar === other
|
@@ -123,8 +128,9 @@ module ActiveSupport
|
|
123
128
|
}.freeze
|
124
129
|
|
125
130
|
PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
|
131
|
+
VARIABLE_PARTS = [:years, :months, :weeks, :days].freeze
|
126
132
|
|
127
|
-
|
133
|
+
attr_reader :value
|
128
134
|
|
129
135
|
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
|
130
136
|
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
|
@@ -140,38 +146,38 @@ module ActiveSupport
|
|
140
146
|
new(calculate_total_seconds(parts), parts)
|
141
147
|
end
|
142
148
|
|
143
|
-
def ===(other)
|
149
|
+
def ===(other) # :nodoc:
|
144
150
|
other.is_a?(Duration)
|
145
151
|
rescue ::NoMethodError
|
146
152
|
false
|
147
153
|
end
|
148
154
|
|
149
|
-
def seconds(value)
|
150
|
-
new(value, seconds: value)
|
155
|
+
def seconds(value) # :nodoc:
|
156
|
+
new(value, { seconds: value }, false)
|
151
157
|
end
|
152
158
|
|
153
|
-
def minutes(value)
|
154
|
-
new(value * SECONDS_PER_MINUTE, minutes: value)
|
159
|
+
def minutes(value) # :nodoc:
|
160
|
+
new(value * SECONDS_PER_MINUTE, { minutes: value }, false)
|
155
161
|
end
|
156
162
|
|
157
|
-
def hours(value)
|
158
|
-
new(value * SECONDS_PER_HOUR, hours: value)
|
163
|
+
def hours(value) # :nodoc:
|
164
|
+
new(value * SECONDS_PER_HOUR, { hours: value }, false)
|
159
165
|
end
|
160
166
|
|
161
|
-
def days(value)
|
162
|
-
new(value * SECONDS_PER_DAY, days: value)
|
167
|
+
def days(value) # :nodoc:
|
168
|
+
new(value * SECONDS_PER_DAY, { days: value }, true)
|
163
169
|
end
|
164
170
|
|
165
|
-
def weeks(value)
|
166
|
-
new(value * SECONDS_PER_WEEK, weeks: value)
|
171
|
+
def weeks(value) # :nodoc:
|
172
|
+
new(value * SECONDS_PER_WEEK, { weeks: value }, true)
|
167
173
|
end
|
168
174
|
|
169
|
-
def months(value)
|
170
|
-
new(value * SECONDS_PER_MONTH, months: value)
|
175
|
+
def months(value) # :nodoc:
|
176
|
+
new(value * SECONDS_PER_MONTH, { months: value }, true)
|
171
177
|
end
|
172
178
|
|
173
|
-
def years(value)
|
174
|
-
new(value * SECONDS_PER_YEAR, years: value)
|
179
|
+
def years(value) # :nodoc:
|
180
|
+
new(value * SECONDS_PER_YEAR, { years: value }, true)
|
175
181
|
end
|
176
182
|
|
177
183
|
# Creates a new Duration from a seconds value that is converted
|
@@ -186,19 +192,25 @@ module ActiveSupport
|
|
186
192
|
end
|
187
193
|
|
188
194
|
parts = {}
|
189
|
-
|
195
|
+
remainder_sign = value <=> 0
|
196
|
+
remainder = value.round(9).abs
|
197
|
+
variable = false
|
190
198
|
|
191
199
|
PARTS.each do |part|
|
192
200
|
unless part == :seconds
|
193
201
|
part_in_seconds = PARTS_IN_SECONDS[part]
|
194
|
-
parts[part] = remainder.div(part_in_seconds)
|
202
|
+
parts[part] = remainder.div(part_in_seconds) * remainder_sign
|
195
203
|
remainder %= part_in_seconds
|
204
|
+
|
205
|
+
unless parts[part].zero?
|
206
|
+
variable ||= VARIABLE_PARTS.include?(part)
|
207
|
+
end
|
196
208
|
end
|
197
209
|
end unless value == 0
|
198
210
|
|
199
|
-
parts[:seconds] = remainder
|
211
|
+
parts[:seconds] = remainder * remainder_sign
|
200
212
|
|
201
|
-
new(value, parts)
|
213
|
+
new(value, parts, variable)
|
202
214
|
end
|
203
215
|
|
204
216
|
private
|
@@ -209,12 +221,23 @@ module ActiveSupport
|
|
209
221
|
end
|
210
222
|
end
|
211
223
|
|
212
|
-
def initialize(value, parts)
|
224
|
+
def initialize(value, parts, variable = nil) # :nodoc:
|
213
225
|
@value, @parts = value, parts
|
214
226
|
@parts.reject! { |k, v| v.zero? } unless value == 0
|
227
|
+
@parts.freeze
|
228
|
+
@variable = variable
|
229
|
+
|
230
|
+
if @variable.nil?
|
231
|
+
@variable = @parts.any? { |part, _| VARIABLE_PARTS.include?(part) }
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Returns a copy of the parts hash that defines the duration
|
236
|
+
def parts
|
237
|
+
@parts.dup
|
215
238
|
end
|
216
239
|
|
217
|
-
def coerce(other)
|
240
|
+
def coerce(other) # :nodoc:
|
218
241
|
case other
|
219
242
|
when Scalar
|
220
243
|
[other, self]
|
@@ -239,13 +262,13 @@ module ActiveSupport
|
|
239
262
|
# are treated as seconds.
|
240
263
|
def +(other)
|
241
264
|
if Duration === other
|
242
|
-
parts = @parts.merge(other.
|
265
|
+
parts = @parts.merge(other._parts) do |_key, value, other_value|
|
243
266
|
value + other_value
|
244
267
|
end
|
245
|
-
Duration.new(value + other.value, parts)
|
268
|
+
Duration.new(value + other.value, parts, @variable || other.variable?)
|
246
269
|
else
|
247
270
|
seconds = @parts.fetch(:seconds, 0) + other
|
248
|
-
Duration.new(value + other, @parts.merge(seconds: seconds))
|
271
|
+
Duration.new(value + other, @parts.merge(seconds: seconds), @variable)
|
249
272
|
end
|
250
273
|
end
|
251
274
|
|
@@ -258,9 +281,9 @@ module ActiveSupport
|
|
258
281
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
259
282
|
def *(other)
|
260
283
|
if Scalar === other || Duration === other
|
261
|
-
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
284
|
+
Duration.new(value * other.value, @parts.transform_values { |number| number * other.value }, @variable || other.variable?)
|
262
285
|
elsif Numeric === other
|
263
|
-
Duration.new(value * other, parts.transform_values { |number| number * other })
|
286
|
+
Duration.new(value * other, @parts.transform_values { |number| number * other }, @variable)
|
264
287
|
else
|
265
288
|
raise_type_error(other)
|
266
289
|
end
|
@@ -269,11 +292,11 @@ module ActiveSupport
|
|
269
292
|
# Divides this Duration by a Numeric and returns a new Duration.
|
270
293
|
def /(other)
|
271
294
|
if Scalar === other
|
272
|
-
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
295
|
+
Duration.new(value / other.value, @parts.transform_values { |number| number / other.value }, @variable)
|
273
296
|
elsif Duration === other
|
274
297
|
value / other.value
|
275
298
|
elsif Numeric === other
|
276
|
-
Duration.new(value / other, parts.transform_values { |number| number / other })
|
299
|
+
Duration.new(value / other, @parts.transform_values { |number| number / other }, @variable)
|
277
300
|
else
|
278
301
|
raise_type_error(other)
|
279
302
|
end
|
@@ -291,15 +314,15 @@ module ActiveSupport
|
|
291
314
|
end
|
292
315
|
end
|
293
316
|
|
294
|
-
def -@
|
295
|
-
Duration.new(-value, parts.transform_values(&:-@))
|
317
|
+
def -@ # :nodoc:
|
318
|
+
Duration.new(-value, @parts.transform_values(&:-@), @variable)
|
296
319
|
end
|
297
320
|
|
298
|
-
def +@
|
321
|
+
def +@ # :nodoc:
|
299
322
|
self
|
300
323
|
end
|
301
324
|
|
302
|
-
def is_a?(klass)
|
325
|
+
def is_a?(klass) # :nodoc:
|
303
326
|
Duration == klass || value.is_a?(klass)
|
304
327
|
end
|
305
328
|
alias :kind_of? :is_a?
|
@@ -419,24 +442,24 @@ module ActiveSupport
|
|
419
442
|
alias :until :ago
|
420
443
|
alias :before :ago
|
421
444
|
|
422
|
-
def inspect
|
423
|
-
return "#{value} seconds" if parts.empty?
|
445
|
+
def inspect # :nodoc:
|
446
|
+
return "#{value} seconds" if @parts.empty?
|
424
447
|
|
425
|
-
parts.
|
448
|
+
@parts.
|
426
449
|
sort_by { |unit, _ | PARTS.index(unit) }.
|
427
450
|
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
428
|
-
to_sentence(locale:
|
451
|
+
to_sentence(locale: false)
|
429
452
|
end
|
430
453
|
|
431
|
-
def as_json(options = nil)
|
454
|
+
def as_json(options = nil) # :nodoc:
|
432
455
|
to_i
|
433
456
|
end
|
434
457
|
|
435
|
-
def init_with(coder)
|
458
|
+
def init_with(coder) # :nodoc:
|
436
459
|
initialize(coder["value"], coder["parts"])
|
437
460
|
end
|
438
461
|
|
439
|
-
def encode_with(coder)
|
462
|
+
def encode_with(coder) # :nodoc:
|
440
463
|
coder.map = { "value" => @value, "parts" => @parts }
|
441
464
|
end
|
442
465
|
|
@@ -446,16 +469,24 @@ module ActiveSupport
|
|
446
469
|
ISO8601Serializer.new(self, precision: precision).serialize
|
447
470
|
end
|
448
471
|
|
472
|
+
def variable? # :nodoc:
|
473
|
+
@variable
|
474
|
+
end
|
475
|
+
|
476
|
+
def _parts # :nodoc:
|
477
|
+
@parts
|
478
|
+
end
|
479
|
+
|
449
480
|
private
|
450
481
|
def sum(sign, time = ::Time.current)
|
451
482
|
unless time.acts_like?(:time) || time.acts_like?(:date)
|
452
483
|
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
453
484
|
end
|
454
485
|
|
455
|
-
if parts.empty?
|
486
|
+
if @parts.empty?
|
456
487
|
time.since(sign * value)
|
457
488
|
else
|
458
|
-
parts.inject(time) do |t, (type, number)|
|
489
|
+
@parts.inject(time) do |t, (type, number)|
|
459
490
|
if type == :seconds
|
460
491
|
t.since(sign * number)
|
461
492
|
elsif type == :minutes
|
@@ -4,42 +4,105 @@ require "yaml"
|
|
4
4
|
require "active_support/encrypted_file"
|
5
5
|
require "active_support/ordered_options"
|
6
6
|
require "active_support/core_ext/object/inclusion"
|
7
|
+
require "active_support/core_ext/hash/keys"
|
7
8
|
require "active_support/core_ext/module/delegation"
|
8
9
|
|
9
10
|
module ActiveSupport
|
11
|
+
# = Encrypted Configuration
|
12
|
+
#
|
13
|
+
# Provides convenience methods on top of EncryptedFile to access values stored
|
14
|
+
# as encrypted YAML.
|
15
|
+
#
|
16
|
+
# Values can be accessed via +Hash+ methods, such as +fetch+ and +dig+, or via
|
17
|
+
# dynamic accessor methods, similar to OrderedOptions.
|
18
|
+
#
|
19
|
+
# my_config = ActiveSupport::EncryptedConfiguration.new(...)
|
20
|
+
# my_config.read # => "some_secret: 123\nsome_namespace:\n another_secret: 456"
|
21
|
+
#
|
22
|
+
# my_config[:some_secret]
|
23
|
+
# # => 123
|
24
|
+
# my_config.some_secret
|
25
|
+
# # => 123
|
26
|
+
# my_config.dig(:some_namespace, :another_secret)
|
27
|
+
# # => 456
|
28
|
+
# my_config.some_namespace.another_secret
|
29
|
+
# # => 456
|
30
|
+
# my_config.fetch(:foo)
|
31
|
+
# # => KeyError
|
32
|
+
# my_config.foo!
|
33
|
+
# # => KeyError
|
34
|
+
#
|
10
35
|
class EncryptedConfiguration < EncryptedFile
|
11
|
-
|
36
|
+
class InvalidContentError < RuntimeError
|
37
|
+
def initialize(content_path)
|
38
|
+
super "Invalid YAML in '#{content_path}'."
|
39
|
+
end
|
40
|
+
|
41
|
+
def message
|
42
|
+
cause.is_a?(Psych::SyntaxError) ? "#{super}\n\n #{cause.message}" : super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
12
46
|
delegate_missing_to :options
|
13
47
|
|
14
48
|
def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
|
15
49
|
super content_path: config_path, key_path: key_path,
|
16
50
|
env_key: env_key, raise_if_missing_key: raise_if_missing_key
|
51
|
+
@config = nil
|
52
|
+
@options = nil
|
17
53
|
end
|
18
54
|
|
19
|
-
#
|
55
|
+
# Reads the file and returns the decrypted content. See EncryptedFile#read.
|
20
56
|
def read
|
21
57
|
super
|
22
58
|
rescue ActiveSupport::EncryptedFile::MissingContentError
|
59
|
+
# Allow a config to be started without a file present
|
23
60
|
""
|
24
61
|
end
|
25
62
|
|
26
|
-
def
|
27
|
-
deserialize(
|
28
|
-
|
29
|
-
super
|
63
|
+
def validate! # :nodoc:
|
64
|
+
deserialize(read)
|
30
65
|
end
|
31
66
|
|
67
|
+
# Returns the decrypted content as a Hash with symbolized keys.
|
68
|
+
#
|
69
|
+
# my_config = ActiveSupport::EncryptedConfiguration.new(...)
|
70
|
+
# my_config.read # => "some_secret: 123\nsome_namespace:\n another_secret: 456"
|
71
|
+
#
|
72
|
+
# my_config.config
|
73
|
+
# # => { some_secret: 123, some_namespace: { another_secret: 789 } }
|
74
|
+
#
|
32
75
|
def config
|
33
76
|
@config ||= deserialize(read).deep_symbolize_keys
|
34
77
|
end
|
35
78
|
|
79
|
+
def inspect # :nodoc:
|
80
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
81
|
+
end
|
82
|
+
|
36
83
|
private
|
84
|
+
def deep_transform(hash)
|
85
|
+
return hash unless hash.is_a?(Hash)
|
86
|
+
|
87
|
+
h = ActiveSupport::OrderedOptions.new
|
88
|
+
hash.each do |k, v|
|
89
|
+
h[k] = deep_transform(v)
|
90
|
+
end
|
91
|
+
h
|
92
|
+
end
|
93
|
+
|
37
94
|
def options
|
38
|
-
@options ||=
|
95
|
+
@options ||= deep_transform(config)
|
39
96
|
end
|
40
97
|
|
41
|
-
def deserialize(
|
42
|
-
YAML.
|
98
|
+
def deserialize(content)
|
99
|
+
config = YAML.respond_to?(:unsafe_load) ?
|
100
|
+
YAML.unsafe_load(content, filename: content_path) :
|
101
|
+
YAML.load(content, filename: content_path)
|
102
|
+
|
103
|
+
config.presence || {}
|
104
|
+
rescue Psych::SyntaxError
|
105
|
+
raise InvalidContentError.new(content_path)
|
43
106
|
end
|
44
107
|
end
|
45
108
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "pathname"
|
4
|
-
require "
|
4
|
+
require "tempfile"
|
5
5
|
require "active_support/message_encryptor"
|
6
6
|
|
7
7
|
module ActiveSupport
|
@@ -45,10 +45,28 @@ module ActiveSupport
|
|
45
45
|
@env_key, @raise_if_missing_key = env_key, raise_if_missing_key
|
46
46
|
end
|
47
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.
|
48
52
|
def key
|
49
53
|
read_env_key || read_key_file || handle_missing_key
|
50
54
|
end
|
51
55
|
|
56
|
+
# Returns truthy if #key is truthy. Returns falsy otherwise. Unlike #key,
|
57
|
+
# does not raise MissingKeyError when +raise_if_missing_key+ is true.
|
58
|
+
def key?
|
59
|
+
read_env_key || read_key_file
|
60
|
+
end
|
61
|
+
|
62
|
+
# Reads the file and returns the decrypted content.
|
63
|
+
#
|
64
|
+
# Raises:
|
65
|
+
# - MissingKeyError if the key is missing and +raise_if_missing_key+ is true.
|
66
|
+
# - MissingContentError if the encrypted file does not exist or otherwise
|
67
|
+
# if the key is missing.
|
68
|
+
# - ActiveSupport::MessageEncryptor::InvalidMessage if the content cannot be
|
69
|
+
# decrypted or verified.
|
52
70
|
def read
|
53
71
|
if !key.nil? && content_path.exist?
|
54
72
|
decrypt content_path.binread
|
@@ -69,17 +87,16 @@ module ActiveSupport
|
|
69
87
|
|
70
88
|
private
|
71
89
|
def writing(contents)
|
72
|
-
|
73
|
-
|
74
|
-
|
90
|
+
Tempfile.create(["", "-" + content_path.basename.to_s.chomp(".enc")]) do |tmp_file|
|
91
|
+
tmp_path = Pathname.new(tmp_file)
|
92
|
+
tmp_path.binwrite contents
|
75
93
|
|
76
|
-
|
94
|
+
yield tmp_path
|
77
95
|
|
78
|
-
|
96
|
+
updated_contents = tmp_path.binread
|
79
97
|
|
80
|
-
|
81
|
-
|
82
|
-
FileUtils.rm(tmp_path) if tmp_path&.exist?
|
98
|
+
write(updated_contents) if updated_contents != contents
|
99
|
+
end
|
83
100
|
end
|
84
101
|
|
85
102
|
|
@@ -93,17 +110,16 @@ module ActiveSupport
|
|
93
110
|
end
|
94
111
|
|
95
112
|
def encryptor
|
96
|
-
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
|
113
|
+
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER, serializer: Marshal)
|
97
114
|
end
|
98
115
|
|
99
116
|
|
100
117
|
def read_env_key
|
101
|
-
ENV[env_key]
|
118
|
+
ENV[env_key].presence
|
102
119
|
end
|
103
120
|
|
104
121
|
def read_key_file
|
105
|
-
|
106
|
-
@key_file_contents = (key_path.binread.strip if key_path.exist?)
|
122
|
+
@key_file_contents ||= (key_path.binread.strip if key_path.exist?)
|
107
123
|
end
|
108
124
|
|
109
125
|
def handle_missing_key
|
@@ -1,20 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/string_inquirer"
|
4
|
+
require "active_support/core_ext/object/inclusion"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
|
-
class EnvironmentInquirer < StringInquirer
|
7
|
-
|
7
|
+
class EnvironmentInquirer < StringInquirer # :nodoc:
|
8
|
+
# Optimization for the three default environments, so this inquirer doesn't need to rely on
|
9
|
+
# the slower delegation through method_missing that StringInquirer would normally entail.
|
10
|
+
DEFAULT_ENVIRONMENTS = %w[ development test production ]
|
11
|
+
|
12
|
+
# Environments that'll respond true for #local?
|
13
|
+
LOCAL_ENVIRONMENTS = %w[ development test ]
|
14
|
+
|
8
15
|
def initialize(env)
|
16
|
+
raise(ArgumentError, "'local' is a reserved environment name") if env == "local"
|
17
|
+
|
9
18
|
super(env)
|
10
19
|
|
11
20
|
DEFAULT_ENVIRONMENTS.each do |default|
|
12
21
|
instance_variable_set :"@#{default}", env == default
|
13
22
|
end
|
23
|
+
|
24
|
+
@local = in? LOCAL_ENVIRONMENTS
|
14
25
|
end
|
15
26
|
|
16
27
|
DEFAULT_ENVIRONMENTS.each do |env|
|
17
|
-
class_eval
|
28
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
29
|
+
def #{env}?
|
30
|
+
@#{env}
|
31
|
+
end
|
32
|
+
RUBY
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns true if we're in the development or test environment.
|
36
|
+
def local?
|
37
|
+
@local
|
18
38
|
end
|
19
39
|
end
|
20
40
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport::ErrorReporter::TestHelper # :nodoc:
|
4
|
+
class ErrorSubscriber
|
5
|
+
attr_reader :events
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@events = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def report(error, handled:, severity:, source:, context:)
|
12
|
+
@events << [error, handled, severity, source, context]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|