activesupport 5.2.8.1 → 6.1.6.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 +426 -424
- 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 +34 -34
- data/lib/active_support/cache/mem_cache_store.rb +39 -24
- 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 +72 -45
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/cache.rb +148 -78
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +70 -3
- 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 +51 -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/uuid.rb +1 -0
- data/lib/active_support/core_ext/enumerable.rb +171 -75
- 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 +14 -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 +34 -13
- 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/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 +70 -13
- 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 +53 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +6 -1
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +16 -2
- data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
- data/lib/active_support/dependencies.rb +109 -34
- data/lib/active_support/deprecation/behaviors.rb +16 -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 +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/digest.rb +2 -0
- 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 +82 -33
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +22 -4
- 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 +2 -1
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +64 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +70 -42
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +49 -58
- 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 +50 -6
- 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 +12 -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 +2 -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/rescuable.rb +4 -4
- 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 -28
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +91 -0
- 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/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 -17
- 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 +54 -27
- 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 -3
- 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,24 +181,28 @@ 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
|
-
|
189
|
+
remainder_sign = value <=> 0
|
190
|
+
remainder = value.round(9).abs
|
187
191
|
|
188
192
|
PARTS.each do |part|
|
189
193
|
unless part == :seconds
|
190
194
|
part_in_seconds = PARTS_IN_SECONDS[part]
|
191
|
-
parts[part] = remainder.div(part_in_seconds)
|
195
|
+
parts[part] = remainder.div(part_in_seconds) * remainder_sign
|
192
196
|
remainder %= part_in_seconds
|
193
197
|
end
|
194
198
|
end unless value == 0
|
195
199
|
|
196
|
-
parts[:seconds] = remainder
|
200
|
+
parts[:seconds] = remainder * remainder_sign
|
197
201
|
|
198
202
|
new(value, parts)
|
199
203
|
end
|
200
204
|
|
201
205
|
private
|
202
|
-
|
203
206
|
def calculate_total_seconds(parts)
|
204
207
|
parts.inject(0) do |total, (part, value)|
|
205
208
|
total + value * PARTS_IN_SECONDS[part]
|
@@ -208,14 +211,16 @@ module ActiveSupport
|
|
208
211
|
end
|
209
212
|
|
210
213
|
def initialize(value, parts) #:nodoc:
|
211
|
-
@value, @parts = value, parts
|
212
|
-
@parts.default = 0
|
214
|
+
@value, @parts = value, parts
|
213
215
|
@parts.reject! { |k, v| v.zero? } unless value == 0
|
214
216
|
end
|
215
217
|
|
216
218
|
def coerce(other) #:nodoc:
|
217
|
-
|
219
|
+
case other
|
220
|
+
when Scalar
|
218
221
|
[other, self]
|
222
|
+
when Duration
|
223
|
+
[Scalar.new(other.value), self]
|
219
224
|
else
|
220
225
|
[Scalar.new(other), self]
|
221
226
|
end
|
@@ -235,13 +240,12 @@ module ActiveSupport
|
|
235
240
|
# are treated as seconds.
|
236
241
|
def +(other)
|
237
242
|
if Duration === other
|
238
|
-
parts = @parts.
|
239
|
-
|
240
|
-
parts[key] += value
|
243
|
+
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
244
|
+
value + other_value
|
241
245
|
end
|
242
246
|
Duration.new(value + other.value, parts)
|
243
247
|
else
|
244
|
-
seconds = @parts
|
248
|
+
seconds = @parts.fetch(:seconds, 0) + other
|
245
249
|
Duration.new(value + other, @parts.merge(seconds: seconds))
|
246
250
|
end
|
247
251
|
end
|
@@ -255,9 +259,9 @@ module ActiveSupport
|
|
255
259
|
# Multiplies this Duration by a Numeric and returns a new Duration.
|
256
260
|
def *(other)
|
257
261
|
if Scalar === other || Duration === other
|
258
|
-
Duration.new(value * other.value, parts.
|
262
|
+
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
259
263
|
elsif Numeric === other
|
260
|
-
Duration.new(value * other, parts.
|
264
|
+
Duration.new(value * other, parts.transform_values { |number| number * other })
|
261
265
|
else
|
262
266
|
raise_type_error(other)
|
263
267
|
end
|
@@ -266,11 +270,11 @@ module ActiveSupport
|
|
266
270
|
# Divides this Duration by a Numeric and returns a new Duration.
|
267
271
|
def /(other)
|
268
272
|
if Scalar === other
|
269
|
-
Duration.new(value / other.value, parts.
|
273
|
+
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
270
274
|
elsif Duration === other
|
271
275
|
value / other.value
|
272
276
|
elsif Numeric === other
|
273
|
-
Duration.new(value / other, parts.
|
277
|
+
Duration.new(value / other, parts.transform_values { |number| number / other })
|
274
278
|
else
|
275
279
|
raise_type_error(other)
|
276
280
|
end
|
@@ -289,7 +293,11 @@ module ActiveSupport
|
|
289
293
|
end
|
290
294
|
|
291
295
|
def -@ #:nodoc:
|
292
|
-
Duration.new(-value, parts.
|
296
|
+
Duration.new(-value, parts.transform_values(&:-@))
|
297
|
+
end
|
298
|
+
|
299
|
+
def +@ #:nodoc:
|
300
|
+
self
|
293
301
|
end
|
294
302
|
|
295
303
|
def is_a?(klass) #:nodoc:
|
@@ -336,12 +344,55 @@ module ActiveSupport
|
|
336
344
|
# 1.year.to_i # => 31556952
|
337
345
|
#
|
338
346
|
# In such cases, Ruby's core
|
339
|
-
# Date[
|
340
|
-
# Time[
|
347
|
+
# Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
|
348
|
+
# Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
|
341
349
|
# date and time arithmetic.
|
342
350
|
def to_i
|
343
351
|
@value.to_i
|
344
352
|
end
|
353
|
+
alias :in_seconds :to_i
|
354
|
+
|
355
|
+
# Returns the amount of minutes a duration covers as a float
|
356
|
+
#
|
357
|
+
# 1.day.in_minutes # => 1440.0
|
358
|
+
def in_minutes
|
359
|
+
in_seconds / SECONDS_PER_MINUTE.to_f
|
360
|
+
end
|
361
|
+
|
362
|
+
# Returns the amount of hours a duration covers as a float
|
363
|
+
#
|
364
|
+
# 1.day.in_hours # => 24.0
|
365
|
+
def in_hours
|
366
|
+
in_seconds / SECONDS_PER_HOUR.to_f
|
367
|
+
end
|
368
|
+
|
369
|
+
# Returns the amount of days a duration covers as a float
|
370
|
+
#
|
371
|
+
# 12.hours.in_days # => 0.5
|
372
|
+
def in_days
|
373
|
+
in_seconds / SECONDS_PER_DAY.to_f
|
374
|
+
end
|
375
|
+
|
376
|
+
# Returns the amount of weeks a duration covers as a float
|
377
|
+
#
|
378
|
+
# 2.months.in_weeks # => 8.696
|
379
|
+
def in_weeks
|
380
|
+
in_seconds / SECONDS_PER_WEEK.to_f
|
381
|
+
end
|
382
|
+
|
383
|
+
# Returns the amount of months a duration covers as a float
|
384
|
+
#
|
385
|
+
# 9.weeks.in_months # => 2.07
|
386
|
+
def in_months
|
387
|
+
in_seconds / SECONDS_PER_MONTH.to_f
|
388
|
+
end
|
389
|
+
|
390
|
+
# Returns the amount of years a duration covers as a float
|
391
|
+
#
|
392
|
+
# 30.days.in_years # => 0.082
|
393
|
+
def in_years
|
394
|
+
in_seconds / SECONDS_PER_YEAR.to_f
|
395
|
+
end
|
345
396
|
|
346
397
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
347
398
|
# same parts as this one.
|
@@ -370,10 +421,9 @@ module ActiveSupport
|
|
370
421
|
alias :before :ago
|
371
422
|
|
372
423
|
def inspect #:nodoc:
|
373
|
-
return "
|
424
|
+
return "#{value} seconds" if parts.empty?
|
374
425
|
|
375
426
|
parts.
|
376
|
-
reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
|
377
427
|
sort_by { |unit, _ | PARTS.index(unit) }.
|
378
428
|
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
379
429
|
to_sentence(locale: ::I18n.default_locale)
|
@@ -398,7 +448,6 @@ module ActiveSupport
|
|
398
448
|
end
|
399
449
|
|
400
450
|
private
|
401
|
-
|
402
451
|
def sum(sign, time = ::Time.current)
|
403
452
|
unless time.acts_like?(:time) || time.acts_like?(:date)
|
404
453
|
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
@@ -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
|
|
@@ -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
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/callbacks"
|
4
|
+
require "concurrent/hash"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
class ExecutionWrapper
|
@@ -65,7 +66,7 @@ module ActiveSupport
|
|
65
66
|
def self.run!(reset: false)
|
66
67
|
if reset
|
67
68
|
lost_instance = active.delete(Thread.current)
|
68
|
-
lost_instance
|
69
|
+
lost_instance&.complete!
|
69
70
|
else
|
70
71
|
return Null if active?
|
71
72
|
end
|