activesupport 7.2.3 → 8.0.0.beta1
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 +41 -385
- data/README.rdoc +1 -1
- data/lib/active_support/backtrace_cleaner.rb +2 -2
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +74 -61
- data/lib/active_support/cache/file_store.rb +14 -4
- data/lib/active_support/cache/mem_cache_store.rb +15 -13
- data/lib/active_support/cache/memory_store.rb +9 -5
- data/lib/active_support/cache/null_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +6 -3
- data/lib/active_support/cache/strategy/local_cache.rb +20 -56
- data/lib/active_support/cache.rb +17 -12
- data/lib/active_support/callbacks.rb +3 -5
- data/lib/active_support/class_attribute.rb +26 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/core_ext/benchmark.rb +6 -10
- data/lib/active_support/core_ext/class/attribute.rb +12 -21
- data/lib/active_support/core_ext/date/conversions.rb +2 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +2 -2
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -4
- data/lib/active_support/core_ext/enumerable.rb +13 -20
- data/lib/active_support/core_ext/erb/util.rb +2 -2
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -3
- data/lib/active_support/core_ext/object/json.rb +18 -14
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range.rb +0 -1
- data/lib/active_support/core_ext/securerandom.rb +8 -24
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/multibyte.rb +3 -3
- data/lib/active_support/core_ext/time/calculations.rb +14 -2
- data/lib/active_support/core_ext/time/compatibility.rb +1 -9
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/time/zones.rb +1 -1
- data/lib/active_support/current_attributes.rb +7 -14
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/error_reporter.rb +27 -6
- data/lib/active_support/execution_wrapper.rb +1 -1
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +31 -31
- data/lib/active_support/i18n_railtie.rb +19 -10
- data/lib/active_support/isolated_execution_state.rb +0 -1
- data/lib/active_support/json/decoding.rb +1 -1
- data/lib/active_support/json/encoding.rb +7 -25
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/message_encryptors.rb +2 -2
- data/lib/active_support/message_verifier.rb +0 -9
- data/lib/active_support/message_verifiers.rb +3 -5
- data/lib/active_support/messages/rotator.rb +0 -5
- data/lib/active_support/multibyte/chars.rb +1 -4
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/railtie.rb +4 -0
- data/lib/active_support/tagged_logging.rb +5 -0
- data/lib/active_support/testing/assertions.rb +72 -21
- data/lib/active_support/testing/isolation.rb +0 -2
- data/lib/active_support/testing/parallelization/server.rb +2 -15
- data/lib/active_support/testing/parallelization/worker.rb +2 -2
- data/lib/active_support/testing/parallelization.rb +1 -12
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/time_helpers.rb +2 -1
- data/lib/active_support/time_with_zone.rb +22 -13
- data/lib/active_support/values/time_zone.rb +17 -15
- data/lib/active_support/xml_mini.rb +0 -2
- data/lib/active_support.rb +10 -2
- metadata +27 -8
- data/lib/active_support/core_ext/range/sole.rb +0 -17
|
@@ -120,7 +120,7 @@ module ActiveSupport
|
|
|
120
120
|
# healthy to consider this edge case because with mtimes in the future
|
|
121
121
|
# reloading is not triggered.
|
|
122
122
|
def max_mtime(paths)
|
|
123
|
-
time_now = Time.
|
|
123
|
+
time_now = Time.now
|
|
124
124
|
max_mtime = nil
|
|
125
125
|
|
|
126
126
|
# Time comparisons are performed with #compare_without_coercion because
|
|
@@ -262,7 +262,9 @@ module ActiveSupport
|
|
|
262
262
|
# hash[:a][:c] # => "c"
|
|
263
263
|
# dup[:a][:c] # => "c"
|
|
264
264
|
def dup
|
|
265
|
-
|
|
265
|
+
self.class.new(self).tap do |new_hash|
|
|
266
|
+
set_defaults(new_hash)
|
|
267
|
+
end
|
|
266
268
|
end
|
|
267
269
|
|
|
268
270
|
# This method has the same semantics of +update+, except it does not
|
|
@@ -340,26 +342,21 @@ module ActiveSupport
|
|
|
340
342
|
NOT_GIVEN = Object.new # :nodoc:
|
|
341
343
|
|
|
342
344
|
def transform_keys(hash = NOT_GIVEN, &block)
|
|
343
|
-
if NOT_GIVEN.equal?(hash)
|
|
344
|
-
|
|
345
|
-
self.class.new(super(&block))
|
|
346
|
-
else
|
|
347
|
-
to_enum(:transform_keys)
|
|
348
|
-
end
|
|
349
|
-
else
|
|
350
|
-
self.class.new(super)
|
|
351
|
-
end
|
|
345
|
+
return to_enum(:transform_keys) if NOT_GIVEN.equal?(hash) && !block_given?
|
|
346
|
+
dup.tap { |h| h.transform_keys!(hash, &block) }
|
|
352
347
|
end
|
|
353
348
|
|
|
354
349
|
def transform_keys!(hash = NOT_GIVEN, &block)
|
|
355
|
-
if NOT_GIVEN.equal?(hash)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
350
|
+
return to_enum(:transform_keys!) if NOT_GIVEN.equal?(hash) && !block_given?
|
|
351
|
+
|
|
352
|
+
if hash.nil?
|
|
353
|
+
super
|
|
354
|
+
elsif NOT_GIVEN.equal?(hash)
|
|
355
|
+
keys.each { |key| self[yield(key)] = delete(key) }
|
|
356
|
+
elsif block_given?
|
|
357
|
+
keys.each { |key| self[hash[key] || yield(key)] = delete(key) }
|
|
361
358
|
else
|
|
362
|
-
|
|
359
|
+
keys.each { |key| self[hash[key] || key] = delete(key) }
|
|
363
360
|
end
|
|
364
361
|
|
|
365
362
|
self
|
|
@@ -381,13 +378,10 @@ module ActiveSupport
|
|
|
381
378
|
|
|
382
379
|
# Convert to a regular hash with string keys.
|
|
383
380
|
def to_hash
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
_new_hash[key] = convert_value(value, conversion: :to_hash)
|
|
389
|
-
end
|
|
390
|
-
_new_hash
|
|
381
|
+
copy = Hash[self]
|
|
382
|
+
copy.transform_values! { |v| convert_value_to_hash(v) }
|
|
383
|
+
set_defaults(copy)
|
|
384
|
+
copy
|
|
391
385
|
end
|
|
392
386
|
|
|
393
387
|
def to_proc
|
|
@@ -401,11 +395,7 @@ module ActiveSupport
|
|
|
401
395
|
|
|
402
396
|
def convert_value(value, conversion: nil)
|
|
403
397
|
if value.is_a? Hash
|
|
404
|
-
|
|
405
|
-
value.to_hash
|
|
406
|
-
else
|
|
407
|
-
value.nested_under_indifferent_access
|
|
408
|
-
end
|
|
398
|
+
value.nested_under_indifferent_access
|
|
409
399
|
elsif value.is_a?(Array)
|
|
410
400
|
if conversion != :assignment || value.frozen?
|
|
411
401
|
value = value.dup
|
|
@@ -416,13 +406,23 @@ module ActiveSupport
|
|
|
416
406
|
end
|
|
417
407
|
end
|
|
418
408
|
|
|
419
|
-
def
|
|
409
|
+
def convert_value_to_hash(value)
|
|
410
|
+
if value.is_a? Hash
|
|
411
|
+
value.to_hash
|
|
412
|
+
elsif value.is_a?(Array)
|
|
413
|
+
value.map { |e| convert_value_to_hash(e) }
|
|
414
|
+
else
|
|
415
|
+
value
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def set_defaults(target)
|
|
420
421
|
if default_proc
|
|
421
422
|
target.default_proc = default_proc.dup
|
|
422
423
|
else
|
|
423
424
|
target.default = default
|
|
424
425
|
end
|
|
425
|
-
target
|
|
426
426
|
end
|
|
427
427
|
|
|
428
428
|
def update_with_single_argument(other_hash, block)
|
|
@@ -14,15 +14,18 @@ module I18n
|
|
|
14
14
|
|
|
15
15
|
config.eager_load_namespaces << I18n
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
config.
|
|
17
|
+
# Make sure i18n is ready before eager loading, in case any eager loaded
|
|
18
|
+
# code needs it.
|
|
19
|
+
config.before_eager_load do |app|
|
|
20
20
|
I18n::Railtie.initialize_i18n(app)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
|
|
23
|
+
# i18n initialization needs to run after application initialization, since
|
|
24
|
+
# initializers may configure i18n.
|
|
25
|
+
#
|
|
26
|
+
# If the application eager loaded, this was done on before_eager_load. The
|
|
27
|
+
# hook is still OK, though, because initialize_i18n is idempotent.
|
|
28
|
+
config.after_initialize do |app|
|
|
26
29
|
I18n::Railtie.initialize_i18n(app)
|
|
27
30
|
end
|
|
28
31
|
|
|
@@ -49,7 +52,8 @@ module I18n
|
|
|
49
52
|
when :load_path
|
|
50
53
|
I18n.load_path += value
|
|
51
54
|
when :raise_on_missing_translations
|
|
52
|
-
|
|
55
|
+
strict = value == :strict
|
|
56
|
+
setup_raise_on_missing_translations_config(app, strict)
|
|
53
57
|
else
|
|
54
58
|
I18n.public_send("#{setting}=", value)
|
|
55
59
|
end
|
|
@@ -62,8 +66,9 @@ module I18n
|
|
|
62
66
|
|
|
63
67
|
if app.config.reloading_enabled?
|
|
64
68
|
directories = watched_dirs_with_extensions(reloadable_paths)
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
root_load_paths = I18n.load_path.select { |path| path.start_with?(Rails.root.to_s) }
|
|
70
|
+
reloader = app.config.file_watcher.new(root_load_paths, directories) do
|
|
71
|
+
I18n.load_path.delete_if { |p| p.start_with?(Rails.root.to_s) && !File.exist?(p) }
|
|
67
72
|
I18n.load_path |= reloadable_paths.flat_map(&:existent)
|
|
68
73
|
end
|
|
69
74
|
|
|
@@ -77,11 +82,15 @@ module I18n
|
|
|
77
82
|
@i18n_inited = true
|
|
78
83
|
end
|
|
79
84
|
|
|
80
|
-
def self.setup_raise_on_missing_translations_config(app)
|
|
85
|
+
def self.setup_raise_on_missing_translations_config(app, strict)
|
|
81
86
|
ActiveSupport.on_load(:action_view) do
|
|
82
87
|
ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
|
|
83
88
|
end
|
|
84
89
|
|
|
90
|
+
ActiveSupport.on_load(:active_model_translation) do
|
|
91
|
+
ActiveModel::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations if strict
|
|
92
|
+
end
|
|
93
|
+
|
|
85
94
|
if app.config.i18n.raise_on_missing_translations &&
|
|
86
95
|
I18n.exception_handler.is_a?(I18n::ExceptionHandler) # Only override the i18n gem's default exception handler.
|
|
87
96
|
|
|
@@ -18,7 +18,7 @@ module ActiveSupport
|
|
|
18
18
|
# See http://www.json.org for more info.
|
|
19
19
|
#
|
|
20
20
|
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
|
|
21
|
-
#
|
|
21
|
+
# => {"team" => "rails", "players" => "36"}
|
|
22
22
|
def decode(json)
|
|
23
23
|
data = ::JSON.parse(json, quirks_mode: true)
|
|
24
24
|
|
|
@@ -13,30 +13,12 @@ module ActiveSupport
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
module JSON
|
|
16
|
+
# Dumps objects in JSON (JavaScript Object Notation).
|
|
17
|
+
# See http://www.json.org for more info.
|
|
18
|
+
#
|
|
19
|
+
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
|
|
20
|
+
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
|
|
16
21
|
class << self
|
|
17
|
-
# Dumps objects in JSON (JavaScript Object Notation).
|
|
18
|
-
# See http://www.json.org for more info.
|
|
19
|
-
#
|
|
20
|
-
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
|
|
21
|
-
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
|
|
22
|
-
#
|
|
23
|
-
# Generates JSON that is safe to include in JavaScript as it escapes
|
|
24
|
-
# U+2028 (Line Separator) and U+2029 (Paragraph Separator):
|
|
25
|
-
#
|
|
26
|
-
# ActiveSupport::JSON.encode({ key: "\u2028" })
|
|
27
|
-
# # => "{\"key\":\"\\u2028\"}"
|
|
28
|
-
#
|
|
29
|
-
# By default, it also generates JSON that is safe to include in HTML, as
|
|
30
|
-
# it escapes <tt><</tt>, <tt>></tt>, and <tt>&</tt>:
|
|
31
|
-
#
|
|
32
|
-
# ActiveSupport::JSON.encode({ key: "<>&" })
|
|
33
|
-
# # => "{\"key\":\"\\u003c\\u003e\\u0026\"}"
|
|
34
|
-
#
|
|
35
|
-
# This can be changed with the +escape_html_entities+ option, or the
|
|
36
|
-
# global escape_html_entities_in_json configuration option.
|
|
37
|
-
#
|
|
38
|
-
# ActiveSupport::JSON.encode({ key: "<>&" }, escape_html_entities: false)
|
|
39
|
-
# # => "{\"key\":\"<>&\"}"
|
|
40
22
|
def encode(value, options = nil)
|
|
41
23
|
Encoding.json_encoder.new(options).encode(value)
|
|
42
24
|
end
|
|
@@ -54,14 +36,14 @@ module ActiveSupport
|
|
|
54
36
|
# Encode the given object into a JSON string
|
|
55
37
|
def encode(value)
|
|
56
38
|
unless options.empty?
|
|
57
|
-
value = value.as_json(options.dup)
|
|
39
|
+
value = value.as_json(options.dup.freeze)
|
|
58
40
|
end
|
|
59
41
|
json = stringify(jsonify(value))
|
|
60
42
|
|
|
61
43
|
# Rails does more escaping than the JSON gem natively does (we
|
|
62
44
|
# escape \u2028 and \u2029 and optionally >, <, & to work around
|
|
63
45
|
# certain browser problems).
|
|
64
|
-
if Encoding.escape_html_entities_in_json
|
|
46
|
+
if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
|
|
65
47
|
json.gsub!(">", '\u003e')
|
|
66
48
|
json.gsub!("<", '\u003c')
|
|
67
49
|
json.gsub!("&", '\u0026')
|
|
@@ -53,7 +53,7 @@ module ActiveSupport
|
|
|
53
53
|
# loaded. If the component has already loaded, the block is executed
|
|
54
54
|
# immediately.
|
|
55
55
|
#
|
|
56
|
-
#
|
|
56
|
+
# Options:
|
|
57
57
|
#
|
|
58
58
|
# * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
|
|
59
59
|
# * <tt>:run_once</tt> - Given +block+ will run only once.
|
|
@@ -28,8 +28,8 @@ module ActiveSupport
|
|
|
28
28
|
# <tt>transitional = false</tt>.
|
|
29
29
|
|
|
30
30
|
##
|
|
31
|
-
# :
|
|
32
|
-
# :call-seq:
|
|
31
|
+
# :method: initialize
|
|
32
|
+
# :call-seq: initialize(&secret_generator)
|
|
33
33
|
#
|
|
34
34
|
# Initializes a new instance. +secret_generator+ must accept a salt and a
|
|
35
35
|
# +secret_length+ kwarg, and return a suitable secret (string) or secrets
|
|
@@ -154,8 +154,6 @@ module ActiveSupport
|
|
|
154
154
|
# not URL-safe. In other words, they can contain "+" and "/". If you want to
|
|
155
155
|
# generate URL-safe strings (in compliance with "Base 64 Encoding with URL
|
|
156
156
|
# and Filename Safe Alphabet" in RFC 4648), you can pass +true+.
|
|
157
|
-
# Note that MessageVerifier will always accept both URL-safe and URL-unsafe
|
|
158
|
-
# encoded messages, to allow a smooth transition between the two settings.
|
|
159
157
|
#
|
|
160
158
|
# [+:force_legacy_metadata_serializer+]
|
|
161
159
|
# Whether to use the legacy metadata serializer, which serializes the
|
|
@@ -320,13 +318,6 @@ module ActiveSupport
|
|
|
320
318
|
end
|
|
321
319
|
|
|
322
320
|
private
|
|
323
|
-
def decode(encoded, url_safe: @url_safe)
|
|
324
|
-
catch :invalid_message_format do
|
|
325
|
-
return super
|
|
326
|
-
end
|
|
327
|
-
super(encoded, url_safe: !url_safe)
|
|
328
|
-
end
|
|
329
|
-
|
|
330
321
|
def sign_encoded(encoded)
|
|
331
322
|
digest = generate_digest(encoded)
|
|
332
323
|
encoded << SEPARATOR << digest
|
|
@@ -28,8 +28,8 @@ module ActiveSupport
|
|
|
28
28
|
# <tt>transitional = false</tt>.
|
|
29
29
|
|
|
30
30
|
##
|
|
31
|
-
# :
|
|
32
|
-
# :call-seq:
|
|
31
|
+
# :method: initialize
|
|
32
|
+
# :call-seq: initialize(&secret_generator)
|
|
33
33
|
#
|
|
34
34
|
# Initializes a new instance. +secret_generator+ must accept a salt, and
|
|
35
35
|
# return a suitable secret (string). +secret_generator+ may also accept
|
|
@@ -59,9 +59,7 @@ module ActiveSupport
|
|
|
59
59
|
|
|
60
60
|
##
|
|
61
61
|
# :method: rotate
|
|
62
|
-
# :call-seq:
|
|
63
|
-
# rotate(**options)
|
|
64
|
-
# rotate(&block)
|
|
62
|
+
# :call-seq: rotate(**options)
|
|
65
63
|
#
|
|
66
64
|
# Adds +options+ to the list of option sets. Messages will be signed using
|
|
67
65
|
# the first set in the list. When verifying, however, each set will be
|
|
@@ -55,10 +55,7 @@ module ActiveSupport # :nodoc:
|
|
|
55
55
|
# Creates a new Chars instance by wrapping _string_.
|
|
56
56
|
def initialize(string)
|
|
57
57
|
@wrapped_string = string
|
|
58
|
-
|
|
59
|
-
@wrapped_string = @wrapped_string.dup
|
|
60
|
-
@wrapped_string.force_encoding(Encoding::UTF_8)
|
|
61
|
-
end
|
|
58
|
+
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
# Forward all undefined methods to the wrapped string.
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ActiveSupport
|
|
4
|
+
# = Number Helper
|
|
5
|
+
#
|
|
6
|
+
# Provides methods for formatting numbers into currencies, percentages,
|
|
7
|
+
# phone numbers, and more.
|
|
8
|
+
#
|
|
9
|
+
# Example usage in a class:
|
|
10
|
+
# class Topic
|
|
11
|
+
# include ActiveSupport::NumberHelper
|
|
12
|
+
#
|
|
13
|
+
# def price
|
|
14
|
+
# number_to_currency(@price)
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# Example usage in a module:
|
|
19
|
+
# require "active_support/number_helper"
|
|
20
|
+
#
|
|
21
|
+
# module NumberFormatting
|
|
22
|
+
# def format_price(price)
|
|
23
|
+
# ActiveSupport::NumberHelper.number_to_currency(price)
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
4
26
|
module NumberHelper
|
|
5
27
|
extend ActiveSupport::Autoload
|
|
6
28
|
|
|
@@ -96,6 +96,10 @@ module ActiveSupport
|
|
|
96
96
|
config.eager_load_namespaces << TZInfo
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
+
initializer "active_support.to_time_preserves_timezone" do |app|
|
|
100
|
+
ActiveSupport.to_time_preserves_timezone = app.config.active_support.to_time_preserves_timezone
|
|
101
|
+
end
|
|
102
|
+
|
|
99
103
|
# Sets the default week start
|
|
100
104
|
# If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
|
|
101
105
|
initializer "active_support.initialize_beginning_of_week" do |app|
|
|
@@ -113,6 +113,11 @@ module ActiveSupport
|
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
+
# Returns an `ActiveSupport::Logger` that has already been wrapped with tagged logging concern.
|
|
117
|
+
def self.logger(*args, **kwargs)
|
|
118
|
+
new ActiveSupport::Logger.new(*args, **kwargs)
|
|
119
|
+
end
|
|
120
|
+
|
|
116
121
|
def self.new(logger)
|
|
117
122
|
logger = logger.clone
|
|
118
123
|
|
|
@@ -19,7 +19,7 @@ module ActiveSupport
|
|
|
19
19
|
#
|
|
20
20
|
# assert_not foo, 'foo should be false'
|
|
21
21
|
def assert_not(object, message = nil)
|
|
22
|
-
message ||= "Expected #{mu_pp(object)} to be nil or false"
|
|
22
|
+
message ||= -> { "Expected #{mu_pp(object)} to be nil or false" }
|
|
23
23
|
assert !object, message
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -118,9 +118,13 @@ module ActiveSupport
|
|
|
118
118
|
|
|
119
119
|
expressions.zip(exps, before) do |(code, diff), exp, before_value|
|
|
120
120
|
actual = exp.call
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
rich_message = -> do
|
|
122
|
+
code_string = code.respond_to?(:call) ? _callable_to_source_string(code) : code
|
|
123
|
+
error = "`#{code_string}` didn't change by #{diff}, but by #{actual - before_value}"
|
|
124
|
+
error = "#{message}.\n#{error}" if message
|
|
125
|
+
error
|
|
126
|
+
end
|
|
127
|
+
assert_equal(before_value + diff, actual, rich_message)
|
|
124
128
|
end
|
|
125
129
|
|
|
126
130
|
retval
|
|
@@ -195,22 +199,32 @@ module ActiveSupport
|
|
|
195
199
|
retval = _assert_nothing_raised_or_warn("assert_changes", &block)
|
|
196
200
|
|
|
197
201
|
unless from == UNTRACKED
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
rich_message = -> do
|
|
203
|
+
error = "Expected change from #{from.inspect}, got #{before.inspect}"
|
|
204
|
+
error = "#{message}.\n#{error}" if message
|
|
205
|
+
error
|
|
206
|
+
end
|
|
207
|
+
assert from === before, rich_message
|
|
201
208
|
end
|
|
202
209
|
|
|
203
210
|
after = exp.call
|
|
204
211
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
rich_message = -> do
|
|
213
|
+
code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
|
|
214
|
+
error = "`#{code_string}` didn't change"
|
|
215
|
+
error = "#{error}. It was already #{to.inspect}" if before == to
|
|
216
|
+
error = "#{message}.\n#{error}" if message
|
|
217
|
+
error
|
|
218
|
+
end
|
|
219
|
+
refute_equal before, after, rich_message
|
|
209
220
|
|
|
210
221
|
unless to == UNTRACKED
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
222
|
+
rich_message = -> do
|
|
223
|
+
error = "Expected change to #{to.inspect}, got #{after.inspect}\n"
|
|
224
|
+
error = "#{message}.\n#{error}" if message
|
|
225
|
+
error
|
|
226
|
+
end
|
|
227
|
+
assert to === after, rich_message
|
|
214
228
|
end
|
|
215
229
|
|
|
216
230
|
retval
|
|
@@ -242,20 +256,27 @@ module ActiveSupport
|
|
|
242
256
|
retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
|
|
243
257
|
|
|
244
258
|
unless from == UNTRACKED
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
259
|
+
rich_message = -> do
|
|
260
|
+
error = "Expected initial value of #{from.inspect}, got #{before.inspect}"
|
|
261
|
+
error = "#{message}.\n#{error}" if message
|
|
262
|
+
error
|
|
263
|
+
end
|
|
264
|
+
assert from === before, rich_message
|
|
248
265
|
end
|
|
249
266
|
|
|
250
267
|
after = exp.call
|
|
251
268
|
|
|
252
|
-
|
|
253
|
-
|
|
269
|
+
rich_message = -> do
|
|
270
|
+
code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
|
|
271
|
+
error = "`#{code_string}` changed"
|
|
272
|
+
error = "#{message}.\n#{error}" if message
|
|
273
|
+
error
|
|
274
|
+
end
|
|
254
275
|
|
|
255
276
|
if before.nil?
|
|
256
|
-
assert_nil after,
|
|
277
|
+
assert_nil after, rich_message
|
|
257
278
|
else
|
|
258
|
-
assert_equal before, after,
|
|
279
|
+
assert_equal before, after, rich_message
|
|
259
280
|
end
|
|
260
281
|
|
|
261
282
|
retval
|
|
@@ -276,6 +297,36 @@ module ActiveSupport
|
|
|
276
297
|
|
|
277
298
|
raise
|
|
278
299
|
end
|
|
300
|
+
|
|
301
|
+
def _callable_to_source_string(callable)
|
|
302
|
+
if defined?(RubyVM::AbstractSyntaxTree) && callable.is_a?(Proc)
|
|
303
|
+
ast = begin
|
|
304
|
+
RubyVM::AbstractSyntaxTree.of(callable, keep_script_lines: true)
|
|
305
|
+
rescue SystemCallError
|
|
306
|
+
# Failed to get the source somehow
|
|
307
|
+
return callable
|
|
308
|
+
end
|
|
309
|
+
return callable unless ast
|
|
310
|
+
|
|
311
|
+
source = ast.source
|
|
312
|
+
source.strip!
|
|
313
|
+
|
|
314
|
+
# We ignore procs defined with do/end as they are likely multi-line anyway.
|
|
315
|
+
if source.start_with?("{")
|
|
316
|
+
source.delete_suffix!("}")
|
|
317
|
+
source.delete_prefix!("{")
|
|
318
|
+
source.strip!
|
|
319
|
+
# It won't read nice if the callable contains multiple
|
|
320
|
+
# lines, and it should be a rare occurence anyway.
|
|
321
|
+
# Same if it takes arguments.
|
|
322
|
+
if !source.include?("\n") && !source.start_with?("|")
|
|
323
|
+
return source
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
callable
|
|
329
|
+
end
|
|
279
330
|
end
|
|
280
331
|
end
|
|
281
332
|
end
|
|
@@ -14,7 +14,6 @@ module ActiveSupport
|
|
|
14
14
|
def initialize
|
|
15
15
|
@queue = Queue.new
|
|
16
16
|
@active_workers = Concurrent::Map.new
|
|
17
|
-
@worker_pids = Concurrent::Map.new
|
|
18
17
|
@in_flight = Concurrent::Map.new
|
|
19
18
|
end
|
|
20
19
|
|
|
@@ -41,24 +40,12 @@ module ActiveSupport
|
|
|
41
40
|
end
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
def start_worker(worker_id
|
|
43
|
+
def start_worker(worker_id)
|
|
45
44
|
@active_workers[worker_id] = true
|
|
46
|
-
@worker_pids[worker_id] = worker_pid
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
def stop_worker(worker_id
|
|
47
|
+
def stop_worker(worker_id)
|
|
50
48
|
@active_workers.delete(worker_id)
|
|
51
|
-
@worker_pids.delete(worker_id)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def remove_dead_workers(dead_pids)
|
|
55
|
-
dead_pids.each do |dead_pid|
|
|
56
|
-
worker_id = @worker_pids.key(dead_pid)
|
|
57
|
-
if worker_id
|
|
58
|
-
@active_workers.delete(worker_id)
|
|
59
|
-
@worker_pids.delete(worker_id)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
49
|
end
|
|
63
50
|
|
|
64
51
|
def active_workers?
|
|
@@ -18,7 +18,7 @@ module ActiveSupport
|
|
|
18
18
|
DRb.stop_service
|
|
19
19
|
|
|
20
20
|
@queue = DRbObject.new_with_uri(@url)
|
|
21
|
-
@queue.start_worker(@id
|
|
21
|
+
@queue.start_worker(@id)
|
|
22
22
|
|
|
23
23
|
begin
|
|
24
24
|
after_fork
|
|
@@ -29,7 +29,7 @@ module ActiveSupport
|
|
|
29
29
|
set_process_title("(stopping)")
|
|
30
30
|
|
|
31
31
|
run_cleanup
|
|
32
|
-
@queue.stop_worker(@id
|
|
32
|
+
@queue.stop_worker(@id)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -47,19 +47,8 @@ module ActiveSupport
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def shutdown
|
|
50
|
-
dead_worker_pids = @worker_pool.filter_map do |pid|
|
|
51
|
-
Process.waitpid(pid, Process::WNOHANG)
|
|
52
|
-
rescue Errno::ECHILD
|
|
53
|
-
pid
|
|
54
|
-
end
|
|
55
|
-
@queue_server.remove_dead_workers(dead_worker_pids)
|
|
56
|
-
|
|
57
50
|
@queue_server.shutdown
|
|
58
|
-
@worker_pool.each
|
|
59
|
-
Process.waitpid(pid)
|
|
60
|
-
rescue Errno::ECHILD
|
|
61
|
-
nil
|
|
62
|
-
end
|
|
51
|
+
@worker_pool.each { |pid| Process.waitpid pid }
|
|
63
52
|
end
|
|
64
53
|
end
|
|
65
54
|
end
|