activesupport 5.2.8.1 → 6.1.6.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +426 -424
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +34 -34
  10. data/lib/active_support/cache/mem_cache_store.rb +39 -24
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +72 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +148 -78
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +70 -3
  18. data/lib/active_support/concurrency/share_lock.rb +0 -1
  19. data/lib/active_support/configurable.rb +10 -14
  20. data/lib/active_support/configuration_file.rb +51 -0
  21. data/lib/active_support/core_ext/array/access.rb +18 -6
  22. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  23. data/lib/active_support/core_ext/array/extract.rb +21 -0
  24. data/lib/active_support/core_ext/array.rb +1 -1
  25. data/lib/active_support/core_ext/benchmark.rb +2 -2
  26. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  27. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  28. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  29. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  30. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  31. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  32. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  33. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  34. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  35. data/lib/active_support/core_ext/digest/uuid.rb +1 -0
  36. data/lib/active_support/core_ext/enumerable.rb +171 -75
  37. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  38. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  39. data/lib/active_support/core_ext/hash/except.rb +2 -2
  40. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  41. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  42. data/lib/active_support/core_ext/hash.rb +1 -2
  43. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  44. data/lib/active_support/core_ext/kernel.rb +0 -1
  45. data/lib/active_support/core_ext/load_error.rb +1 -1
  46. data/lib/active_support/core_ext/marshal.rb +2 -0
  47. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  48. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  49. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  50. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  51. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  52. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  53. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  54. data/lib/active_support/core_ext/module.rb +0 -1
  55. data/lib/active_support/core_ext/name_error.rb +29 -2
  56. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  57. data/lib/active_support/core_ext/numeric.rb +0 -1
  58. data/lib/active_support/core_ext/object/blank.rb +1 -2
  59. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  60. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  61. data/lib/active_support/core_ext/object/json.rb +14 -2
  62. data/lib/active_support/core_ext/object/try.rb +17 -7
  63. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  64. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  65. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  66. data/lib/active_support/core_ext/range/each.rb +0 -1
  67. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  68. data/lib/active_support/core_ext/regexp.rb +8 -5
  69. data/lib/active_support/core_ext/securerandom.rb +23 -3
  70. data/lib/active_support/core_ext/string/access.rb +5 -16
  71. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  72. data/lib/active_support/core_ext/string/filters.rb +42 -1
  73. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  74. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  75. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  76. data/lib/active_support/core_ext/string/output_safety.rb +70 -13
  77. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  78. data/lib/active_support/core_ext/string/strip.rb +3 -1
  79. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  80. data/lib/active_support/core_ext/symbol.rb +3 -0
  81. data/lib/active_support/core_ext/time/calculations.rb +53 -3
  82. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  83. data/lib/active_support/core_ext/uri.rb +6 -1
  84. data/lib/active_support/core_ext.rb +1 -1
  85. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  86. data/lib/active_support/current_attributes.rb +16 -2
  87. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/deprecation/behaviors.rb +16 -3
  90. data/lib/active_support/deprecation/disallowed.rb +56 -0
  91. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  92. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  93. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  94. data/lib/active_support/deprecation/reporting.rb +50 -7
  95. data/lib/active_support/deprecation.rb +6 -1
  96. data/lib/active_support/descendants_tracker.rb +59 -9
  97. data/lib/active_support/digest.rb +2 -0
  98. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  99. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  100. data/lib/active_support/duration.rb +82 -33
  101. data/lib/active_support/encrypted_configuration.rb +0 -4
  102. data/lib/active_support/encrypted_file.rb +22 -4
  103. data/lib/active_support/environment_inquirer.rb +20 -0
  104. data/lib/active_support/evented_file_update_checker.rb +82 -117
  105. data/lib/active_support/execution_wrapper.rb +2 -1
  106. data/lib/active_support/file_update_checker.rb +0 -1
  107. data/lib/active_support/fork_tracker.rb +64 -0
  108. data/lib/active_support/gem_version.rb +3 -3
  109. data/lib/active_support/hash_with_indifferent_access.rb +70 -42
  110. data/lib/active_support/i18n.rb +1 -0
  111. data/lib/active_support/i18n_railtie.rb +15 -8
  112. data/lib/active_support/inflector/inflections.rb +2 -7
  113. data/lib/active_support/inflector/methods.rb +49 -58
  114. data/lib/active_support/inflector/transliterate.rb +47 -18
  115. data/lib/active_support/json/decoding.rb +25 -26
  116. data/lib/active_support/json/encoding.rb +11 -3
  117. data/lib/active_support/key_generator.rb +1 -33
  118. data/lib/active_support/lazy_load_hooks.rb +5 -2
  119. data/lib/active_support/locale/en.rb +33 -0
  120. data/lib/active_support/locale/en.yml +7 -3
  121. data/lib/active_support/log_subscriber.rb +39 -9
  122. data/lib/active_support/logger.rb +2 -17
  123. data/lib/active_support/logger_silence.rb +11 -19
  124. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  125. data/lib/active_support/message_encryptor.rb +8 -13
  126. data/lib/active_support/message_verifier.rb +10 -10
  127. data/lib/active_support/messages/metadata.rb +11 -2
  128. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  129. data/lib/active_support/messages/rotator.rb +10 -9
  130. data/lib/active_support/multibyte/chars.rb +10 -68
  131. data/lib/active_support/multibyte/unicode.rb +15 -327
  132. data/lib/active_support/notifications/fanout.rb +116 -16
  133. data/lib/active_support/notifications/instrumenter.rb +71 -9
  134. data/lib/active_support/notifications.rb +72 -8
  135. data/lib/active_support/number_helper/number_converter.rb +5 -6
  136. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  137. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  138. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  139. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  140. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  141. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  142. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  143. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  144. data/lib/active_support/number_helper.rb +38 -12
  145. data/lib/active_support/option_merger.rb +22 -3
  146. data/lib/active_support/ordered_hash.rb +1 -1
  147. data/lib/active_support/ordered_options.rb +13 -3
  148. data/lib/active_support/parameter_filter.rb +133 -0
  149. data/lib/active_support/per_thread_registry.rb +2 -1
  150. data/lib/active_support/rails.rb +1 -10
  151. data/lib/active_support/railtie.rb +23 -1
  152. data/lib/active_support/reloader.rb +4 -5
  153. data/lib/active_support/rescuable.rb +4 -4
  154. data/lib/active_support/secure_compare_rotator.rb +51 -0
  155. data/lib/active_support/security_utils.rb +19 -12
  156. data/lib/active_support/string_inquirer.rb +4 -3
  157. data/lib/active_support/subscriber.rb +72 -28
  158. data/lib/active_support/tagged_logging.rb +42 -8
  159. data/lib/active_support/test_case.rb +91 -0
  160. data/lib/active_support/testing/assertions.rb +30 -9
  161. data/lib/active_support/testing/deprecation.rb +0 -1
  162. data/lib/active_support/testing/file_fixtures.rb +2 -0
  163. data/lib/active_support/testing/isolation.rb +2 -2
  164. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  165. data/lib/active_support/testing/parallelization/server.rb +78 -0
  166. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  167. data/lib/active_support/testing/parallelization.rb +51 -0
  168. data/lib/active_support/testing/stream.rb +1 -2
  169. data/lib/active_support/testing/time_helpers.rb +47 -12
  170. data/lib/active_support/time_with_zone.rb +81 -47
  171. data/lib/active_support/values/time_zone.rb +34 -17
  172. data/lib/active_support/xml_mini/jdom.rb +2 -3
  173. data/lib/active_support/xml_mini/libxml.rb +2 -2
  174. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  175. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  176. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  177. data/lib/active_support/xml_mini/rexml.rb +10 -3
  178. data/lib/active_support/xml_mini.rb +2 -10
  179. data/lib/active_support.rb +14 -1
  180. metadata +54 -27
  181. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  182. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  183. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  184. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  185. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  186. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  187. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  188. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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!
@@ -7,9 +7,9 @@ module ActiveSupport
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 5
11
- MINOR = 2
12
- TINY = 8
10
+ MAJOR = 6
11
+ MINOR = 1
12
+ TINY = 6
13
13
  PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "active_support/core_ext/hash/keys"
4
4
  require "active_support/core_ext/hash/reverse_merge"
5
+ require "active_support/core_ext/hash/except"
6
+ require "active_support/core_ext/hash/slice"
5
7
 
6
8
  module ActiveSupport
7
9
  # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -68,7 +70,7 @@ module ActiveSupport
68
70
  super()
69
71
  update(constructor)
70
72
 
71
- hash = constructor.to_hash
73
+ hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
72
74
  self.default = hash.default if hash.default
73
75
  self.default_proc = hash.default_proc if hash.default_proc
74
76
  else
@@ -90,12 +92,12 @@ module ActiveSupport
90
92
  #
91
93
  # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
92
94
  def []=(key, value)
93
- regular_writer(convert_key(key), convert_value(value, for: :assignment))
95
+ regular_writer(convert_key(key), convert_value(value, conversion: :assignment))
94
96
  end
95
97
 
96
98
  alias_method :store, :[]=
97
99
 
98
- # 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:
99
101
  #
100
102
  # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
101
103
  # hash_1[:key] = 'value'
@@ -105,11 +107,14 @@ module ActiveSupport
105
107
  #
106
108
  # hash_1.update(hash_2) # => {"key"=>"New Value!"}
107
109
  #
108
- # 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
109
114
  # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
110
115
  # In either case the merge respects the semantics of indifferent access.
111
116
  #
112
- # 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
113
118
  # of the values end up in the receiver, but which one is unspecified.
114
119
  #
115
120
  # When given a block, the value for duplicated keys will be determined
@@ -120,18 +125,15 @@ module ActiveSupport
120
125
  # hash_1[:key] = 10
121
126
  # hash_2['key'] = 12
122
127
  # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
123
- def update(other_hash)
124
- if other_hash.is_a? HashWithIndifferentAccess
125
- super(other_hash)
128
+ def update(*other_hashes, &block)
129
+ if other_hashes.size == 1
130
+ update_with_single_argument(other_hashes.first, block)
126
131
  else
127
- other_hash.to_hash.each_pair do |key, value|
128
- if block_given? && key?(key)
129
- value = yield(convert_key(key), self[key], value)
130
- end
131
- regular_writer(convert_key(key), convert_value(value))
132
+ other_hashes.each do |other_hash|
133
+ update_with_single_argument(other_hash, block)
132
134
  end
133
- self
134
135
  end
136
+ self
135
137
  end
136
138
 
137
139
  alias_method :merge!, :update
@@ -190,20 +192,18 @@ module ActiveSupport
190
192
  super(convert_key(key), *extras)
191
193
  end
192
194
 
193
- if Hash.new.respond_to?(:dig)
194
- # Same as <tt>Hash#dig</tt> where the key passed as argument can be
195
- # either a string or a symbol:
196
- #
197
- # counters = ActiveSupport::HashWithIndifferentAccess.new
198
- # counters[:foo] = { bar: 1 }
199
- #
200
- # counters.dig('foo', 'bar') # => 1
201
- # counters.dig(:foo, :bar) # => 1
202
- # counters.dig(:zoo) # => nil
203
- def dig(*args)
204
- args[0] = convert_key(args[0]) if args.size > 0
205
- super(*args)
206
- end
195
+ # Same as <tt>Hash#dig</tt> where the key passed as argument can be
196
+ # either a string or a symbol:
197
+ #
198
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
199
+ # counters[:foo] = { bar: 1 }
200
+ #
201
+ # counters.dig('foo', 'bar') # => 1
202
+ # counters.dig(:foo, :bar) # => 1
203
+ # counters.dig(:zoo) # => nil
204
+ def dig(*args)
205
+ args[0] = convert_key(args[0]) if args.size > 0
206
+ super(*args)
207
207
  end
208
208
 
209
209
  # Same as <tt>Hash#default</tt> where the key passed as argument can be
@@ -226,8 +226,8 @@ module ActiveSupport
226
226
  # hash[:a] = 'x'
227
227
  # hash[:b] = 'y'
228
228
  # hash.values_at('a', 'b') # => ["x", "y"]
229
- def values_at(*indices)
230
- indices.collect { |key| self[convert_key(key)] }
229
+ def values_at(*keys)
230
+ super(*keys.map { |key| convert_key(key) })
231
231
  end
232
232
 
233
233
  # Returns an array of the values at the specified indices, but also
@@ -240,8 +240,8 @@ module ActiveSupport
240
240
  # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
241
241
  # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
242
242
  def fetch_values(*indices, &block)
243
- indices.collect { |key| fetch(key, &block) }
244
- end if Hash.method_defined?(:fetch_values)
243
+ super(*indices.map { |key| convert_key(key) }, &block)
244
+ end
245
245
 
246
246
  # Returns a shallow copy of the hash.
247
247
  #
@@ -260,8 +260,8 @@ module ActiveSupport
260
260
  # This method has the same semantics of +update+, except it does not
261
261
  # modify the receiver but rather returns a new hash with indifferent
262
262
  # access with the result of the merge.
263
- def merge(hash, &block)
264
- dup.update(hash, &block)
263
+ def merge(*hashes, &block)
264
+ dup.update(*hashes, &block)
265
265
  end
266
266
 
267
267
  # Like +merge+ but the other way around: Merges the receiver into the
@@ -294,6 +294,15 @@ module ActiveSupport
294
294
  super(convert_key(key))
295
295
  end
296
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
301
+ def except(*keys)
302
+ slice(*self.keys - keys.map { |key| convert_key(key) })
303
+ end
304
+ alias_method :without, :except
305
+
297
306
  def stringify_keys!; self end
298
307
  def deep_stringify_keys!; self end
299
308
  def stringify_keys; dup end
@@ -353,40 +362,59 @@ module ActiveSupport
353
362
  set_defaults(_new_hash)
354
363
 
355
364
  each do |key, value|
356
- _new_hash[key] = convert_value(value, for: :to_hash)
365
+ _new_hash[key] = convert_value(value, conversion: :to_hash)
357
366
  end
358
367
  _new_hash
359
368
  end
360
369
 
361
370
  private
362
- def convert_key(key) # :doc:
363
- 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
364
379
  end
365
380
 
366
- def convert_value(value, options = {}) # :doc:
381
+ def convert_value(value, conversion: nil)
367
382
  if value.is_a? Hash
368
- if options[:for] == :to_hash
383
+ if conversion == :to_hash
369
384
  value.to_hash
370
385
  else
371
386
  value.nested_under_indifferent_access
372
387
  end
373
388
  elsif value.is_a?(Array)
374
- if options[:for] != :assignment || value.frozen?
389
+ if conversion != :assignment || value.frozen?
375
390
  value = value.dup
376
391
  end
377
- value.map! { |e| convert_value(e, options) }
392
+ value.map! { |e| convert_value(e, conversion: conversion) }
378
393
  else
379
394
  value
380
395
  end
381
396
  end
382
397
 
383
- def set_defaults(target) # :doc:
398
+ def set_defaults(target)
384
399
  if default_proc
385
400
  target.default_proc = default_proc.dup
386
401
  else
387
402
  target.default = default
388
403
  end
389
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
390
418
  end
391
419
  end
392
420
 
@@ -13,3 +13,4 @@ require "active_support/lazy_load_hooks"
13
13
 
14
14
  ActiveSupport.run_load_hooks(:i18n)
15
15
  I18n.load_path << File.expand_path("locale/en.yml", __dir__)
16
+ I18n.load_path << File.expand_path("locale/en.rb", __dir__)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
- require "active_support/file_update_checker"
5
4
  require "active_support/core_ext/array/wrap"
6
5
 
7
6
  # :enddoc:
@@ -13,6 +12,8 @@ module I18n
13
12
  config.i18n.load_path = []
14
13
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
15
14
 
15
+ config.eager_load_namespaces << I18n
16
+
16
17
  # Set the i18n configuration after initialization since a lot of
17
18
  # configuration is still usually done in application initializers.
18
19
  config.after_initialize do |app|
@@ -47,8 +48,10 @@ module I18n
47
48
  app.config.i18n.load_path.unshift(*value.flat_map(&:existent))
48
49
  when :load_path
49
50
  I18n.load_path += value
51
+ when :raise_on_missing_translations
52
+ forward_raise_on_missing_translations_config(app)
50
53
  else
51
- I18n.send("#{setting}=", value)
54
+ I18n.public_send("#{setting}=", value)
52
55
  end
53
56
  end
54
57
 
@@ -61,8 +64,6 @@ module I18n
61
64
  reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
62
65
  I18n.load_path.keep_if { |p| File.exist?(p) }
63
66
  I18n.load_path |= reloadable_paths.flat_map(&:existent)
64
-
65
- I18n.reload!
66
67
  end
67
68
 
68
69
  app.reloaders << reloader
@@ -74,6 +75,16 @@ module I18n
74
75
  @i18n_inited = true
75
76
  end
76
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
+
77
88
  def self.include_fallbacks_module
78
89
  I18n.backend.class.include(I18n::Backend::Fallbacks)
79
90
  end
@@ -91,10 +102,6 @@ module I18n
91
102
  [I18n.default_locale]
92
103
  end
93
104
 
94
- if args.empty? || args.first.is_a?(Hash)
95
- args.unshift I18n.default_locale
96
- end
97
-
98
105
  I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
99
106
  end
100
107
 
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "concurrent/map"
4
- require "active_support/core_ext/array/prepend_and_append"
5
- require "active_support/core_ext/regexp"
6
4
  require "active_support/i18n"
7
- require "active_support/deprecation"
8
5
 
9
6
  module ActiveSupport
10
7
  module Inflector
@@ -67,8 +64,7 @@ module ActiveSupport
67
64
  @__instance__[locale] ||= new
68
65
  end
69
66
 
70
- attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
71
- deprecate :acronym_regex
67
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
72
68
 
73
69
  attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
74
70
 
@@ -80,7 +76,7 @@ module ActiveSupport
80
76
  # Private, for the test suite.
81
77
  def initialize_dup(orig) # :nodoc:
82
78
  %w(plurals singulars uncountables humans acronyms).each do |scope|
83
- instance_variable_set("@#{scope}", orig.send(scope).dup)
79
+ instance_variable_set("@#{scope}", orig.public_send(scope).dup)
84
80
  end
85
81
  define_acronym_regex_patterns
86
82
  end
@@ -233,7 +229,6 @@ module ActiveSupport
233
229
  end
234
230
 
235
231
  private
236
-
237
232
  def define_acronym_regex_patterns
238
233
  @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
239
234
  @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/inflections"
4
- require "active_support/core_ext/regexp"
4
+ require "active_support/core_ext/object/blank"
5
5
 
6
6
  module ActiveSupport
7
7
  # The Inflector transforms words from singular to plural, class names to table
@@ -74,7 +74,7 @@ module ActiveSupport
74
74
  string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
75
75
  end
76
76
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
77
- string.gsub!("/".freeze, "::".freeze)
77
+ string.gsub!("/", "::")
78
78
  string
79
79
  end
80
80
 
@@ -91,11 +91,11 @@ module ActiveSupport
91
91
  # camelize(underscore('SSLError')) # => "SslError"
92
92
  def underscore(camel_cased_word)
93
93
  return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
94
- word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
95
- word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
96
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
97
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
98
- word.tr!("-".freeze, "_".freeze)
94
+ word = camel_cased_word.to_s.gsub("::", "/")
95
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
96
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
97
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
98
+ word.tr!("-", "_")
99
99
  word.downcase!
100
100
  word
101
101
  end
@@ -131,11 +131,11 @@ module ActiveSupport
131
131
 
132
132
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
133
133
 
134
- result.sub!(/\A_+/, "".freeze)
134
+ result.sub!(/\A_+/, "")
135
135
  unless keep_id_suffix
136
- result.sub!(/_id\z/, "".freeze)
136
+ result.delete_suffix!("_id")
137
137
  end
138
- result.tr!("_".freeze, " ".freeze)
138
+ result.tr!("_", " ")
139
139
 
140
140
  result.gsub!(/([a-z\d]*)/i) do |match|
141
141
  "#{inflections.acronyms[match.downcase] || match.downcase}"
@@ -173,7 +173,7 @@ module ActiveSupport
173
173
  # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
174
174
  # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
175
175
  def titleize(word, keep_id_suffix: false)
176
- humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`])[a-z]/) do |match|
176
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])[a-z]/) do |match|
177
177
  match.capitalize
178
178
  end
179
179
  end
@@ -197,17 +197,17 @@ module ActiveSupport
197
197
  #
198
198
  # Singular names are not handled correctly:
199
199
  #
200
- # classify('calculus') # => "Calculus"
200
+ # classify('calculus') # => "Calculu"
201
201
  def classify(table_name)
202
202
  # strip out any leading schema name
203
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
203
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
204
204
  end
205
205
 
206
206
  # Replaces underscores with dashes in the string.
207
207
  #
208
208
  # dasherize('puni_puni') # => "puni-puni"
209
209
  def dasherize(underscored_word)
210
- underscored_word.tr("_".freeze, "-".freeze)
210
+ underscored_word.tr("_", "-")
211
211
  end
212
212
 
213
213
  # Removes the module part from the expression in the string.
@@ -270,32 +270,36 @@ module ActiveSupport
270
270
  # NameError is raised when the name is not in CamelCase or the constant is
271
271
  # unknown.
272
272
  def constantize(camel_cased_word)
273
- names = camel_cased_word.split("::".freeze)
274
-
275
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
276
- Object.const_get(camel_cased_word) if names.empty?
277
-
278
- # Remove the first blank element in case of '::ClassName' notation.
279
- names.shift if names.size > 1 && names.first.empty?
280
-
281
- names.inject(Object) do |constant, name|
282
- if constant == Object
283
- constant.const_get(name)
284
- else
285
- candidate = constant.const_get(name)
286
- next candidate if constant.const_defined?(name, false)
287
- next candidate unless Object.const_defined?(name)
288
-
289
- # Go down the ancestors to check if it is owned directly. The check
290
- # stops when we reach Object or the end of ancestors tree.
291
- constant = constant.ancestors.inject(constant) do |const, ancestor|
292
- break const if ancestor == Object
293
- break ancestor if ancestor.const_defined?(name, false)
294
- const
273
+ if camel_cased_word.blank? || !camel_cased_word.include?("::")
274
+ Object.const_get(camel_cased_word)
275
+ else
276
+ names = camel_cased_word.split("::")
277
+
278
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
279
+ Object.const_get(camel_cased_word) if names.empty?
280
+
281
+ # Remove the first blank element in case of '::ClassName' notation.
282
+ names.shift if names.size > 1 && names.first.empty?
283
+
284
+ names.inject(Object) do |constant, name|
285
+ if constant == Object
286
+ constant.const_get(name)
287
+ else
288
+ candidate = constant.const_get(name)
289
+ next candidate if constant.const_defined?(name, false)
290
+ next candidate unless Object.const_defined?(name)
291
+
292
+ # Go down the ancestors to check if it is owned directly. The check
293
+ # stops when we reach Object or the end of ancestors tree.
294
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
295
+ break const if ancestor == Object
296
+ break ancestor if ancestor.const_defined?(name, false)
297
+ const
298
+ end
299
+
300
+ # owner is in Object, so raise
301
+ constant.const_get(name, false)
295
302
  end
296
-
297
- # owner is in Object, so raise
298
- constant.const_get(name, false)
299
303
  end
300
304
  end
301
305
  end
@@ -327,10 +331,9 @@ module ActiveSupport
327
331
  rescue NameError => e
328
332
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
329
333
  e.name.to_s == camel_cased_word.to_s)
330
- rescue ArgumentError => e
331
- raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
332
334
  rescue LoadError => e
333
- raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
335
+ message = e.respond_to?(:original_message) ? e.original_message : e.message
336
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message)
334
337
  end
335
338
 
336
339
  # Returns the suffix that should be added to a number to denote the position
@@ -343,18 +346,7 @@ module ActiveSupport
343
346
  # ordinal(-11) # => "th"
344
347
  # ordinal(-1021) # => "st"
345
348
  def ordinal(number)
346
- abs_number = number.to_i.abs
347
-
348
- if (11..13).include?(abs_number % 100)
349
- "th"
350
- else
351
- case abs_number % 10
352
- when 1; "st"
353
- when 2; "nd"
354
- when 3; "rd"
355
- else "th"
356
- end
357
- end
349
+ I18n.translate("number.nth.ordinals", number: number)
358
350
  end
359
351
 
360
352
  # Turns a number into an ordinal string used to denote the position in an
@@ -367,24 +359,23 @@ module ActiveSupport
367
359
  # ordinalize(-11) # => "-11th"
368
360
  # ordinalize(-1021) # => "-1021st"
369
361
  def ordinalize(number)
370
- "#{number}#{ordinal(number)}"
362
+ I18n.translate("number.nth.ordinalized", number: number)
371
363
  end
372
364
 
373
365
  private
374
-
375
366
  # Mounts a regular expression, returned as a string to ease interpolation,
376
367
  # that will match part by part the given constant.
377
368
  #
378
369
  # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
379
370
  # const_regexp("::") # => "::"
380
371
  def const_regexp(camel_cased_word)
381
- parts = camel_cased_word.split("::".freeze)
372
+ parts = camel_cased_word.split("::")
382
373
 
383
374
  return Regexp.escape(camel_cased_word) if parts.blank?
384
375
 
385
376
  last = parts.pop
386
377
 
387
- parts.reverse.inject(last) do |acc, part|
378
+ parts.reverse!.inject(last) do |acc, part|
388
379
  part.empty? ? acc : "#{part}(::#{acc})?"
389
380
  end
390
381
  end