activesupport 5.2.0 → 6.1.0
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 +362 -333
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +31 -29
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +84 -45
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/cache.rb +174 -113
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +76 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +171 -70
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +7 -2
- data/lib/active_support/core_ext/object/to_query.rb +5 -2
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +82 -0
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +69 -12
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +7 -5
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/dependencies.rb +118 -35
- data/lib/active_support/deprecation/behaviors.rb +20 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +21 -13
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +51 -8
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/encrypted_configuration.rb +1 -5
- data/lib/active_support/encrypted_file.rb +23 -5
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +78 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +16 -5
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +50 -57
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +52 -7
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +72 -24
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +92 -1
- data/lib/active_support/testing/assertions.rb +30 -9
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -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 +5 -9
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +34 -18
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +10 -3
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support.rb +14 -1
- metadata +57 -30
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -25
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -4,7 +4,6 @@ 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
6
|
require "active_support/core_ext/string/filters"
|
7
|
-
require "active_support/deprecation"
|
8
7
|
|
9
8
|
module ActiveSupport
|
10
9
|
# Provides accurate date and time measurements using Date#advance and
|
@@ -40,7 +39,7 @@ module ActiveSupport
|
|
40
39
|
|
41
40
|
def +(other)
|
42
41
|
if Duration === other
|
43
|
-
seconds = value + other.parts
|
42
|
+
seconds = value + other.parts.fetch(:seconds, 0)
|
44
43
|
new_parts = other.parts.merge(seconds: seconds)
|
45
44
|
new_value = value + other.value
|
46
45
|
|
@@ -52,8 +51,8 @@ module ActiveSupport
|
|
52
51
|
|
53
52
|
def -(other)
|
54
53
|
if Duration === other
|
55
|
-
seconds = value - other.parts
|
56
|
-
new_parts = other.parts.
|
54
|
+
seconds = value - other.parts.fetch(:seconds, 0)
|
55
|
+
new_parts = other.parts.transform_values(&:-@)
|
57
56
|
new_parts = new_parts.merge(seconds: seconds)
|
58
57
|
new_value = value - other.value
|
59
58
|
|
@@ -65,7 +64,7 @@ module ActiveSupport
|
|
65
64
|
|
66
65
|
def *(other)
|
67
66
|
if Duration === other
|
68
|
-
new_parts = other.parts.
|
67
|
+
new_parts = other.parts.transform_values { |other_value| value * other_value }
|
69
68
|
new_value = value * other.value
|
70
69
|
|
71
70
|
Duration.new(new_value, new_parts)
|
@@ -148,31 +147,31 @@ module ActiveSupport
|
|
148
147
|
end
|
149
148
|
|
150
149
|
def seconds(value) #:nodoc:
|
151
|
-
new(value,
|
150
|
+
new(value, seconds: value)
|
152
151
|
end
|
153
152
|
|
154
153
|
def minutes(value) #:nodoc:
|
155
|
-
new(value * SECONDS_PER_MINUTE,
|
154
|
+
new(value * SECONDS_PER_MINUTE, minutes: value)
|
156
155
|
end
|
157
156
|
|
158
157
|
def hours(value) #:nodoc:
|
159
|
-
new(value * SECONDS_PER_HOUR,
|
158
|
+
new(value * SECONDS_PER_HOUR, hours: value)
|
160
159
|
end
|
161
160
|
|
162
161
|
def days(value) #:nodoc:
|
163
|
-
new(value * SECONDS_PER_DAY,
|
162
|
+
new(value * SECONDS_PER_DAY, days: value)
|
164
163
|
end
|
165
164
|
|
166
165
|
def weeks(value) #:nodoc:
|
167
|
-
new(value * SECONDS_PER_WEEK,
|
166
|
+
new(value * SECONDS_PER_WEEK, weeks: value)
|
168
167
|
end
|
169
168
|
|
170
169
|
def months(value) #:nodoc:
|
171
|
-
new(value * SECONDS_PER_MONTH,
|
170
|
+
new(value * SECONDS_PER_MONTH, months: value)
|
172
171
|
end
|
173
172
|
|
174
173
|
def years(value) #:nodoc:
|
175
|
-
new(value * SECONDS_PER_YEAR,
|
174
|
+
new(value * SECONDS_PER_YEAR, years: value)
|
176
175
|
end
|
177
176
|
|
178
177
|
# Creates a new Duration from a seconds value that is converted
|
@@ -182,16 +181,20 @@ module ActiveSupport
|
|
182
181
|
# ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
|
183
182
|
#
|
184
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
|
+
|
185
188
|
parts = {}
|
186
|
-
remainder = value.
|
189
|
+
remainder = value.round(9)
|
187
190
|
|
188
191
|
PARTS.each do |part|
|
189
192
|
unless part == :seconds
|
190
193
|
part_in_seconds = PARTS_IN_SECONDS[part]
|
191
194
|
parts[part] = remainder.div(part_in_seconds)
|
192
|
-
remainder
|
195
|
+
remainder %= part_in_seconds
|
193
196
|
end
|
194
|
-
end
|
197
|
+
end unless value == 0
|
195
198
|
|
196
199
|
parts[:seconds] = remainder
|
197
200
|
|
@@ -199,7 +202,6 @@ module ActiveSupport
|
|
199
202
|
end
|
200
203
|
|
201
204
|
private
|
202
|
-
|
203
205
|
def calculate_total_seconds(parts)
|
204
206
|
parts.inject(0) do |total, (part, value)|
|
205
207
|
total + value * PARTS_IN_SECONDS[part]
|
@@ -208,14 +210,16 @@ module ActiveSupport
|
|
208
210
|
end
|
209
211
|
|
210
212
|
def initialize(value, parts) #:nodoc:
|
211
|
-
@value, @parts = value, parts
|
212
|
-
@parts.
|
213
|
-
@parts.reject! { |k, v| v.zero? }
|
213
|
+
@value, @parts = value, parts
|
214
|
+
@parts.reject! { |k, v| v.zero? } unless value == 0
|
214
215
|
end
|
215
216
|
|
216
217
|
def coerce(other) #:nodoc:
|
217
|
-
|
218
|
+
case other
|
219
|
+
when Scalar
|
218
220
|
[other, self]
|
221
|
+
when Duration
|
222
|
+
[Scalar.new(other.value), self]
|
219
223
|
else
|
220
224
|
[Scalar.new(other), self]
|
221
225
|
end
|
@@ -235,13 +239,12 @@ module ActiveSupport
|
|
235
239
|
# are treated as seconds.
|
236
240
|
def +(other)
|
237
241
|
if Duration === other
|
238
|
-
parts = @parts.
|
239
|
-
|
240
|
-
parts[key] += value
|
242
|
+
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
243
|
+
value + other_value
|
241
244
|
end
|
242
245
|
Duration.new(value + other.value, parts)
|
243
246
|
else
|
244
|
-
seconds = @parts
|
247
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
245
248
|
Duration.new(value + other, @parts.merge(seconds: seconds))
|
246
249
|
end
|
247
250
|
end
|
@@ -255,9 +258,9 @@ module ActiveSupport
|
|
255
258
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
256
259
|
def *(other)
|
257
260
|
if Scalar === other || Duration === other
|
258
|
-
Duration.new(value * other.value, parts.
|
261
|
+
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
259
262
|
elsif Numeric === other
|
260
|
-
Duration.new(value * other, parts.
|
263
|
+
Duration.new(value * other, parts.transform_values { |number| number * other })
|
261
264
|
else
|
262
265
|
raise_type_error(other)
|
263
266
|
end
|
@@ -266,11 +269,11 @@ module ActiveSupport
|
|
266
269
|
# Divides this Duration by a Numeric and returns a new Duration.
|
267
270
|
def /(other)
|
268
271
|
if Scalar === other
|
269
|
-
Duration.new(value / other.value, parts.
|
272
|
+
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
270
273
|
elsif Duration === other
|
271
274
|
value / other.value
|
272
275
|
elsif Numeric === other
|
273
|
-
Duration.new(value / other, parts.
|
276
|
+
Duration.new(value / other, parts.transform_values { |number| number / other })
|
274
277
|
else
|
275
278
|
raise_type_error(other)
|
276
279
|
end
|
@@ -289,7 +292,11 @@ module ActiveSupport
|
|
289
292
|
end
|
290
293
|
|
291
294
|
def -@ #:nodoc:
|
292
|
-
Duration.new(-value, parts.
|
295
|
+
Duration.new(-value, parts.transform_values(&:-@))
|
296
|
+
end
|
297
|
+
|
298
|
+
def +@ #:nodoc:
|
299
|
+
self
|
293
300
|
end
|
294
301
|
|
295
302
|
def is_a?(klass) #:nodoc:
|
@@ -336,12 +343,55 @@ module ActiveSupport
|
|
336
343
|
# 1.year.to_i # => 31556952
|
337
344
|
#
|
338
345
|
# In such cases, Ruby's core
|
339
|
-
# Date[
|
340
|
-
# 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
|
341
348
|
# date and time arithmetic.
|
342
349
|
def to_i
|
343
350
|
@value.to_i
|
344
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
|
345
395
|
|
346
396
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
347
397
|
# same parts as this one.
|
@@ -370,10 +420,9 @@ module ActiveSupport
|
|
370
420
|
alias :before :ago
|
371
421
|
|
372
422
|
def inspect #:nodoc:
|
373
|
-
return "
|
423
|
+
return "#{value} seconds" if parts.empty?
|
374
424
|
|
375
425
|
parts.
|
376
|
-
reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
|
377
426
|
sort_by { |unit, _ | PARTS.index(unit) }.
|
378
427
|
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
379
428
|
to_sentence(locale: ::I18n.default_locale)
|
@@ -398,10 +447,15 @@ module ActiveSupport
|
|
398
447
|
end
|
399
448
|
|
400
449
|
private
|
401
|
-
|
402
450
|
def sum(sign, time = ::Time.current)
|
403
|
-
|
404
|
-
|
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)|
|
405
459
|
if type == :seconds
|
406
460
|
t.since(sign * number)
|
407
461
|
elsif type == :minutes
|
@@ -411,8 +465,6 @@ module ActiveSupport
|
|
411
465
|
else
|
412
466
|
t.advance(type => sign * number)
|
413
467
|
end
|
414
|
-
else
|
415
|
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
416
468
|
end
|
417
469
|
end
|
418
470
|
end
|
@@ -38,12 +38,8 @@ module ActiveSupport
|
|
38
38
|
@options ||= ActiveSupport::InheritableOptions.new(config)
|
39
39
|
end
|
40
40
|
|
41
|
-
def serialize(config)
|
42
|
-
config.present? ? YAML.dump(config) : ""
|
43
|
-
end
|
44
|
-
|
45
41
|
def deserialize(config)
|
46
|
-
|
42
|
+
YAML.load(config).presence || {}
|
47
43
|
end
|
48
44
|
end
|
49
45
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "pathname"
|
4
|
+
require "tmpdir"
|
4
5
|
require "active_support/message_encryptor"
|
5
6
|
|
6
7
|
module ActiveSupport
|
@@ -19,17 +20,28 @@ module ActiveSupport
|
|
19
20
|
end
|
20
21
|
end
|
21
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
|
+
|
22
29
|
CIPHER = "aes-128-gcm"
|
23
30
|
|
24
31
|
def self.generate_key
|
25
32
|
SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
|
26
33
|
end
|
27
34
|
|
35
|
+
def self.expected_key_length # :nodoc:
|
36
|
+
@expected_key_length ||= generate_key.length
|
37
|
+
end
|
38
|
+
|
28
39
|
|
29
40
|
attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
|
30
41
|
|
31
42
|
def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
|
32
|
-
@content_path
|
43
|
+
@content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path }
|
44
|
+
@key_path = Pathname.new(key_path)
|
33
45
|
@env_key, @raise_if_missing_key = env_key, raise_if_missing_key
|
34
46
|
end
|
35
47
|
|
@@ -57,7 +69,7 @@ module ActiveSupport
|
|
57
69
|
|
58
70
|
private
|
59
71
|
def writing(contents)
|
60
|
-
tmp_file = "#{
|
72
|
+
tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
|
61
73
|
tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
|
62
74
|
tmp_path.binwrite contents
|
63
75
|
|
@@ -67,11 +79,12 @@ module ActiveSupport
|
|
67
79
|
|
68
80
|
write(updated_contents) if updated_contents != contents
|
69
81
|
ensure
|
70
|
-
FileUtils.rm(tmp_path) if tmp_path
|
82
|
+
FileUtils.rm(tmp_path) if tmp_path&.exist?
|
71
83
|
end
|
72
84
|
|
73
85
|
|
74
86
|
def encrypt(contents)
|
87
|
+
check_key_length
|
75
88
|
encryptor.encrypt_and_sign contents
|
76
89
|
end
|
77
90
|
|
@@ -89,11 +102,16 @@ module ActiveSupport
|
|
89
102
|
end
|
90
103
|
|
91
104
|
def read_key_file
|
92
|
-
|
105
|
+
return @key_file_contents if defined?(@key_file_contents)
|
106
|
+
@key_file_contents = (key_path.binread.strip if key_path.exist?)
|
93
107
|
end
|
94
108
|
|
95
109
|
def handle_missing_key
|
96
|
-
raise MissingKeyError
|
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
|
97
115
|
end
|
98
116
|
end
|
99
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
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "set"
|
4
4
|
require "pathname"
|
5
5
|
require "concurrent/atomic/atomic_boolean"
|
6
|
+
require "listen"
|
7
|
+
require "active_support/fork_tracker"
|
6
8
|
|
7
9
|
module ActiveSupport
|
8
10
|
# Allows you to "listen" to changes in a file system.
|
@@ -38,48 +40,22 @@ module ActiveSupport
|
|
38
40
|
raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
|
39
41
|
end
|
40
42
|
|
41
|
-
@
|
42
|
-
@
|
43
|
-
|
44
|
-
@dirs = {}
|
45
|
-
dirs.each do |dir, exts|
|
46
|
-
@dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
|
47
|
-
end
|
48
|
-
|
49
|
-
@block = block
|
50
|
-
@updated = Concurrent::AtomicBoolean.new(false)
|
51
|
-
@lcsp = @ph.longest_common_subpath(@dirs.keys)
|
52
|
-
@pid = Process.pid
|
53
|
-
@boot_mutex = Mutex.new
|
54
|
-
|
55
|
-
if (@dtw = directories_to_watch).any?
|
56
|
-
# Loading listen triggers warnings. These are originated by a legit
|
57
|
-
# usage of attr_* macros for private attributes, but adds a lot of noise
|
58
|
-
# to our test suite. Thus, we lazy load it and disable warnings locally.
|
59
|
-
silence_warnings do
|
60
|
-
begin
|
61
|
-
require "listen"
|
62
|
-
rescue LoadError => e
|
63
|
-
raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
boot!
|
43
|
+
@block = block
|
44
|
+
@core = Core.new(files, dirs)
|
45
|
+
ObjectSpace.define_finalizer(self, @core.finalizer)
|
68
46
|
end
|
69
47
|
|
70
48
|
def updated?
|
71
|
-
@
|
72
|
-
|
73
|
-
|
74
|
-
@pid = Process.pid
|
75
|
-
@updated.make_true
|
76
|
-
end
|
49
|
+
if @core.restart?
|
50
|
+
@core.thread_safely(&:restart)
|
51
|
+
@core.updated.make_true
|
77
52
|
end
|
78
|
-
|
53
|
+
|
54
|
+
@core.updated.true?
|
79
55
|
end
|
80
56
|
|
81
57
|
def execute
|
82
|
-
@updated.make_false
|
58
|
+
@core.updated.make_false
|
83
59
|
@block.call
|
84
60
|
end
|
85
61
|
|
@@ -91,115 +67,104 @@ module ActiveSupport
|
|
91
67
|
end
|
92
68
|
end
|
93
69
|
|
94
|
-
|
95
|
-
|
96
|
-
Listen.to(*@dtw, &method(:changed)).start
|
97
|
-
end
|
70
|
+
class Core
|
71
|
+
attr_reader :updated
|
98
72
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
73
|
+
def initialize(files, dirs)
|
74
|
+
@files = files.map { |file| Pathname(file).expand_path }.to_set
|
75
|
+
|
76
|
+
@dirs = dirs.each_with_object({}) do |(dir, exts), hash|
|
77
|
+
hash[Pathname(dir).expand_path] = Array(exts).map { |ext| ext.to_s.sub(/\A\.?/, ".") }.to_set
|
102
78
|
end
|
103
|
-
end
|
104
79
|
|
105
|
-
|
106
|
-
file = @ph.xpath(file)
|
80
|
+
@common_path = common_path(@dirs.keys)
|
107
81
|
|
108
|
-
|
109
|
-
|
110
|
-
elsif file.directory?
|
111
|
-
false
|
112
|
-
else
|
113
|
-
ext = @ph.normalize_extension(file.extname)
|
82
|
+
@dtw = directories_to_watch
|
83
|
+
@missing = []
|
114
84
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
85
|
+
@updated = Concurrent::AtomicBoolean.new(false)
|
86
|
+
@mutex = Mutex.new
|
87
|
+
|
88
|
+
start
|
89
|
+
@after_fork = ActiveSupport::ForkTracker.after_fork { start }
|
123
90
|
end
|
124
91
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
92
|
+
def finalizer
|
93
|
+
proc do
|
94
|
+
stop
|
95
|
+
ActiveSupport::ForkTracker.unregister(@after_fork)
|
96
|
+
end
|
97
|
+
end
|
129
98
|
|
130
|
-
|
131
|
-
|
132
|
-
|
99
|
+
def thread_safely
|
100
|
+
@mutex.synchronize do
|
101
|
+
yield self
|
133
102
|
end
|
103
|
+
end
|
134
104
|
|
135
|
-
|
105
|
+
def start
|
106
|
+
normalize_dirs!
|
107
|
+
@dtw, @missing = [*@dtw, *@missing].partition(&:exist?)
|
108
|
+
@listener = @dtw.any? ? Listen.to(*@dtw, &method(:changed)) : nil
|
109
|
+
@listener&.start
|
136
110
|
end
|
137
111
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
112
|
+
def stop
|
113
|
+
@listener&.stop
|
114
|
+
end
|
142
115
|
|
143
|
-
|
144
|
-
|
145
|
-
|
116
|
+
def restart
|
117
|
+
stop
|
118
|
+
start
|
119
|
+
end
|
146
120
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
return if paths.empty?
|
151
|
-
|
152
|
-
lcsp = Pathname.new(paths[0])
|
153
|
-
|
154
|
-
paths[1..-1].each do |path|
|
155
|
-
until ascendant_of?(lcsp, path)
|
156
|
-
if lcsp.root?
|
157
|
-
# If we get here a root directory is not an ascendant of path.
|
158
|
-
# This may happen if there are paths in different drives on
|
159
|
-
# Windows.
|
160
|
-
return
|
161
|
-
else
|
162
|
-
lcsp = lcsp.parent
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
121
|
+
def restart?
|
122
|
+
@missing.any?(&:exist?)
|
123
|
+
end
|
166
124
|
|
167
|
-
|
125
|
+
def normalize_dirs!
|
126
|
+
@dirs.transform_keys! do |dir|
|
127
|
+
dir.exist? ? dir.realpath : dir
|
168
128
|
end
|
129
|
+
end
|
169
130
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
break ascendant if ascendant.directory?
|
174
|
-
end
|
131
|
+
def changed(modified, added, removed)
|
132
|
+
unless @updated.true?
|
133
|
+
@updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
|
175
134
|
end
|
135
|
+
end
|
176
136
|
|
177
|
-
|
178
|
-
|
179
|
-
return dirs if dirs.length < 2
|
137
|
+
def watching?(file)
|
138
|
+
file = Pathname(file)
|
180
139
|
|
181
|
-
|
182
|
-
|
140
|
+
if @files.member?(file)
|
141
|
+
true
|
142
|
+
elsif file.directory?
|
143
|
+
false
|
144
|
+
else
|
145
|
+
ext = file.extname
|
183
146
|
|
184
|
-
|
185
|
-
|
147
|
+
file.dirname.ascend do |dir|
|
148
|
+
matching = @dirs[dir]
|
186
149
|
|
187
|
-
|
188
|
-
|
150
|
+
if matching && (matching.empty? || matching.include?(ext))
|
151
|
+
break true
|
152
|
+
elsif dir == @common_path || dir.root?
|
153
|
+
break false
|
189
154
|
end
|
190
155
|
end
|
191
|
-
|
192
|
-
# Array#- preserves order.
|
193
|
-
dirs - descendants
|
194
156
|
end
|
157
|
+
end
|
195
158
|
|
196
|
-
|
159
|
+
def directories_to_watch
|
160
|
+
dtw = @dirs.keys | @files.map(&:dirname)
|
161
|
+
accounted_for = dtw.to_set + Gem.path.map { |path| Pathname(path) }
|
162
|
+
dtw.reject { |dir| dir.ascend.drop(1).any? { |parent| accounted_for.include?(parent) } }
|
163
|
+
end
|
197
164
|
|
198
|
-
|
199
|
-
|
200
|
-
break true if base == ascendant
|
201
|
-
end
|
202
|
-
end
|
165
|
+
def common_path(paths)
|
166
|
+
paths.map { |path| path.ascend.to_a }.reduce(&:&)&.first
|
203
167
|
end
|
168
|
+
end
|
204
169
|
end
|
205
170
|
end
|