activesupport 7.2.3.1 → 8.1.4.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 +584 -234
- data/lib/active_support/backtrace_cleaner.rb +72 -1
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/cache/file_store.rb +13 -2
- data/lib/active_support/cache/mem_cache_store.rb +17 -14
- data/lib/active_support/cache/memory_store.rb +9 -3
- data/lib/active_support/cache/redis_cache_store.rb +51 -34
- data/lib/active_support/cache/strategy/local_cache.rb +17 -8
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +86 -18
- data/lib/active_support/callbacks.rb +25 -11
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +34 -0
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/conversions.rb +3 -3
- data/lib/active_support/core_ext/array.rb +7 -7
- data/lib/active_support/core_ext/benchmark.rb +0 -16
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +24 -18
- data/lib/active_support/core_ext/class.rb +2 -2
- data/lib/active_support/core_ext/date/conversions.rb +2 -0
- data/lib/active_support/core_ext/date.rb +5 -5
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
- data/lib/active_support/core_ext/date_time.rb +5 -5
- data/lib/active_support/core_ext/digest.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +8 -3
- data/lib/active_support/core_ext/erb/util.rb +3 -3
- data/lib/active_support/core_ext/file.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/hash.rb +8 -8
- data/lib/active_support/core_ext/integer.rb +3 -3
- data/lib/active_support/core_ext/kernel.rb +3 -3
- data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
- data/lib/active_support/core_ext/module.rb +11 -11
- data/lib/active_support/core_ext/numeric.rb +3 -3
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +24 -11
- data/lib/active_support/core_ext/object/to_query.rb +7 -1
- data/lib/active_support/core_ext/object.rb +13 -13
- data/lib/active_support/core_ext/pathname.rb +2 -2
- data/lib/active_support/core_ext/range/compare_range.rb +2 -14
- data/lib/active_support/core_ext/range/overlap.rb +3 -3
- data/lib/active_support/core_ext/range/sole.rb +2 -0
- data/lib/active_support/core_ext/range.rb +4 -5
- data/lib/active_support/core_ext/string/filters.rb +2 -0
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/multibyte.rb +10 -1
- data/lib/active_support/core_ext/string/output_safety.rb +27 -13
- data/lib/active_support/core_ext/string.rb +13 -13
- data/lib/active_support/core_ext/symbol.rb +1 -1
- data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
- data/lib/active_support/core_ext/time/calculations.rb +11 -2
- data/lib/active_support/core_ext/time/compatibility.rb +2 -27
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/time.rb +5 -5
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +13 -10
- data/lib/active_support/delegation.rb +25 -44
- data/lib/active_support/dependencies/interlock.rb +11 -5
- data/lib/active_support/dependencies.rb +6 -2
- data/lib/active_support/deprecation/reporting.rb +4 -21
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/duration.rb +20 -16
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/error_reporter.rb +76 -2
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +5 -2
- data/lib/active_support/execution_context.rb +75 -7
- data/lib/active_support/file_update_checker.rb +7 -5
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +44 -24
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/inflector/inflections.rb +34 -16
- data/lib/active_support/inflector/methods.rb +6 -22
- data/lib/active_support/inflector/transliterate.rb +18 -10
- data/lib/active_support/isolated_execution_state.rb +17 -17
- data/lib/active_support/json/decoding.rb +9 -3
- data/lib/active_support/json/encoding.rb +149 -19
- data/lib/active_support/log_subscriber.rb +2 -6
- data/lib/active_support/logger_thread_safe_level.rb +6 -3
- data/lib/active_support/message_encryptors.rb +52 -0
- data/lib/active_support/message_pack/extensions.rb +23 -1
- data/lib/active_support/message_verifiers.rb +52 -0
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +5 -0
- data/lib/active_support/messages/serializer_with_fallback.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +8 -1
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/notifications/fanout.rb +64 -43
- data/lib/active_support/notifications/instrumenter.rb +1 -1
- data/lib/active_support/notifications.rb +2 -2
- data/lib/active_support/number_helper/number_converter.rb +4 -1
- data/lib/active_support/number_helper/number_to_currency_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -4
- data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -7
- data/lib/active_support/number_helper/rounding_helper.rb +1 -1
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/ordered_options.rb +2 -2
- data/lib/active_support/railtie.rb +32 -9
- data/lib/active_support/security_utils.rb +4 -5
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +0 -5
- data/lib/active_support/syntax_error_proxy.rb +9 -1
- data/lib/active_support/tagged_logging.rb +5 -0
- data/lib/active_support/test_case.rb +67 -6
- data/lib/active_support/testing/assertions.rb +118 -27
- data/lib/active_support/testing/autorun.rb +5 -0
- data/lib/active_support/testing/error_reporter_assertions.rb +17 -0
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/isolation.rb +0 -2
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/worker.rb +7 -1
- data/lib/active_support/testing/parallelization.rb +17 -0
- data/lib/active_support/testing/tests_without_assertions.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +9 -4
- data/lib/active_support/time_with_zone.rb +36 -23
- data/lib/active_support/values/time_zone.rb +22 -13
- data/lib/active_support/xml_mini.rb +1 -2
- data/lib/active_support.rb +21 -9
- metadata +33 -17
- data/lib/active_support/core_ext/range/each.rb +0 -24
- data/lib/active_support/proxy_object.rb +0 -20
data/lib/active_support/cache.rb
CHANGED
|
@@ -36,6 +36,7 @@ module ActiveSupport
|
|
|
36
36
|
:serializer,
|
|
37
37
|
:skip_nil,
|
|
38
38
|
:raw,
|
|
39
|
+
:max_key_size,
|
|
39
40
|
]
|
|
40
41
|
|
|
41
42
|
# Mapping of canonical option names to aliases that a store will recognize.
|
|
@@ -187,6 +188,12 @@ module ActiveSupport
|
|
|
187
188
|
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
|
|
188
189
|
#
|
|
189
190
|
class Store
|
|
191
|
+
# Default +ConnectionPool+ options
|
|
192
|
+
DEFAULT_POOL_OPTIONS = { size: 5, timeout: 5 }.freeze
|
|
193
|
+
|
|
194
|
+
# Keys are truncated with the Active Support digest if they exceed the limit.
|
|
195
|
+
MAX_KEY_SIZE = 250
|
|
196
|
+
|
|
190
197
|
cattr_accessor :logger, instance_writer: true
|
|
191
198
|
cattr_accessor :raise_on_invalid_cache_expiration_time, default: false
|
|
192
199
|
|
|
@@ -195,9 +202,6 @@ module ActiveSupport
|
|
|
195
202
|
|
|
196
203
|
class << self
|
|
197
204
|
private
|
|
198
|
-
DEFAULT_POOL_OPTIONS = { size: 5, timeout: 5 }.freeze
|
|
199
|
-
private_constant :DEFAULT_POOL_OPTIONS
|
|
200
|
-
|
|
201
205
|
def retrieve_pool_options(options)
|
|
202
206
|
if options.key?(:pool)
|
|
203
207
|
pool_options = options.delete(:pool)
|
|
@@ -287,7 +291,7 @@ module ActiveSupport
|
|
|
287
291
|
# <tt>coder: nil</tt> to avoid the overhead of safeguarding against
|
|
288
292
|
# mutation.
|
|
289
293
|
#
|
|
290
|
-
# The +:coder+ option is
|
|
294
|
+
# The +:coder+ option is mutually exclusive with the +:serializer+ and
|
|
291
295
|
# +:compressor+ options. Specifying them together will raise an
|
|
292
296
|
# +ArgumentError+.
|
|
293
297
|
#
|
|
@@ -299,6 +303,9 @@ module ActiveSupport
|
|
|
299
303
|
@options[:compress] = true unless @options.key?(:compress)
|
|
300
304
|
@options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT
|
|
301
305
|
|
|
306
|
+
@max_key_size = @options.delete(:max_key_size)
|
|
307
|
+
@max_key_size = MAX_KEY_SIZE if @max_key_size.nil? # allow 'false' as a value
|
|
308
|
+
|
|
302
309
|
@coder = @options.delete(:coder) do
|
|
303
310
|
legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer]
|
|
304
311
|
serializer = @options.delete(:serializer) || default_serializer
|
|
@@ -420,7 +427,7 @@ module ActiveSupport
|
|
|
420
427
|
# t1.join
|
|
421
428
|
#
|
|
422
429
|
# p val_1 # => "new value 1"
|
|
423
|
-
# p val_2 # => "
|
|
430
|
+
# p val_2 # => "original value"
|
|
424
431
|
# p cache.fetch("foo") # => "new value 1"
|
|
425
432
|
#
|
|
426
433
|
# # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
|
|
@@ -539,10 +546,11 @@ module ActiveSupport
|
|
|
539
546
|
|
|
540
547
|
options = names.extract_options!
|
|
541
548
|
options = merged_options(options)
|
|
549
|
+
keys = names.map { |name| normalize_key(name, options) }
|
|
542
550
|
|
|
543
|
-
instrument_multi :read_multi,
|
|
551
|
+
instrument_multi :read_multi, keys, options do |payload|
|
|
544
552
|
read_multi_entries(names, **options, event: payload).tap do |results|
|
|
545
|
-
payload[:hits] = results.keys
|
|
553
|
+
payload[:hits] = results.keys.map { |name| normalize_key(name, options) }
|
|
546
554
|
end
|
|
547
555
|
end
|
|
548
556
|
end
|
|
@@ -552,10 +560,11 @@ module ActiveSupport
|
|
|
552
560
|
return hash if hash.empty?
|
|
553
561
|
|
|
554
562
|
options = merged_options(options)
|
|
563
|
+
normalized_hash = hash.transform_keys { |key| normalize_key(key, options) }
|
|
555
564
|
|
|
556
|
-
instrument_multi :write_multi,
|
|
565
|
+
instrument_multi :write_multi, normalized_hash, options do |payload|
|
|
557
566
|
entries = hash.each_with_object({}) do |(name, value), memo|
|
|
558
|
-
memo[normalize_key(name, options)] = Entry.new(value, **options
|
|
567
|
+
memo[normalize_key(name, options)] = Entry.new(value, **options, version: normalize_version(name, options))
|
|
559
568
|
end
|
|
560
569
|
|
|
561
570
|
write_multi_entries entries, **options
|
|
@@ -597,9 +606,9 @@ module ActiveSupport
|
|
|
597
606
|
|
|
598
607
|
options = names.extract_options!
|
|
599
608
|
options = merged_options(options)
|
|
600
|
-
|
|
609
|
+
keys = names.map { |name| normalize_key(name, options) }
|
|
601
610
|
writes = {}
|
|
602
|
-
ordered = instrument_multi :read_multi,
|
|
611
|
+
ordered = instrument_multi :read_multi, keys, options do |payload|
|
|
603
612
|
if options[:force]
|
|
604
613
|
reads = {}
|
|
605
614
|
else
|
|
@@ -611,7 +620,7 @@ module ActiveSupport
|
|
|
611
620
|
end
|
|
612
621
|
writes.compact! if options[:skip_nil]
|
|
613
622
|
|
|
614
|
-
payload[:hits] = reads.keys
|
|
623
|
+
payload[:hits] = reads.keys.map { |name| normalize_key(name, options) }
|
|
615
624
|
payload[:super_operation] = :fetch_multi
|
|
616
625
|
|
|
617
626
|
ordered
|
|
@@ -657,13 +666,15 @@ module ActiveSupport
|
|
|
657
666
|
# version, the read will be treated as a cache miss. This feature is
|
|
658
667
|
# used to support recyclable cache keys.
|
|
659
668
|
#
|
|
669
|
+
# * +:unless_exist+ - Prevents overwriting an existing cache entry.
|
|
670
|
+
#
|
|
660
671
|
# Other options will be handled by the specific cache store implementation.
|
|
661
672
|
def write(name, value, options = nil)
|
|
662
673
|
options = merged_options(options)
|
|
663
674
|
key = normalize_key(name, options)
|
|
664
675
|
|
|
665
676
|
instrument(:write, key, options) do
|
|
666
|
-
entry = Entry.new(value, **options
|
|
677
|
+
entry = Entry.new(value, **options, version: normalize_version(name, options))
|
|
667
678
|
write_entry(key, entry, **options)
|
|
668
679
|
end
|
|
669
680
|
end
|
|
@@ -689,7 +700,7 @@ module ActiveSupport
|
|
|
689
700
|
return 0 if names.empty?
|
|
690
701
|
|
|
691
702
|
options = merged_options(options)
|
|
692
|
-
names.map
|
|
703
|
+
names = names.map { |key| normalize_key(key, options) }
|
|
693
704
|
|
|
694
705
|
instrument_multi(:delete_multi, names, options) do
|
|
695
706
|
delete_multi_entries(names, **options)
|
|
@@ -740,6 +751,32 @@ module ActiveSupport
|
|
|
740
751
|
raise NotImplementedError.new("#{self.class.name} does not support decrement")
|
|
741
752
|
end
|
|
742
753
|
|
|
754
|
+
# Reads a counter that was set by #increment / #decrement.
|
|
755
|
+
#
|
|
756
|
+
# cache.write_counter("foo", 1)
|
|
757
|
+
# cache.read_counter("foo") # => 1
|
|
758
|
+
# cache.increment("foo")
|
|
759
|
+
# cache.read_counter("foo") # => 2
|
|
760
|
+
#
|
|
761
|
+
# Options are passed to the underlying cache implementation.
|
|
762
|
+
def read_counter(name, **options)
|
|
763
|
+
options = merged_options(options).merge(raw: true)
|
|
764
|
+
read(name, **options)&.to_i
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# Writes a counter that can then be modified by #increment / #decrement.
|
|
768
|
+
#
|
|
769
|
+
# cache.write_counter("foo", 1)
|
|
770
|
+
# cache.read_counter("foo") # => 1
|
|
771
|
+
# cache.increment("foo")
|
|
772
|
+
# cache.read_counter("foo") # => 2
|
|
773
|
+
#
|
|
774
|
+
# Options are passed to the underlying cache implementation.
|
|
775
|
+
def write_counter(name, value, **options)
|
|
776
|
+
options = merged_options(options).merge(raw: true)
|
|
777
|
+
write(name, value.to_i, **options)
|
|
778
|
+
end
|
|
779
|
+
|
|
743
780
|
# Cleans up the cache by removing expired entries.
|
|
744
781
|
#
|
|
745
782
|
# Options are passed to the underlying cache implementation.
|
|
@@ -759,6 +796,17 @@ module ActiveSupport
|
|
|
759
796
|
raise NotImplementedError.new("#{self.class.name} does not support clear")
|
|
760
797
|
end
|
|
761
798
|
|
|
799
|
+
# Get the current namespace
|
|
800
|
+
def namespace
|
|
801
|
+
@options[:namespace]
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
# Set the current namespace. Note, this will be ignored if custom
|
|
805
|
+
# options are passed to cache wills with a namespace key.
|
|
806
|
+
def namespace=(namespace)
|
|
807
|
+
@options[:namespace] = namespace
|
|
808
|
+
end
|
|
809
|
+
|
|
762
810
|
private
|
|
763
811
|
def default_serializer
|
|
764
812
|
case Cache.format_version
|
|
@@ -925,16 +973,33 @@ module ActiveSupport
|
|
|
925
973
|
options
|
|
926
974
|
end
|
|
927
975
|
|
|
928
|
-
# Expands and
|
|
976
|
+
# Expands, namespaces and truncates the cache key.
|
|
929
977
|
# Raises an exception when the key is +nil+ or an empty string.
|
|
930
978
|
# May be overridden by cache stores to do additional normalization.
|
|
931
979
|
def normalize_key(key, options = nil)
|
|
980
|
+
key = expand_and_namespace_key(key, options)
|
|
981
|
+
truncate_key(key)
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
def expand_and_namespace_key(key, options = nil)
|
|
932
985
|
str_key = expanded_key(key)
|
|
933
986
|
raise(ArgumentError, "key cannot be blank") if !str_key || str_key.empty?
|
|
934
987
|
|
|
935
988
|
namespace_key str_key, options
|
|
936
989
|
end
|
|
937
990
|
|
|
991
|
+
def truncate_key(key)
|
|
992
|
+
if key && @max_key_size && key.bytesize > @max_key_size
|
|
993
|
+
suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}"
|
|
994
|
+
truncate_at = @max_key_size - suffix.bytesize
|
|
995
|
+
key = key.byteslice(0, truncate_at)
|
|
996
|
+
key.scrub!("")
|
|
997
|
+
"#{key}#{suffix}"
|
|
998
|
+
else
|
|
999
|
+
key
|
|
1000
|
+
end
|
|
1001
|
+
end
|
|
1002
|
+
|
|
938
1003
|
# Prefix the key with a namespace string:
|
|
939
1004
|
#
|
|
940
1005
|
# namespace_key 'foo', namespace: 'cache'
|
|
@@ -944,9 +1009,12 @@ module ActiveSupport
|
|
|
944
1009
|
#
|
|
945
1010
|
# namespace_key 'foo', namespace: -> { 'cache' }
|
|
946
1011
|
# # => 'cache:foo'
|
|
947
|
-
def namespace_key(key,
|
|
948
|
-
|
|
949
|
-
|
|
1012
|
+
def namespace_key(key, call_options = nil)
|
|
1013
|
+
namespace = if call_options&.key?(:namespace)
|
|
1014
|
+
call_options[:namespace]
|
|
1015
|
+
else
|
|
1016
|
+
options[:namespace]
|
|
1017
|
+
end
|
|
950
1018
|
|
|
951
1019
|
if namespace.respond_to?(:call)
|
|
952
1020
|
namespace = namespace.call
|
|
@@ -4,9 +4,9 @@ require "active_support/concern"
|
|
|
4
4
|
require "active_support/descendants_tracker"
|
|
5
5
|
require "active_support/core_ext/array/extract_options"
|
|
6
6
|
require "active_support/core_ext/class/attribute"
|
|
7
|
+
require "active_support/core_ext/module/redefine_method"
|
|
7
8
|
require "active_support/core_ext/string/filters"
|
|
8
9
|
require "active_support/core_ext/object/blank"
|
|
9
|
-
require "thread"
|
|
10
10
|
|
|
11
11
|
module ActiveSupport
|
|
12
12
|
# = Active Support \Callbacks
|
|
@@ -67,7 +67,7 @@ module ActiveSupport
|
|
|
67
67
|
|
|
68
68
|
included do
|
|
69
69
|
extend ActiveSupport::DescendantsTracker
|
|
70
|
-
class_attribute :__callbacks, instance_writer: false, default: {}
|
|
70
|
+
class_attribute :__callbacks, instance_writer: false, instance_predicate: false, default: {}
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
CALLBACK_FILTER_TYPES = [:before, :after, :around].freeze
|
|
@@ -574,7 +574,7 @@ module ActiveSupport
|
|
|
574
574
|
@name = name
|
|
575
575
|
@config = {
|
|
576
576
|
scope: [:kind],
|
|
577
|
-
terminator:
|
|
577
|
+
terminator: DEFAULT_TERMINATOR
|
|
578
578
|
}.merge!(config)
|
|
579
579
|
@chain = []
|
|
580
580
|
@all_callbacks = nil
|
|
@@ -662,8 +662,8 @@ module ActiveSupport
|
|
|
662
662
|
@chain.delete_if { |c| callback.duplicates?(c) }
|
|
663
663
|
end
|
|
664
664
|
|
|
665
|
-
|
|
666
|
-
|
|
665
|
+
class DefaultTerminator # :nodoc:
|
|
666
|
+
def call(target, result_lambda)
|
|
667
667
|
terminate = true
|
|
668
668
|
catch(:abort) do
|
|
669
669
|
result_lambda.call
|
|
@@ -672,6 +672,7 @@ module ActiveSupport
|
|
|
672
672
|
terminate
|
|
673
673
|
end
|
|
674
674
|
end
|
|
675
|
+
DEFAULT_TERMINATOR = DefaultTerminator.new.freeze
|
|
675
676
|
end
|
|
676
677
|
|
|
677
678
|
module ClassMethods
|
|
@@ -905,12 +906,13 @@ module ActiveSupport
|
|
|
905
906
|
names.each do |name|
|
|
906
907
|
name = name.to_sym
|
|
907
908
|
|
|
908
|
-
([self] + self.descendants).each do |target|
|
|
909
|
-
target.set_callbacks name, CallbackChain.new(name, options)
|
|
910
|
-
end
|
|
911
|
-
|
|
912
909
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
913
|
-
def _run_#{name}_callbacks
|
|
910
|
+
def _run_#{name}_callbacks
|
|
911
|
+
yield if block_given?
|
|
912
|
+
end
|
|
913
|
+
silence_redefinition_of_method(:_run_#{name}_callbacks)
|
|
914
|
+
|
|
915
|
+
def _run_#{name}_callbacks!(&block)
|
|
914
916
|
run_callbacks #{name.inspect}, &block
|
|
915
917
|
end
|
|
916
918
|
|
|
@@ -926,6 +928,10 @@ module ActiveSupport
|
|
|
926
928
|
__callbacks[#{name.inspect}]
|
|
927
929
|
end
|
|
928
930
|
RUBY
|
|
931
|
+
|
|
932
|
+
([self] + self.descendants).each do |target|
|
|
933
|
+
target.set_callbacks name, CallbackChain.new(name, options)
|
|
934
|
+
end
|
|
929
935
|
end
|
|
930
936
|
end
|
|
931
937
|
|
|
@@ -935,9 +941,17 @@ module ActiveSupport
|
|
|
935
941
|
end
|
|
936
942
|
|
|
937
943
|
def set_callbacks(name, callbacks) # :nodoc:
|
|
938
|
-
|
|
944
|
+
# HACK: We're making assumption on how `class_attribute` is implemented
|
|
945
|
+
# to save constantly duping the callback hash. If this desync with class_attribute
|
|
946
|
+
# we'll lose the optimization, but won't cause an actual behavior bug.
|
|
947
|
+
unless singleton_class.private_method_defined?(:__class_attr__callbacks, false)
|
|
939
948
|
self.__callbacks = __callbacks.dup
|
|
940
949
|
end
|
|
950
|
+
name = name.to_sym
|
|
951
|
+
callbacks_was = self.__callbacks[name.to_sym]
|
|
952
|
+
if (callbacks_was.nil? || callbacks_was.empty?) && !callbacks.empty?
|
|
953
|
+
alias_method("_run_#{name}_callbacks", "_run_#{name}_callbacks!")
|
|
954
|
+
end
|
|
941
955
|
self.__callbacks[name.to_sym] = callbacks
|
|
942
956
|
self.__callbacks
|
|
943
957
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module ClassAttribute # :nodoc:
|
|
5
|
+
class << self
|
|
6
|
+
def redefine(owner, name, namespaced_name, value)
|
|
7
|
+
if owner.singleton_class?
|
|
8
|
+
if owner.attached_object.is_a?(Module)
|
|
9
|
+
redefine_method(owner, namespaced_name, private: true) { value }
|
|
10
|
+
else
|
|
11
|
+
redefine_method(owner, name) { value }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
redefine_method(owner.singleton_class, namespaced_name, private: true) { value }
|
|
16
|
+
|
|
17
|
+
redefine_method(owner.singleton_class, "#{namespaced_name}=", private: true) do |new_value|
|
|
18
|
+
if owner.equal?(self)
|
|
19
|
+
value = new_value
|
|
20
|
+
else
|
|
21
|
+
::ActiveSupport::ClassAttribute.redefine(self, name, namespaced_name, new_value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def redefine_method(owner, name, private: false, &block)
|
|
27
|
+
owner.silence_redefinition_of_method(name)
|
|
28
|
+
owner.define_method(name, &block)
|
|
29
|
+
owner.send(:private, name) if private
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -55,6 +55,11 @@ module ActiveSupport
|
|
|
55
55
|
@path = path
|
|
56
56
|
@line = line
|
|
57
57
|
@namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
|
|
58
|
+
@sources = []
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def class_eval
|
|
62
|
+
yield @sources
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
def define_cached_method(canonical_name, namespace:, as: nil, &block)
|
|
@@ -65,6 +70,10 @@ module ActiveSupport
|
|
|
65
70
|
@namespaces.each_value do |method_set|
|
|
66
71
|
method_set.apply(@owner, @path, @line - 1)
|
|
67
72
|
end
|
|
73
|
+
|
|
74
|
+
unless @sources.empty?
|
|
75
|
+
@owner.class_eval("# frozen_string_literal: true\n" + @sources.join(";"), @path, @line - 1)
|
|
76
|
+
end
|
|
68
77
|
end
|
|
69
78
|
end
|
|
70
79
|
end
|
|
@@ -4,69 +4,15 @@ require "monitor"
|
|
|
4
4
|
|
|
5
5
|
module ActiveSupport
|
|
6
6
|
module Concurrency
|
|
7
|
-
module LoadInterlockAwareMonitorMixin # :nodoc:
|
|
8
|
-
EXCEPTION_NEVER = { Exception => :never }.freeze
|
|
9
|
-
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
|
|
10
|
-
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
|
|
11
|
-
|
|
12
|
-
# Enters an exclusive section, but allows dependency loading while blocked
|
|
13
|
-
def mon_enter
|
|
14
|
-
mon_try_enter ||
|
|
15
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def synchronize(&block)
|
|
19
|
-
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
|
20
|
-
mon_enter
|
|
21
|
-
|
|
22
|
-
begin
|
|
23
|
-
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
|
24
|
-
ensure
|
|
25
|
-
mon_exit
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
7
|
# A monitor that will permit dependency loading while blocked waiting for
|
|
31
8
|
# the lock.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@owner = nil
|
|
41
|
-
@count = 0
|
|
42
|
-
@mutex = Mutex.new
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
def mon_try_enter
|
|
47
|
-
if @owner != Thread.current
|
|
48
|
-
return false unless @mutex.try_lock
|
|
49
|
-
@owner = Thread.current
|
|
50
|
-
end
|
|
51
|
-
@count += 1
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def mon_enter
|
|
55
|
-
@mutex.lock if @owner != Thread.current
|
|
56
|
-
@owner = Thread.current
|
|
57
|
-
@count += 1
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def mon_exit
|
|
61
|
-
unless @owner == Thread.current
|
|
62
|
-
raise ThreadError, "current thread not owner"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
@count -= 1
|
|
66
|
-
return unless @count == 0
|
|
67
|
-
@owner = nil
|
|
68
|
-
@mutex.unlock
|
|
69
|
-
end
|
|
70
|
-
end
|
|
9
|
+
LoadInterlockAwareMonitor = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
|
|
10
|
+
"ActiveSupport::Concurrency::LoadInterlockAwareMonitor",
|
|
11
|
+
"::Monitor",
|
|
12
|
+
ActiveSupport.deprecator,
|
|
13
|
+
message: "ActiveSupport::Concurrency::LoadInterlockAwareMonitor is deprecated and will be " \
|
|
14
|
+
"removed in Rails 9.0. Use Monitor directly instead, as the loading interlock is " \
|
|
15
|
+
"no longer used."
|
|
16
|
+
)
|
|
71
17
|
end
|
|
72
18
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Concurrency
|
|
5
|
+
class ThreadMonitor # :nodoc:
|
|
6
|
+
EXCEPTION_NEVER = { Exception => :never }.freeze
|
|
7
|
+
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
|
|
8
|
+
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@owner = nil
|
|
12
|
+
@count = 0
|
|
13
|
+
@mutex = Mutex.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def synchronize(&block)
|
|
17
|
+
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
|
18
|
+
mon_enter
|
|
19
|
+
|
|
20
|
+
begin
|
|
21
|
+
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
|
22
|
+
ensure
|
|
23
|
+
mon_exit
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def mon_try_enter
|
|
30
|
+
if @owner != Thread.current
|
|
31
|
+
return false unless @mutex.try_lock
|
|
32
|
+
@owner = Thread.current
|
|
33
|
+
end
|
|
34
|
+
@count += 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def mon_enter
|
|
38
|
+
@mutex.lock if @owner != Thread.current
|
|
39
|
+
@owner = Thread.current
|
|
40
|
+
@count += 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def mon_exit
|
|
44
|
+
unless @owner == Thread.current
|
|
45
|
+
raise ThreadError, "current thread not owner"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
@count -= 1
|
|
49
|
+
return unless @count == 0
|
|
50
|
+
@owner = nil
|
|
51
|
+
@mutex.unlock
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
ActiveSupport.deprecator.warn <<~MSG
|
|
4
|
+
ActiveSupport::Configurable is deprecated without replacement, and will be removed in Rails 8.2.
|
|
5
|
+
|
|
6
|
+
The previous behavior can be emulated with `attr_accessor`s, `class_attribute`s, or combinations of `class_attribute` and `InheritableOptions` depending on requirements.
|
|
7
|
+
MSG
|
|
8
|
+
|
|
3
9
|
require "active_support/concern"
|
|
4
10
|
require "active_support/ordered_options"
|
|
5
11
|
|
|
@@ -27,6 +33,19 @@ module ActiveSupport
|
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
module ClassMethods
|
|
36
|
+
# Reads and writes attributes from a configuration OrderedOptions.
|
|
37
|
+
#
|
|
38
|
+
# require "active_support/configurable"
|
|
39
|
+
#
|
|
40
|
+
# class User
|
|
41
|
+
# include ActiveSupport::Configurable
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# User.config.allowed_access = true
|
|
45
|
+
# User.config.level = 1
|
|
46
|
+
#
|
|
47
|
+
# User.config.allowed_access # => true
|
|
48
|
+
# User.config.level # => 1
|
|
30
49
|
def config
|
|
31
50
|
@_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
|
|
32
51
|
superclass.config.inheritable_copy
|
|
@@ -36,6 +55,21 @@ module ActiveSupport
|
|
|
36
55
|
end
|
|
37
56
|
end
|
|
38
57
|
|
|
58
|
+
# Configure values from within the passed block.
|
|
59
|
+
#
|
|
60
|
+
# require "active_support/configurable"
|
|
61
|
+
#
|
|
62
|
+
# class User
|
|
63
|
+
# include ActiveSupport::Configurable
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# User.config.allowed_access # => nil
|
|
67
|
+
#
|
|
68
|
+
# User.configure do |config|
|
|
69
|
+
# config.allowed_access = true
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# User.config.allowed_access # => true
|
|
39
73
|
def configure
|
|
40
74
|
yield config
|
|
41
75
|
end
|
|
@@ -19,11 +19,20 @@ module ActiveSupport
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def parse(context: nil, **options)
|
|
22
|
-
source = render(context)
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
source = @content.include?("<%") ? render(context) : @content
|
|
23
|
+
|
|
24
|
+
if source == @content
|
|
25
|
+
if YAML.respond_to?(:unsafe_load)
|
|
26
|
+
YAML.unsafe_load_file(@content_path, **options) || {}
|
|
27
|
+
else
|
|
28
|
+
YAML.load_file(@content_path, **options) || {}
|
|
29
|
+
end
|
|
25
30
|
else
|
|
26
|
-
YAML.
|
|
31
|
+
if YAML.respond_to?(:unsafe_load)
|
|
32
|
+
YAML.unsafe_load(source, **options) || {}
|
|
33
|
+
else
|
|
34
|
+
YAML.load(source, **options) || {}
|
|
35
|
+
end
|
|
27
36
|
end
|
|
28
37
|
rescue Psych::SyntaxError => error
|
|
29
38
|
raise "YAML syntax error occurred while parsing #{@content_path}. " \
|
|
@@ -33,8 +42,7 @@ module ActiveSupport
|
|
|
33
42
|
|
|
34
43
|
private
|
|
35
44
|
def read(content_path)
|
|
36
|
-
require "yaml"
|
|
37
|
-
require "erb"
|
|
45
|
+
require "yaml" unless defined?(YAML)
|
|
38
46
|
|
|
39
47
|
File.read(content_path).tap do |content|
|
|
40
48
|
if content.include?("\u00A0")
|
|
@@ -44,6 +52,7 @@ module ActiveSupport
|
|
|
44
52
|
end
|
|
45
53
|
|
|
46
54
|
def render(context)
|
|
55
|
+
require "erb" unless defined?(ERB)
|
|
47
56
|
erb = ERB.new(@content).tap { |e| e.filename = @content_path }
|
|
48
57
|
context ? erb.result(context) : erb.result
|
|
49
58
|
end
|