activesupport 6.0.3.4 → 6.1.7.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.

Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +456 -398
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/array_inquirer.rb +4 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +5 -4
  9. data/lib/active_support/cache/mem_cache_store.rb +29 -18
  10. data/lib/active_support/cache/memory_store.rb +46 -26
  11. data/lib/active_support/cache/redis_cache_store.rb +27 -27
  12. data/lib/active_support/cache/strategy/local_cache.rb +21 -6
  13. data/lib/active_support/cache.rb +92 -45
  14. data/lib/active_support/callbacks.rb +65 -56
  15. data/lib/active_support/concern.rb +46 -2
  16. data/lib/active_support/configurable.rb +3 -3
  17. data/lib/active_support/configuration_file.rb +51 -0
  18. data/lib/active_support/core_ext/benchmark.rb +2 -2
  19. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  20. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  21. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  22. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  23. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  24. data/lib/active_support/core_ext/digest/uuid.rb +1 -0
  25. data/lib/active_support/core_ext/enumerable.rb +76 -4
  26. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  27. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  28. data/lib/active_support/core_ext/hash/except.rb +1 -1
  29. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  30. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  31. data/lib/active_support/core_ext/load_error.rb +1 -1
  32. data/lib/active_support/core_ext/marshal.rb +2 -0
  33. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  34. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  35. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  36. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  37. data/lib/active_support/core_ext/module/delegation.rb +38 -28
  38. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  39. data/lib/active_support/core_ext/name_error.rb +29 -2
  40. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  41. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  42. data/lib/active_support/core_ext/object/json.rb +13 -2
  43. data/lib/active_support/core_ext/object/try.rb +2 -2
  44. data/lib/active_support/core_ext/range/compare_range.rb +9 -3
  45. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  46. data/lib/active_support/core_ext/regexp.rb +8 -1
  47. data/lib/active_support/core_ext/string/access.rb +5 -24
  48. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  49. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  50. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  51. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  52. data/lib/active_support/core_ext/string/output_safety.rb +38 -10
  53. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  54. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  55. data/lib/active_support/core_ext/symbol.rb +3 -0
  56. data/lib/active_support/core_ext/time/calculations.rb +22 -1
  57. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  58. data/lib/active_support/core_ext/uri.rb +5 -1
  59. data/lib/active_support/core_ext.rb +1 -1
  60. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  61. data/lib/active_support/current_attributes.rb +9 -2
  62. data/lib/active_support/dependencies/zeitwerk_integration.rb +4 -1
  63. data/lib/active_support/dependencies.rb +43 -19
  64. data/lib/active_support/deprecation/behaviors.rb +15 -2
  65. data/lib/active_support/deprecation/disallowed.rb +56 -0
  66. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  67. data/lib/active_support/deprecation/method_wrappers.rb +3 -2
  68. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  69. data/lib/active_support/deprecation/reporting.rb +50 -7
  70. data/lib/active_support/deprecation.rb +6 -1
  71. data/lib/active_support/descendants_tracker.rb +6 -2
  72. data/lib/active_support/digest.rb +2 -0
  73. data/lib/active_support/duration/iso8601_serializer.rb +15 -9
  74. data/lib/active_support/duration.rb +75 -25
  75. data/lib/active_support/encrypted_file.rb +19 -2
  76. data/lib/active_support/environment_inquirer.rb +20 -0
  77. data/lib/active_support/evented_file_update_checker.rb +69 -133
  78. data/lib/active_support/execution_wrapper.rb +16 -13
  79. data/lib/active_support/fork_tracker.rb +64 -0
  80. data/lib/active_support/gem_version.rb +3 -3
  81. data/lib/active_support/hash_with_indifferent_access.rb +48 -24
  82. data/lib/active_support/i18n_railtie.rb +14 -19
  83. data/lib/active_support/inflector/inflections.rb +1 -2
  84. data/lib/active_support/inflector/methods.rb +36 -33
  85. data/lib/active_support/inflector/transliterate.rb +4 -4
  86. data/lib/active_support/json/decoding.rb +4 -4
  87. data/lib/active_support/json/encoding.rb +5 -1
  88. data/lib/active_support/key_generator.rb +1 -1
  89. data/lib/active_support/locale/en.yml +7 -3
  90. data/lib/active_support/log_subscriber.rb +8 -0
  91. data/lib/active_support/logger.rb +1 -1
  92. data/lib/active_support/logger_silence.rb +2 -26
  93. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  94. data/lib/active_support/message_encryptor.rb +4 -7
  95. data/lib/active_support/message_verifier.rb +5 -5
  96. data/lib/active_support/messages/metadata.rb +9 -1
  97. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  98. data/lib/active_support/messages/rotator.rb +6 -5
  99. data/lib/active_support/multibyte/chars.rb +4 -42
  100. data/lib/active_support/multibyte/unicode.rb +9 -83
  101. data/lib/active_support/notifications/fanout.rb +23 -8
  102. data/lib/active_support/notifications/instrumenter.rb +6 -15
  103. data/lib/active_support/notifications.rb +32 -5
  104. data/lib/active_support/number_helper/number_converter.rb +1 -1
  105. data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -7
  106. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  107. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  108. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  109. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  110. data/lib/active_support/number_helper.rb +29 -14
  111. data/lib/active_support/option_merger.rb +3 -2
  112. data/lib/active_support/ordered_options.rb +8 -2
  113. data/lib/active_support/parameter_filter.rb +16 -11
  114. data/lib/active_support/per_thread_registry.rb +2 -1
  115. data/lib/active_support/rails.rb +1 -4
  116. data/lib/active_support/railtie.rb +23 -1
  117. data/lib/active_support/reloader.rb +1 -1
  118. data/lib/active_support/rescuable.rb +4 -4
  119. data/lib/active_support/secure_compare_rotator.rb +51 -0
  120. data/lib/active_support/security_utils.rb +19 -12
  121. data/lib/active_support/string_inquirer.rb +4 -2
  122. data/lib/active_support/subscriber.rb +12 -7
  123. data/lib/active_support/tagged_logging.rb +30 -5
  124. data/lib/active_support/testing/assertions.rb +18 -11
  125. data/lib/active_support/testing/parallelization/server.rb +78 -0
  126. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  127. data/lib/active_support/testing/parallelization.rb +12 -95
  128. data/lib/active_support/testing/time_helpers.rb +40 -3
  129. data/lib/active_support/time_with_zone.rb +67 -43
  130. data/lib/active_support/values/time_zone.rb +22 -10
  131. data/lib/active_support/xml_mini/rexml.rb +8 -1
  132. data/lib/active_support.rb +13 -1
  133. metadata +35 -36
  134. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  135. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  136. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  137. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  138. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  139. data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -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,62 +40,22 @@ module ActiveSupport
38
40
  raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
39
41
  end
40
42
 
41
- @ph = PathHelper.new
42
- @files = files.map { |f| @ph.xpath(f) }.to_set
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
- dtw = directories_to_watch
56
- @dtw, @missing = dtw.partition(&:exist?)
57
-
58
- if @dtw.any?
59
- # Loading listen triggers warnings. These are originated by a legit
60
- # usage of attr_* macros for private attributes, but adds a lot of noise
61
- # to our test suite. Thus, we lazy load it and disable warnings locally.
62
- silence_warnings do
63
- require "listen"
64
- rescue LoadError => e
65
- raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
66
- end
67
- end
68
- boot!
43
+ @block = block
44
+ @core = Core.new(files, dirs)
45
+ ObjectSpace.define_finalizer(self, @core.finalizer)
69
46
  end
70
47
 
71
48
  def updated?
72
- @boot_mutex.synchronize do
73
- if @pid != Process.pid
74
- boot!
75
- @pid = Process.pid
76
- @updated.make_true
77
- end
78
- end
79
-
80
- if @missing.any?(&:exist?)
81
- @boot_mutex.synchronize do
82
- appeared, @missing = @missing.partition(&:exist?)
83
- shutdown!
84
-
85
- @dtw += appeared
86
- boot!
87
-
88
- @updated.make_true
89
- end
49
+ if @core.restart?
50
+ @core.thread_safely(&:restart)
51
+ @core.updated.make_true
90
52
  end
91
53
 
92
- @updated.true?
54
+ @core.updated.true?
93
55
  end
94
56
 
95
57
  def execute
96
- @updated.make_false
58
+ @core.updated.make_false
97
59
  @block.call
98
60
  end
99
61
 
@@ -105,17 +67,59 @@ module ActiveSupport
105
67
  end
106
68
  end
107
69
 
108
- private
109
- def boot!
110
- normalize_dirs!
70
+ class Core
71
+ attr_reader :updated
72
+
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
78
+ end
79
+
80
+ @common_path = common_path(@dirs.keys)
81
+
82
+ @dtw = directories_to_watch
83
+ @missing = []
84
+
85
+ @updated = Concurrent::AtomicBoolean.new(false)
86
+ @mutex = Mutex.new
87
+
88
+ start
89
+ @after_fork = ActiveSupport::ForkTracker.after_fork { start }
90
+ end
91
+
92
+ def finalizer
93
+ proc do
94
+ stop
95
+ ActiveSupport::ForkTracker.unregister(@after_fork)
96
+ end
97
+ end
111
98
 
112
- unless @dtw.empty?
113
- Listen.to(*@dtw, &method(:changed)).start
99
+ def thread_safely
100
+ @mutex.synchronize do
101
+ yield self
114
102
  end
115
103
  end
116
104
 
117
- def shutdown!
118
- Listen.stop
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
110
+ end
111
+
112
+ def stop
113
+ @listener&.stop
114
+ end
115
+
116
+ def restart
117
+ stop
118
+ start
119
+ end
120
+
121
+ def restart?
122
+ @missing.any?(&:exist?)
119
123
  end
120
124
 
121
125
  def normalize_dirs!
@@ -125,27 +129,27 @@ module ActiveSupport
125
129
  end
126
130
 
127
131
  def changed(modified, added, removed)
128
- unless updated?
132
+ unless @updated.true?
129
133
  @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
130
134
  end
131
135
  end
132
136
 
133
137
  def watching?(file)
134
- file = @ph.xpath(file)
138
+ file = Pathname(file)
135
139
 
136
140
  if @files.member?(file)
137
141
  true
138
142
  elsif file.directory?
139
143
  false
140
144
  else
141
- ext = @ph.normalize_extension(file.extname)
145
+ ext = file.extname
142
146
 
143
147
  file.dirname.ascend do |dir|
144
148
  matching = @dirs[dir]
145
149
 
146
150
  if matching && (matching.empty? || matching.include?(ext))
147
151
  break true
148
- elsif dir == @lcsp || dir.root?
152
+ elsif dir == @common_path || dir.root?
149
153
  break false
150
154
  end
151
155
  end
@@ -153,82 +157,14 @@ module ActiveSupport
153
157
  end
154
158
 
155
159
  def directories_to_watch
156
- dtw = @files.map(&:dirname) + @dirs.keys
157
- dtw.compact!
158
- dtw.uniq!
159
-
160
- normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
161
- dtw = dtw.reject do |path|
162
- normalized_gem_paths.any? { |gem_path| path.to_s.start_with?(gem_path) }
163
- end
164
-
165
- @ph.filter_out_descendants(dtw)
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) } }
166
163
  end
167
164
 
168
- class PathHelper
169
- def xpath(path)
170
- Pathname.new(path).expand_path
171
- end
172
-
173
- def normalize_extension(ext)
174
- ext.to_s.sub(/\A\./, "")
175
- end
176
-
177
- # Given a collection of Pathname objects returns the longest subpath
178
- # common to all of them, or +nil+ if there is none.
179
- def longest_common_subpath(paths)
180
- return if paths.empty?
181
-
182
- lcsp = Pathname.new(paths[0])
183
-
184
- paths[1..-1].each do |path|
185
- until ascendant_of?(lcsp, path)
186
- if lcsp.root?
187
- # If we get here a root directory is not an ascendant of path.
188
- # This may happen if there are paths in different drives on
189
- # Windows.
190
- return
191
- else
192
- lcsp = lcsp.parent
193
- end
194
- end
195
- end
196
-
197
- lcsp
198
- end
199
-
200
- # Returns the deepest existing ascendant, which could be the argument itself.
201
- def existing_parent(dir)
202
- dir.ascend do |ascendant|
203
- break ascendant if ascendant.directory?
204
- end
205
- end
206
-
207
- # Filters out directories which are descendants of others in the collection (stable).
208
- def filter_out_descendants(dirs)
209
- return dirs if dirs.length < 2
210
-
211
- dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length }
212
- descendants = []
213
-
214
- until dirs_sorted_by_nparts.empty?
215
- dir = dirs_sorted_by_nparts.shift
216
-
217
- dirs_sorted_by_nparts.reject! do |possible_descendant|
218
- ascendant_of?(dir, possible_descendant) && descendants << possible_descendant
219
- end
220
- end
221
-
222
- # Array#- preserves order.
223
- dirs - descendants
224
- end
225
-
226
- private
227
- def ascendant_of?(base, other)
228
- base != other && other.ascend do |ascendant|
229
- break true if base == ascendant
230
- end
231
- end
165
+ def common_path(paths)
166
+ paths.map { |path| path.ascend.to_a }.reduce(&:&)&.first
232
167
  end
168
+ end
233
169
  end
234
170
  end
@@ -63,18 +63,21 @@ module ActiveSupport
63
63
  # after the work has been performed.
64
64
  #
65
65
  # Where possible, prefer +wrap+.
66
- def self.run!
67
- if active?
68
- Null
66
+ def self.run!(reset: false)
67
+ if reset
68
+ lost_instance = active.delete(Thread.current)
69
+ lost_instance&.complete!
69
70
  else
70
- new.tap do |instance|
71
- success = nil
72
- begin
73
- instance.run!
74
- success = true
75
- ensure
76
- instance.complete! unless success
77
- end
71
+ return Null if active?
72
+ end
73
+
74
+ new.tap do |instance|
75
+ success = nil
76
+ begin
77
+ instance.run!
78
+ success = true
79
+ ensure
80
+ instance.complete! unless success
78
81
  end
79
82
  end
80
83
  end
@@ -103,11 +106,11 @@ module ActiveSupport
103
106
  self.active = Concurrent::Hash.new
104
107
 
105
108
  def self.active? # :nodoc:
106
- @active[Thread.current]
109
+ @active.key?(Thread.current)
107
110
  end
108
111
 
109
112
  def run! # :nodoc:
110
- self.class.active[Thread.current] = true
113
+ self.class.active[Thread.current] = self
111
114
  run_callbacks(:run)
112
115
  end
113
116
 
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module ForkTracker # :nodoc:
5
+ module CoreExt
6
+ def fork(*)
7
+ if block_given?
8
+ super do
9
+ ForkTracker.check!
10
+ yield
11
+ end
12
+ else
13
+ unless pid = super
14
+ ForkTracker.check!
15
+ end
16
+ pid
17
+ end
18
+ end
19
+ ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
20
+ end
21
+
22
+ module CoreExtPrivate
23
+ include CoreExt
24
+
25
+ private
26
+ def fork(*)
27
+ super
28
+ end
29
+ ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
30
+ end
31
+
32
+ @pid = Process.pid
33
+ @callbacks = []
34
+
35
+ class << self
36
+ def check!
37
+ if @pid != Process.pid
38
+ @callbacks.each(&:call)
39
+ @pid = Process.pid
40
+ end
41
+ end
42
+
43
+ def hook!
44
+ if Process.respond_to?(:fork)
45
+ ::Object.prepend(CoreExtPrivate)
46
+ ::Kernel.prepend(CoreExtPrivate)
47
+ ::Kernel.singleton_class.prepend(CoreExt)
48
+ ::Process.singleton_class.prepend(CoreExt)
49
+ end
50
+ end
51
+
52
+ def after_fork(&block)
53
+ @callbacks << block
54
+ block
55
+ end
56
+
57
+ def unregister(callback)
58
+ @callbacks.delete(callback)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ ActiveSupport::ForkTracker.hook!
@@ -8,9 +8,9 @@ module ActiveSupport
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 6
11
- MINOR = 0
12
- TINY = 3
13
- PRE = "4"
11
+ MINOR = 1
12
+ TINY = 7
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -3,6 +3,7 @@
3
3
  require "active_support/core_ext/hash/keys"
4
4
  require "active_support/core_ext/hash/reverse_merge"
5
5
  require "active_support/core_ext/hash/except"
6
+ require "active_support/core_ext/hash/slice"
6
7
 
7
8
  module ActiveSupport
8
9
  # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -69,7 +70,7 @@ module ActiveSupport
69
70
  super()
70
71
  update(constructor)
71
72
 
72
- hash = constructor.to_hash
73
+ hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
73
74
  self.default = hash.default if hash.default
74
75
  self.default_proc = hash.default_proc if hash.default_proc
75
76
  else
@@ -91,12 +92,12 @@ module ActiveSupport
91
92
  #
92
93
  # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
93
94
  def []=(key, value)
94
- regular_writer(convert_key(key), convert_value(value, for: :assignment))
95
+ regular_writer(convert_key(key), convert_value(value, conversion: :assignment))
95
96
  end
96
97
 
97
98
  alias_method :store, :[]=
98
99
 
99
- # Updates the receiver in-place, merging in the hash passed as argument:
100
+ # Updates the receiver in-place, merging in the hashes passed as arguments:
100
101
  #
101
102
  # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
102
103
  # hash_1[:key] = 'value'
@@ -106,11 +107,14 @@ module ActiveSupport
106
107
  #
107
108
  # hash_1.update(hash_2) # => {"key"=>"New Value!"}
108
109
  #
109
- # The argument can be either an
110
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
111
+ # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 }
112
+ #
113
+ # The arguments can be either an
110
114
  # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
111
115
  # In either case the merge respects the semantics of indifferent access.
112
116
  #
113
- # If the argument is a regular hash with keys +:key+ and +"key"+ only one
117
+ # If the argument is a regular hash with keys +:key+ and <tt>"key"</tt> only one
114
118
  # of the values end up in the receiver, but which one is unspecified.
115
119
  #
116
120
  # When given a block, the value for duplicated keys will be determined
@@ -121,18 +125,15 @@ module ActiveSupport
121
125
  # hash_1[:key] = 10
122
126
  # hash_2['key'] = 12
123
127
  # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
124
- def update(other_hash)
125
- if other_hash.is_a? HashWithIndifferentAccess
126
- super(other_hash)
128
+ def update(*other_hashes, &block)
129
+ if other_hashes.size == 1
130
+ update_with_single_argument(other_hashes.first, block)
127
131
  else
128
- other_hash.to_hash.each_pair do |key, value|
129
- if block_given? && key?(key)
130
- value = yield(convert_key(key), self[key], value)
131
- end
132
- regular_writer(convert_key(key), convert_value(value))
132
+ other_hashes.each do |other_hash|
133
+ update_with_single_argument(other_hash, block)
133
134
  end
134
- self
135
135
  end
136
+ self
136
137
  end
137
138
 
138
139
  alias_method :merge!, :update
@@ -259,8 +260,8 @@ module ActiveSupport
259
260
  # This method has the same semantics of +update+, except it does not
260
261
  # modify the receiver but rather returns a new hash with indifferent
261
262
  # access with the result of the merge.
262
- def merge(hash, &block)
263
- dup.update(hash, &block)
263
+ def merge(*hashes, &block)
264
+ dup.update(*hashes, &block)
264
265
  end
265
266
 
266
267
  # Like +merge+ but the other way around: Merges the receiver into the
@@ -293,6 +294,10 @@ module ActiveSupport
293
294
  super(convert_key(key))
294
295
  end
295
296
 
297
+ # Returns a hash with indifferent access that includes everything except given keys.
298
+ # hash = { a: "x", b: "y", c: 10 }.with_indifferent_access
299
+ # hash.except(:a, "b") # => {c: 10}.with_indifferent_access
300
+ # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access
296
301
  def except(*keys)
297
302
  slice(*self.keys - keys.map { |key| convert_key(key) })
298
303
  end
@@ -357,40 +362,59 @@ module ActiveSupport
357
362
  set_defaults(_new_hash)
358
363
 
359
364
  each do |key, value|
360
- _new_hash[key] = convert_value(value, for: :to_hash)
365
+ _new_hash[key] = convert_value(value, conversion: :to_hash)
361
366
  end
362
367
  _new_hash
363
368
  end
364
369
 
365
370
  private
366
- def convert_key(key) # :doc:
367
- key.kind_of?(Symbol) ? key.to_s : key
371
+ if Symbol.method_defined?(:name)
372
+ def convert_key(key)
373
+ key.kind_of?(Symbol) ? key.name : key
374
+ end
375
+ else
376
+ def convert_key(key)
377
+ key.kind_of?(Symbol) ? key.to_s : key
378
+ end
368
379
  end
369
380
 
370
- def convert_value(value, options = {}) # :doc:
381
+ def convert_value(value, conversion: nil)
371
382
  if value.is_a? Hash
372
- if options[:for] == :to_hash
383
+ if conversion == :to_hash
373
384
  value.to_hash
374
385
  else
375
386
  value.nested_under_indifferent_access
376
387
  end
377
388
  elsif value.is_a?(Array)
378
- if options[:for] != :assignment || value.frozen?
389
+ if conversion != :assignment || value.frozen?
379
390
  value = value.dup
380
391
  end
381
- value.map! { |e| convert_value(e, options) }
392
+ value.map! { |e| convert_value(e, conversion: conversion) }
382
393
  else
383
394
  value
384
395
  end
385
396
  end
386
397
 
387
- def set_defaults(target) # :doc:
398
+ def set_defaults(target)
388
399
  if default_proc
389
400
  target.default_proc = default_proc.dup
390
401
  else
391
402
  target.default = default
392
403
  end
393
404
  end
405
+
406
+ def update_with_single_argument(other_hash, block)
407
+ if other_hash.is_a? HashWithIndifferentAccess
408
+ regular_update(other_hash, &block)
409
+ else
410
+ other_hash.to_hash.each_pair do |key, value|
411
+ if block && key?(key)
412
+ value = block.call(convert_key(key), self[key], value)
413
+ end
414
+ regular_writer(convert_key(key), convert_value(value))
415
+ end
416
+ end
417
+ end
394
418
  end
395
419
  end
396
420
 
@@ -12,9 +12,7 @@ module I18n
12
12
  config.i18n.load_path = []
13
13
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
14
14
 
15
- if I18n.respond_to?(:eager_load!)
16
- config.eager_load_namespaces << I18n
17
- end
15
+ config.eager_load_namespaces << I18n
18
16
 
19
17
  # Set the i18n configuration after initialization since a lot of
20
18
  # configuration is still usually done in application initializers.
@@ -50,8 +48,10 @@ module I18n
50
48
  app.config.i18n.load_path.unshift(*value.flat_map(&:existent))
51
49
  when :load_path
52
50
  I18n.load_path += value
51
+ when :raise_on_missing_translations
52
+ forward_raise_on_missing_translations_config(app)
53
53
  else
54
- I18n.send("#{setting}=", value)
54
+ I18n.public_send("#{setting}=", value)
55
55
  end
56
56
  end
57
57
 
@@ -64,8 +64,6 @@ module I18n
64
64
  reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
65
65
  I18n.load_path.keep_if { |p| File.exist?(p) }
66
66
  I18n.load_path |= reloadable_paths.flat_map(&:existent)
67
-
68
- I18n.reload!
69
67
  end
70
68
 
71
69
  app.reloaders << reloader
@@ -77,6 +75,16 @@ module I18n
77
75
  @i18n_inited = true
78
76
  end
79
77
 
78
+ def self.forward_raise_on_missing_translations_config(app)
79
+ ActiveSupport.on_load(:action_view) do
80
+ self.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
81
+ end
82
+
83
+ ActiveSupport.on_load(:action_controller) do
84
+ AbstractController::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
85
+ end
86
+ end
87
+
80
88
  def self.include_fallbacks_module
81
89
  I18n.backend.class.include(I18n::Backend::Fallbacks)
82
90
  end
@@ -94,19 +102,6 @@ module I18n
94
102
  [I18n.default_locale]
95
103
  end
96
104
 
97
- if args.empty? || args.first.is_a?(Hash)
98
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
99
- Using I18n fallbacks with an empty `defaults` sets the defaults to
100
- include the `default_locale`. This behavior will change in Rails 6.1.
101
- If you desire the default locale to be included in the defaults, please
102
- explicitly configure it with `config.i18n.fallbacks.defaults =
103
- [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
104
- {...}]`. If you want to opt-in to the new behavior, use
105
- `config.i18n.fallbacks.defaults = [nil, {...}]`.
106
- MSG
107
- args.unshift I18n.default_locale
108
- end
109
-
110
105
  I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
111
106
  end
112
107
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "concurrent/map"
4
4
  require "active_support/i18n"
5
- require "active_support/deprecation"
6
5
 
7
6
  module ActiveSupport
8
7
  module Inflector
@@ -77,7 +76,7 @@ module ActiveSupport
77
76
  # Private, for the test suite.
78
77
  def initialize_dup(orig) # :nodoc:
79
78
  %w(plurals singulars uncountables humans acronyms).each do |scope|
80
- instance_variable_set("@#{scope}", orig.send(scope).dup)
79
+ instance_variable_set("@#{scope}", orig.public_send(scope).dup)
81
80
  end
82
81
  define_acronym_regex_patterns
83
82
  end