activesupport 7.1.6 → 8.1.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 +256 -1133
- data/README.rdoc +1 -1
- data/lib/active_support/array_inquirer.rb +1 -1
- data/lib/active_support/backtrace_cleaner.rb +81 -3
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +65 -78
- data/lib/active_support/cache/file_store.rb +29 -14
- data/lib/active_support/cache/mem_cache_store.rb +42 -102
- data/lib/active_support/cache/memory_store.rb +11 -6
- data/lib/active_support/cache/null_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +58 -46
- data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
- data/lib/active_support/cache/strategy/local_cache.rb +72 -27
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +146 -86
- data/lib/active_support/callbacks.rb +102 -126
- 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 -5
- data/lib/active_support/core_ext/array.rb +7 -7
- data/lib/active_support/core_ext/benchmark.rb +4 -14
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +26 -19
- data/lib/active_support/core_ext/class/subclasses.rb +15 -35
- data/lib/active_support/core_ext/class.rb +2 -2
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +2 -2
- data/lib/active_support/core_ext/date.rb +5 -5
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -9
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -6
- data/lib/active_support/core_ext/date_time.rb +5 -5
- data/lib/active_support/core_ext/digest/uuid.rb +6 -0
- data/lib/active_support/core_ext/digest.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +25 -8
- data/lib/active_support/core_ext/erb/util.rb +10 -5
- 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/keys.rb +4 -4
- 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 +16 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -163
- data/lib/active_support/core_ext/module/deprecation.rb +1 -4
- data/lib/active_support/core_ext/module/introspection.rb +3 -0
- data/lib/active_support/core_ext/module.rb +11 -11
- data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
- data/lib/active_support/core_ext/numeric.rb +3 -3
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
- 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/try.rb +2 -2
- data/lib/active_support/core_ext/object/with.rb +5 -3
- data/lib/active_support/core_ext/object.rb +13 -13
- data/lib/active_support/core_ext/pathname/blank.rb +4 -0
- data/lib/active_support/core_ext/pathname.rb +2 -2
- data/lib/active_support/core_ext/range/overlap.rb +4 -4
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +4 -4
- data/lib/active_support/core_ext/securerandom.rb +4 -4
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +4 -4
- data/lib/active_support/core_ext/string/multibyte.rb +13 -4
- data/lib/active_support/core_ext/string/output_safety.rb +19 -19
- 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 +25 -30
- data/lib/active_support/core_ext/time/compatibility.rb +2 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -2
- data/lib/active_support/core_ext/time/zones.rb +1 -1
- data/lib/active_support/core_ext/time.rb +5 -5
- data/lib/active_support/core_ext.rb +1 -2
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +58 -50
- data/lib/active_support/delegation.rb +200 -0
- data/lib/active_support/dependencies/autoload.rb +0 -12
- data/lib/active_support/dependencies/interlock.rb +11 -5
- data/lib/active_support/dependencies.rb +6 -2
- data/lib/active_support/deprecation/constant_accessor.rb +47 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
- data/lib/active_support/deprecation/reporting.rb +5 -17
- data/lib/active_support/deprecation.rb +8 -5
- data/lib/active_support/descendants_tracker.rb +9 -87
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -2
- data/lib/active_support/duration.rb +25 -16
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/encrypted_file.rb +1 -1
- data/lib/active_support/error_reporter.rb +121 -6
- 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 -3
- data/lib/active_support/execution_context.rb +64 -7
- data/lib/active_support/execution_wrapper.rb +1 -2
- data/lib/active_support/file_update_checker.rb +9 -7
- data/lib/active_support/fork_tracker.rb +2 -38
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +66 -45
- data/lib/active_support/html_safe_translation.rb +3 -0
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/inflector/inflections.rb +31 -15
- data/lib/active_support/inflector/transliterate.rb +6 -8
- data/lib/active_support/isolated_execution_state.rb +12 -17
- data/lib/active_support/json/decoding.rb +6 -4
- data/lib/active_support/json/encoding.rb +157 -21
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber.rb +2 -18
- data/lib/active_support/logger.rb +15 -2
- data/lib/active_support/logger_thread_safe_level.rb +4 -9
- data/lib/active_support/message_encryptors.rb +54 -2
- data/lib/active_support/message_pack/extensions.rb +20 -2
- data/lib/active_support/message_verifier.rb +21 -0
- data/lib/active_support/message_verifiers.rb +57 -3
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +10 -0
- data/lib/active_support/multibyte/chars.rb +14 -4
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/notifications/fanout.rb +68 -50
- data/lib/active_support/notifications/instrumenter.rb +22 -19
- data/lib/active_support/notifications.rb +28 -27
- data/lib/active_support/number_helper/number_converter.rb +2 -2
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_options.rb +53 -15
- data/lib/active_support/railtie.rb +36 -20
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +1 -5
- data/lib/active_support/syntax_error_proxy.rb +3 -0
- data/lib/active_support/tagged_logging.rb +5 -1
- data/lib/active_support/test_case.rb +63 -6
- data/lib/active_support/testing/assertions.rb +113 -27
- data/lib/active_support/testing/constant_stubbing.rb +30 -8
- data/lib/active_support/testing/deprecation.rb +5 -12
- data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/isolation.rb +19 -9
- data/lib/active_support/testing/method_call_assertions.rb +2 -16
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +18 -2
- data/lib/active_support/testing/parallelization/worker.rb +4 -2
- data/lib/active_support/testing/parallelization.rb +25 -1
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +11 -6
- data/lib/active_support/time_with_zone.rb +39 -26
- data/lib/active_support/values/time_zone.rb +26 -17
- data/lib/active_support/xml_mini.rb +14 -4
- data/lib/active_support.rb +22 -9
- metadata +31 -17
- data/lib/active_support/core_ext/range/each.rb +0 -24
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- data/lib/active_support/proxy_object.rb +0 -17
- data/lib/active_support/ruby_features.rb +0 -7
- data/lib/active_support/testing/strict_warnings.rb +0 -39
|
@@ -2,8 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveSupport
|
|
4
4
|
module ExecutionContext # :nodoc:
|
|
5
|
+
class Record
|
|
6
|
+
attr_reader :store, :current_attributes_instances
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@store = {}
|
|
10
|
+
@current_attributes_instances = {}
|
|
11
|
+
@stack = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def push
|
|
15
|
+
@stack << @store << @current_attributes_instances
|
|
16
|
+
@store = {}
|
|
17
|
+
@current_attributes_instances = {}
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def pop
|
|
22
|
+
@current_attributes_instances = @stack.pop
|
|
23
|
+
@store = @stack.pop
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
5
28
|
@after_change_callbacks = []
|
|
29
|
+
|
|
30
|
+
# Execution context nesting should only legitimately happen during test
|
|
31
|
+
# because the test case itself is wrapped in an executor, and it might call
|
|
32
|
+
# into a controller or job which should be executed with their own fresh context.
|
|
33
|
+
# However in production this should never happen, and for extra safety we make sure to
|
|
34
|
+
# fully clear the state at the end of the request or job cycle.
|
|
35
|
+
@nestable = false
|
|
36
|
+
|
|
6
37
|
class << self
|
|
38
|
+
attr_accessor :nestable
|
|
39
|
+
|
|
7
40
|
def after_change(&block)
|
|
8
41
|
@after_change_callbacks << block
|
|
9
42
|
end
|
|
@@ -14,9 +47,11 @@ module ActiveSupport
|
|
|
14
47
|
options.symbolize_keys!
|
|
15
48
|
keys = options.keys
|
|
16
49
|
|
|
17
|
-
store =
|
|
50
|
+
store = record.store
|
|
18
51
|
|
|
19
|
-
previous_context =
|
|
52
|
+
previous_context = if block_given?
|
|
53
|
+
keys.zip(store.values_at(*keys)).to_h
|
|
54
|
+
end
|
|
20
55
|
|
|
21
56
|
store.merge!(options)
|
|
22
57
|
@after_change_callbacks.each(&:call)
|
|
@@ -32,21 +67,43 @@ module ActiveSupport
|
|
|
32
67
|
end
|
|
33
68
|
|
|
34
69
|
def []=(key, value)
|
|
35
|
-
store[key.to_sym] = value
|
|
70
|
+
record.store[key.to_sym] = value
|
|
36
71
|
@after_change_callbacks.each(&:call)
|
|
37
72
|
end
|
|
38
73
|
|
|
39
74
|
def to_h
|
|
40
|
-
store.dup
|
|
75
|
+
record.store.dup
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def push
|
|
79
|
+
if @nestable
|
|
80
|
+
record.push
|
|
81
|
+
else
|
|
82
|
+
clear
|
|
83
|
+
end
|
|
84
|
+
self
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def pop
|
|
88
|
+
if @nestable
|
|
89
|
+
record.pop
|
|
90
|
+
else
|
|
91
|
+
clear
|
|
92
|
+
end
|
|
93
|
+
self
|
|
41
94
|
end
|
|
42
95
|
|
|
43
96
|
def clear
|
|
44
|
-
|
|
97
|
+
IsolatedExecutionState[:active_support_execution_context] = nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def current_attributes_instances
|
|
101
|
+
record.current_attributes_instances
|
|
45
102
|
end
|
|
46
103
|
|
|
47
104
|
private
|
|
48
|
-
def
|
|
49
|
-
IsolatedExecutionState[:active_support_execution_context] ||=
|
|
105
|
+
def record
|
|
106
|
+
IsolatedExecutionState[:active_support_execution_context] ||= Record.new
|
|
50
107
|
end
|
|
51
108
|
end
|
|
52
109
|
end
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_support/error_reporter"
|
|
4
4
|
require "active_support/callbacks"
|
|
5
|
-
require "concurrent/hash"
|
|
6
5
|
|
|
7
6
|
module ActiveSupport
|
|
8
7
|
class ExecutionWrapper
|
|
@@ -90,7 +89,7 @@ module ActiveSupport
|
|
|
90
89
|
instance = run!
|
|
91
90
|
begin
|
|
92
91
|
yield
|
|
93
|
-
rescue => error
|
|
92
|
+
rescue Exception => error
|
|
94
93
|
error_reporter&.report(error, handled: false, source: source)
|
|
95
94
|
raise
|
|
96
95
|
ensure
|
|
@@ -46,8 +46,11 @@ module ActiveSupport
|
|
|
46
46
|
raise ArgumentError, "A block is required to initialize a FileUpdateChecker"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
@
|
|
49
|
+
gem_paths = Gem.path
|
|
50
|
+
@files = files.reject { |file| File.expand_path(file).start_with?(*gem_paths) }.freeze
|
|
51
|
+
|
|
52
|
+
@globs = compile_glob(dirs)&.reject { |dir| dir.start_with?(*gem_paths) }
|
|
53
|
+
|
|
51
54
|
@block = block
|
|
52
55
|
|
|
53
56
|
@watched = nil
|
|
@@ -103,8 +106,8 @@ module ActiveSupport
|
|
|
103
106
|
def watched
|
|
104
107
|
@watched || begin
|
|
105
108
|
all = @files.select { |f| File.exist?(f) }
|
|
106
|
-
all.concat(Dir[
|
|
107
|
-
all
|
|
109
|
+
all.concat(Dir[*@globs]) if @globs
|
|
110
|
+
all.tap(&:uniq!)
|
|
108
111
|
end
|
|
109
112
|
end
|
|
110
113
|
|
|
@@ -120,7 +123,7 @@ module ActiveSupport
|
|
|
120
123
|
# healthy to consider this edge case because with mtimes in the future
|
|
121
124
|
# reloading is not triggered.
|
|
122
125
|
def max_mtime(paths)
|
|
123
|
-
time_now = Time.
|
|
126
|
+
time_now = Time.at(0, Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond), :nanosecond)
|
|
124
127
|
max_mtime = nil
|
|
125
128
|
|
|
126
129
|
# Time comparisons are performed with #compare_without_coercion because
|
|
@@ -145,10 +148,9 @@ module ActiveSupport
|
|
|
145
148
|
hash.freeze # Freeze so changes aren't accidentally pushed
|
|
146
149
|
return if hash.empty?
|
|
147
150
|
|
|
148
|
-
|
|
151
|
+
hash.map do |key, value|
|
|
149
152
|
"#{escape(key)}/**/*#{compile_ext(value)}"
|
|
150
153
|
end
|
|
151
|
-
"{#{globs.join(",")}}"
|
|
152
154
|
end
|
|
153
155
|
|
|
154
156
|
def escape(key)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveSupport
|
|
4
4
|
module ForkTracker # :nodoc:
|
|
5
|
-
module
|
|
5
|
+
module CoreExt
|
|
6
6
|
def _fork
|
|
7
7
|
pid = super
|
|
8
8
|
if pid == 0
|
|
@@ -12,27 +12,6 @@ module ActiveSupport
|
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
module CoreExt
|
|
16
|
-
def fork(...)
|
|
17
|
-
if block_given?
|
|
18
|
-
super do
|
|
19
|
-
ForkTracker.check!
|
|
20
|
-
yield
|
|
21
|
-
end
|
|
22
|
-
else
|
|
23
|
-
unless pid = super
|
|
24
|
-
ForkTracker.check!
|
|
25
|
-
end
|
|
26
|
-
pid
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
module CoreExtPrivate
|
|
32
|
-
include CoreExt
|
|
33
|
-
private :fork
|
|
34
|
-
end
|
|
35
|
-
|
|
36
15
|
@pid = Process.pid
|
|
37
16
|
@callbacks = []
|
|
38
17
|
|
|
@@ -45,23 +24,8 @@ module ActiveSupport
|
|
|
45
24
|
end
|
|
46
25
|
end
|
|
47
26
|
|
|
48
|
-
if Process.respond_to?(:_fork) # Ruby 3.1+
|
|
49
|
-
def check!
|
|
50
|
-
# We trust the `_fork` callback
|
|
51
|
-
end
|
|
52
|
-
else
|
|
53
|
-
alias_method :check!, :after_fork_callback
|
|
54
|
-
end
|
|
55
|
-
|
|
56
27
|
def hook!
|
|
57
|
-
|
|
58
|
-
::Process.singleton_class.prepend(ModernCoreExt)
|
|
59
|
-
elsif Process.respond_to?(:fork)
|
|
60
|
-
::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
|
|
61
|
-
::Kernel.prepend(CoreExtPrivate)
|
|
62
|
-
::Kernel.singleton_class.prepend(CoreExt)
|
|
63
|
-
::Process.singleton_class.prepend(CoreExt)
|
|
64
|
-
end
|
|
28
|
+
::Process.singleton_class.prepend(CoreExt)
|
|
65
29
|
end
|
|
66
30
|
|
|
67
31
|
def after_fork(&block)
|
data/lib/active_support/gzip.rb
CHANGED
|
@@ -68,15 +68,15 @@ module ActiveSupport
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def initialize(constructor = nil)
|
|
71
|
-
if constructor.
|
|
71
|
+
if constructor.nil?
|
|
72
|
+
super()
|
|
73
|
+
elsif constructor.respond_to?(:to_hash)
|
|
72
74
|
super()
|
|
73
75
|
update(constructor)
|
|
74
76
|
|
|
75
77
|
hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
|
|
76
78
|
self.default = hash.default if hash.default
|
|
77
79
|
self.default_proc = hash.default_proc if hash.default_proc
|
|
78
|
-
elsif constructor.nil?
|
|
79
|
-
super()
|
|
80
80
|
else
|
|
81
81
|
super(constructor)
|
|
82
82
|
end
|
|
@@ -95,11 +95,27 @@ module ActiveSupport
|
|
|
95
95
|
# hash[:key] = 'value'
|
|
96
96
|
#
|
|
97
97
|
# This value can be later fetched using either +:key+ or <tt>'key'</tt>.
|
|
98
|
+
#
|
|
99
|
+
# If the value is a Hash or contains one or multiple Hashes, they will be
|
|
100
|
+
# converted to +HashWithIndifferentAccess+.
|
|
98
101
|
def []=(key, value)
|
|
99
102
|
regular_writer(convert_key(key), convert_value(value, conversion: :assignment))
|
|
100
103
|
end
|
|
101
104
|
|
|
102
|
-
|
|
105
|
+
# Assigns a new value to the hash:
|
|
106
|
+
#
|
|
107
|
+
# hash = ActiveSupport::HashWithIndifferentAccess.new
|
|
108
|
+
# hash[:key] = 'value'
|
|
109
|
+
#
|
|
110
|
+
# This value can be later fetched using either +:key+ or <tt>'key'</tt>.
|
|
111
|
+
#
|
|
112
|
+
# If the value is a Hash or contains one or multiple Hashes, they will be
|
|
113
|
+
# converted to +HashWithIndifferentAccess+. unless `convert_value: false`
|
|
114
|
+
# is set.
|
|
115
|
+
def store(key, value, convert_value: true)
|
|
116
|
+
value = convert_value(value, conversion: :assignment) if convert_value
|
|
117
|
+
regular_writer(convert_key(key), value)
|
|
118
|
+
end
|
|
103
119
|
|
|
104
120
|
# Updates the receiver in-place, merging in the hashes passed as arguments:
|
|
105
121
|
#
|
|
@@ -262,9 +278,7 @@ module ActiveSupport
|
|
|
262
278
|
# hash[:a][:c] # => "c"
|
|
263
279
|
# dup[:a][:c] # => "c"
|
|
264
280
|
def dup
|
|
265
|
-
self.class.new(self)
|
|
266
|
-
set_defaults(new_hash)
|
|
267
|
-
end
|
|
281
|
+
copy_defaults(self.class.new(self))
|
|
268
282
|
end
|
|
269
283
|
|
|
270
284
|
# This method has the same semantics of +update+, except it does not
|
|
@@ -281,13 +295,13 @@ module ActiveSupport
|
|
|
281
295
|
# hash['a'] = nil
|
|
282
296
|
# hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
|
|
283
297
|
def reverse_merge(other_hash)
|
|
284
|
-
super(
|
|
298
|
+
super(cast(other_hash))
|
|
285
299
|
end
|
|
286
300
|
alias_method :with_defaults, :reverse_merge
|
|
287
301
|
|
|
288
302
|
# Same semantics as +reverse_merge+ but modifies the receiver in-place.
|
|
289
303
|
def reverse_merge!(other_hash)
|
|
290
|
-
super(
|
|
304
|
+
super(cast(other_hash))
|
|
291
305
|
end
|
|
292
306
|
alias_method :with_defaults!, :reverse_merge!
|
|
293
307
|
|
|
@@ -296,7 +310,7 @@ module ActiveSupport
|
|
|
296
310
|
# h = { "a" => 100, "b" => 200 }
|
|
297
311
|
# h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400}
|
|
298
312
|
def replace(other_hash)
|
|
299
|
-
super(
|
|
313
|
+
super(cast(other_hash))
|
|
300
314
|
end
|
|
301
315
|
|
|
302
316
|
# Removes the specified key from the hash.
|
|
@@ -313,10 +327,6 @@ module ActiveSupport
|
|
|
313
327
|
end
|
|
314
328
|
alias_method :without, :except
|
|
315
329
|
|
|
316
|
-
def stringify_keys!; self end
|
|
317
|
-
def deep_stringify_keys!; self end
|
|
318
|
-
def stringify_keys; dup end
|
|
319
|
-
def deep_stringify_keys; dup end
|
|
320
330
|
undef :symbolize_keys!
|
|
321
331
|
undef :deep_symbolize_keys!
|
|
322
332
|
def symbolize_keys; to_hash.symbolize_keys! end
|
|
@@ -342,21 +352,26 @@ module ActiveSupport
|
|
|
342
352
|
NOT_GIVEN = Object.new # :nodoc:
|
|
343
353
|
|
|
344
354
|
def transform_keys(hash = NOT_GIVEN, &block)
|
|
345
|
-
|
|
346
|
-
|
|
355
|
+
if NOT_GIVEN.equal?(hash)
|
|
356
|
+
if block_given?
|
|
357
|
+
self.class.new(super(&block))
|
|
358
|
+
else
|
|
359
|
+
to_enum(:transform_keys)
|
|
360
|
+
end
|
|
361
|
+
else
|
|
362
|
+
self.class.new(super)
|
|
363
|
+
end
|
|
347
364
|
end
|
|
348
365
|
|
|
349
366
|
def transform_keys!(hash = NOT_GIVEN, &block)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
elsif block_given?
|
|
357
|
-
keys.each { |key| self[hash[key] || yield(key)] = delete(key) }
|
|
367
|
+
if NOT_GIVEN.equal?(hash)
|
|
368
|
+
if block_given?
|
|
369
|
+
replace(copy_defaults(transform_keys(&block)))
|
|
370
|
+
else
|
|
371
|
+
return to_enum(:transform_keys!)
|
|
372
|
+
end
|
|
358
373
|
else
|
|
359
|
-
|
|
374
|
+
replace(copy_defaults(transform_keys(hash, &block)))
|
|
360
375
|
end
|
|
361
376
|
|
|
362
377
|
self
|
|
@@ -378,33 +393,27 @@ module ActiveSupport
|
|
|
378
393
|
|
|
379
394
|
# Convert to a regular hash with string keys.
|
|
380
395
|
def to_hash
|
|
381
|
-
|
|
382
|
-
|
|
396
|
+
copy = Hash[self]
|
|
397
|
+
copy.transform_values! { |v| convert_value_to_hash(v) }
|
|
398
|
+
copy_defaults(copy)
|
|
399
|
+
end
|
|
383
400
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
end
|
|
387
|
-
_new_hash
|
|
401
|
+
def to_proc
|
|
402
|
+
proc { |key| self[key] }
|
|
388
403
|
end
|
|
389
404
|
|
|
390
405
|
private
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
key.kind_of?(Symbol) ? key.to_s : key
|
|
398
|
-
end
|
|
406
|
+
def cast(other)
|
|
407
|
+
self.class === other ? other : self.class.new(other)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def convert_key(key)
|
|
411
|
+
Symbol === key ? key.name : key
|
|
399
412
|
end
|
|
400
413
|
|
|
401
414
|
def convert_value(value, conversion: nil)
|
|
402
415
|
if value.is_a? Hash
|
|
403
|
-
|
|
404
|
-
value.to_hash
|
|
405
|
-
else
|
|
406
|
-
value.nested_under_indifferent_access
|
|
407
|
-
end
|
|
416
|
+
value.nested_under_indifferent_access
|
|
408
417
|
elsif value.is_a?(Array)
|
|
409
418
|
if conversion != :assignment || value.frozen?
|
|
410
419
|
value = value.dup
|
|
@@ -415,12 +424,24 @@ module ActiveSupport
|
|
|
415
424
|
end
|
|
416
425
|
end
|
|
417
426
|
|
|
418
|
-
def
|
|
427
|
+
def convert_value_to_hash(value)
|
|
428
|
+
if value.is_a? Hash
|
|
429
|
+
value.to_hash
|
|
430
|
+
elsif value.is_a?(Array)
|
|
431
|
+
value.map { |e| convert_value_to_hash(e) }
|
|
432
|
+
else
|
|
433
|
+
value
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def copy_defaults(target)
|
|
419
439
|
if default_proc
|
|
420
440
|
target.default_proc = default_proc.dup
|
|
421
441
|
else
|
|
422
442
|
target.default = default
|
|
423
443
|
end
|
|
444
|
+
target
|
|
424
445
|
end
|
|
425
446
|
|
|
426
447
|
def update_with_single_argument(other_hash, block)
|
|
@@ -9,11 +9,14 @@ module ActiveSupport
|
|
|
9
9
|
html_safe_options = html_escape_translation_options(options)
|
|
10
10
|
|
|
11
11
|
exception = false
|
|
12
|
+
|
|
12
13
|
exception_handler = ->(*args) do
|
|
13
14
|
exception = true
|
|
14
15
|
I18n.exception_handler.call(*args)
|
|
15
16
|
end
|
|
17
|
+
|
|
16
18
|
translation = I18n.translate(key, **html_safe_options, exception_handler: exception_handler)
|
|
19
|
+
|
|
17
20
|
if exception
|
|
18
21
|
translation
|
|
19
22
|
else
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_support"
|
|
4
4
|
require "active_support/core_ext/array/wrap"
|
|
5
|
+
require "rails/railtie"
|
|
5
6
|
|
|
6
7
|
# :enddoc:
|
|
7
8
|
|
|
@@ -14,15 +15,18 @@ module I18n
|
|
|
14
15
|
|
|
15
16
|
config.eager_load_namespaces << I18n
|
|
16
17
|
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
config.
|
|
18
|
+
# Make sure i18n is ready before eager loading, in case any eager loaded
|
|
19
|
+
# code needs it.
|
|
20
|
+
config.before_eager_load do |app|
|
|
20
21
|
I18n::Railtie.initialize_i18n(app)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
|
|
24
|
+
# i18n initialization needs to run after application initialization, since
|
|
25
|
+
# initializers may configure i18n.
|
|
26
|
+
#
|
|
27
|
+
# If the application eager loaded, this was done on before_eager_load. The
|
|
28
|
+
# hook is still OK, though, because initialize_i18n is idempotent.
|
|
29
|
+
config.after_initialize do |app|
|
|
26
30
|
I18n::Railtie.initialize_i18n(app)
|
|
27
31
|
end
|
|
28
32
|
|
|
@@ -49,7 +53,8 @@ module I18n
|
|
|
49
53
|
when :load_path
|
|
50
54
|
I18n.load_path += value
|
|
51
55
|
when :raise_on_missing_translations
|
|
52
|
-
|
|
56
|
+
strict = value == :strict
|
|
57
|
+
setup_raise_on_missing_translations_config(app, strict)
|
|
53
58
|
else
|
|
54
59
|
I18n.public_send("#{setting}=", value)
|
|
55
60
|
end
|
|
@@ -62,8 +67,8 @@ module I18n
|
|
|
62
67
|
|
|
63
68
|
if app.config.reloading_enabled?
|
|
64
69
|
directories = watched_dirs_with_extensions(reloadable_paths)
|
|
65
|
-
reloader = app.config.file_watcher.new(I18n.load_path
|
|
66
|
-
I18n.load_path.
|
|
70
|
+
reloader = app.config.file_watcher.new(I18n.load_path, directories) do
|
|
71
|
+
I18n.load_path.delete_if { |path| path.to_s.start_with?(Rails.root.to_s) && !File.exist?(path) }
|
|
67
72
|
I18n.load_path |= reloadable_paths.flat_map(&:existent)
|
|
68
73
|
end
|
|
69
74
|
|
|
@@ -71,17 +76,20 @@ module I18n
|
|
|
71
76
|
app.reloader.to_run do
|
|
72
77
|
reloader.execute_if_updated { require_unload_lock! }
|
|
73
78
|
end
|
|
74
|
-
reloader.execute
|
|
75
79
|
end
|
|
76
80
|
|
|
77
81
|
@i18n_inited = true
|
|
78
82
|
end
|
|
79
83
|
|
|
80
|
-
def self.setup_raise_on_missing_translations_config(app)
|
|
84
|
+
def self.setup_raise_on_missing_translations_config(app, strict)
|
|
81
85
|
ActiveSupport.on_load(:action_view) do
|
|
82
86
|
ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
|
|
83
87
|
end
|
|
84
88
|
|
|
89
|
+
ActiveSupport.on_load(:active_model_translation) do
|
|
90
|
+
ActiveModel::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations if strict
|
|
91
|
+
end
|
|
92
|
+
|
|
85
93
|
if app.config.i18n.raise_on_missing_translations &&
|
|
86
94
|
I18n.exception_handler.is_a?(I18n::ExceptionHandler) # Only override the i18n gem's default exception handler.
|
|
87
95
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "concurrent/map"
|
|
4
|
+
require "active_support/core_ext/module/delegation"
|
|
4
5
|
require "active_support/i18n"
|
|
5
6
|
|
|
6
7
|
module ActiveSupport
|
|
@@ -29,44 +30,59 @@ module ActiveSupport
|
|
|
29
30
|
# before any of the rules that may already have been loaded.
|
|
30
31
|
class Inflections
|
|
31
32
|
@__instance__ = Concurrent::Map.new
|
|
33
|
+
@__en_instance__ = nil
|
|
34
|
+
|
|
35
|
+
class Uncountables # :nodoc:
|
|
36
|
+
include Enumerable
|
|
37
|
+
|
|
38
|
+
delegate :each, :pop, :empty?, :to_s, :==, :to_a, :to_ary, to: :@members
|
|
32
39
|
|
|
33
|
-
class Uncountables < Array
|
|
34
40
|
def initialize
|
|
35
|
-
@
|
|
36
|
-
|
|
41
|
+
@members = []
|
|
42
|
+
@pattern = nil
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
def delete(entry)
|
|
40
|
-
|
|
41
|
-
@
|
|
46
|
+
@members.delete(entry)
|
|
47
|
+
@pattern = nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def <<(word)
|
|
51
|
+
word = word.downcase
|
|
52
|
+
@members << word
|
|
53
|
+
@pattern = nil
|
|
54
|
+
self
|
|
42
55
|
end
|
|
43
56
|
|
|
44
|
-
def
|
|
45
|
-
|
|
57
|
+
def flatten
|
|
58
|
+
@members.dup
|
|
46
59
|
end
|
|
47
60
|
|
|
48
61
|
def add(words)
|
|
49
62
|
words = words.flatten.map(&:downcase)
|
|
50
|
-
concat(words)
|
|
51
|
-
@
|
|
63
|
+
@members.concat(words)
|
|
64
|
+
@pattern = nil
|
|
52
65
|
self
|
|
53
66
|
end
|
|
54
67
|
|
|
55
68
|
def uncountable?(str)
|
|
56
|
-
@
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
def to_regex(string)
|
|
61
|
-
/\b#{::Regexp.escape(string)}\Z/i
|
|
69
|
+
if @pattern.nil?
|
|
70
|
+
members_pattern = Regexp.union(@members.map { |w| /#{Regexp.escape(w)}/i })
|
|
71
|
+
@pattern = /\b#{members_pattern}\Z/i
|
|
62
72
|
end
|
|
73
|
+
@pattern.match?(str)
|
|
74
|
+
end
|
|
63
75
|
end
|
|
64
76
|
|
|
65
77
|
def self.instance(locale = :en)
|
|
78
|
+
return @__en_instance__ ||= new if locale == :en
|
|
79
|
+
|
|
66
80
|
@__instance__[locale] ||= new
|
|
67
81
|
end
|
|
68
82
|
|
|
69
83
|
def self.instance_or_fallback(locale)
|
|
84
|
+
return @__en_instance__ ||= new if locale == :en
|
|
85
|
+
|
|
70
86
|
I18n.fallbacks[locale].each do |k|
|
|
71
87
|
return @__instance__[k] if @__instance__.key?(k)
|
|
72
88
|
end
|
|
@@ -128,18 +128,16 @@ module ActiveSupport
|
|
|
128
128
|
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
|
|
129
129
|
|
|
130
130
|
unless separator.nil? || separator.empty?
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
# No more than one of the separator in a row.
|
|
132
|
+
if separator.length == 1
|
|
133
|
+
parameterized_string.squeeze!(separator)
|
|
134
134
|
else
|
|
135
135
|
re_sep = Regexp.escape(separator)
|
|
136
|
-
|
|
137
|
-
re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
|
|
136
|
+
parameterized_string.gsub!(/#{re_sep}{2,}/, separator)
|
|
138
137
|
end
|
|
139
|
-
# No more than one of the separator in a row.
|
|
140
|
-
parameterized_string.gsub!(re_duplicate_separator, separator)
|
|
141
138
|
# Remove leading/trailing separator.
|
|
142
|
-
parameterized_string.
|
|
139
|
+
parameterized_string.delete_prefix!(separator)
|
|
140
|
+
parameterized_string.delete_suffix!(separator)
|
|
143
141
|
end
|
|
144
142
|
|
|
145
143
|
parameterized_string.downcase! unless preserve_case
|