activesupport 6.1.0 → 7.1.5.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.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1075 -325
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +32 -7
  8. data/lib/active_support/benchmarkable.rb +3 -2
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +53 -20
  14. data/lib/active_support/cache/mem_cache_store.rb +201 -62
  15. data/lib/active_support/cache/memory_store.rb +86 -24
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +186 -193
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +63 -71
  20. data/lib/active_support/cache.rb +487 -249
  21. data/lib/active_support/callbacks.rb +227 -105
  22. data/lib/active_support/code_generator.rb +70 -0
  23. data/lib/active_support/concern.rb +9 -7
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/concurrency/share_lock.rb +2 -2
  27. data/lib/active_support/configurable.rb +18 -5
  28. data/lib/active_support/configuration_file.rb +7 -2
  29. data/lib/active_support/core_ext/array/access.rb +1 -5
  30. data/lib/active_support/core_ext/array/conversions.rb +15 -13
  31. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  33. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  34. data/lib/active_support/core_ext/class/subclasses.rb +37 -26
  35. data/lib/active_support/core_ext/date/blank.rb +1 -1
  36. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  37. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  38. data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
  39. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  41. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  42. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  43. data/lib/active_support/core_ext/digest/uuid.rb +30 -13
  44. data/lib/active_support/core_ext/enumerable.rb +85 -83
  45. data/lib/active_support/core_ext/erb/util.rb +196 -0
  46. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  49. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  50. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  51. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  52. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  53. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  54. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
  57. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  58. data/lib/active_support/core_ext/module/delegation.rb +81 -43
  59. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  60. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  61. data/lib/active_support/core_ext/name_error.rb +2 -8
  62. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  63. data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +31 -11
  68. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  69. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  70. data/lib/active_support/core_ext/object/json.rb +49 -27
  71. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  72. data/lib/active_support/core_ext/object/try.rb +20 -20
  73. data/lib/active_support/core_ext/object/with.rb +44 -0
  74. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  75. data/lib/active_support/core_ext/object.rb +1 -0
  76. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  77. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  78. data/lib/active_support/core_ext/pathname.rb +4 -0
  79. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  80. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  81. data/lib/active_support/core_ext/range/each.rb +1 -1
  82. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  83. data/lib/active_support/core_ext/range.rb +1 -2
  84. data/lib/active_support/core_ext/securerandom.rb +25 -13
  85. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  86. data/lib/active_support/core_ext/string/filters.rb +21 -15
  87. data/lib/active_support/core_ext/string/indent.rb +1 -1
  88. data/lib/active_support/core_ext/string/inflections.rb +17 -10
  89. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  90. data/lib/active_support/core_ext/string/output_safety.rb +85 -165
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  92. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +30 -8
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -13
  95. data/lib/active_support/core_ext/time/zones.rb +12 -28
  96. data/lib/active_support/core_ext.rb +2 -1
  97. data/lib/active_support/current_attributes.rb +47 -20
  98. data/lib/active_support/deep_mergeable.rb +53 -0
  99. data/lib/active_support/dependencies/autoload.rb +17 -12
  100. data/lib/active_support/dependencies/interlock.rb +10 -18
  101. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  102. data/lib/active_support/dependencies.rb +58 -788
  103. data/lib/active_support/deprecation/behaviors.rb +66 -40
  104. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  105. data/lib/active_support/deprecation/deprecators.rb +104 -0
  106. data/lib/active_support/deprecation/disallowed.rb +6 -8
  107. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  108. data/lib/active_support/deprecation/method_wrappers.rb +9 -26
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
  110. data/lib/active_support/deprecation/reporting.rb +43 -26
  111. data/lib/active_support/deprecation.rb +32 -5
  112. data/lib/active_support/deprecator.rb +7 -0
  113. data/lib/active_support/descendants_tracker.rb +150 -72
  114. data/lib/active_support/digest.rb +5 -3
  115. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  116. data/lib/active_support/duration/iso8601_serializer.rb +9 -3
  117. data/lib/active_support/duration.rb +83 -52
  118. data/lib/active_support/encrypted_configuration.rb +72 -9
  119. data/lib/active_support/encrypted_file.rb +29 -13
  120. data/lib/active_support/environment_inquirer.rb +23 -3
  121. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  122. data/lib/active_support/error_reporter.rb +203 -0
  123. data/lib/active_support/evented_file_update_checker.rb +20 -7
  124. data/lib/active_support/execution_context/test_helper.rb +13 -0
  125. data/lib/active_support/execution_context.rb +53 -0
  126. data/lib/active_support/execution_wrapper.rb +44 -22
  127. data/lib/active_support/executor/test_helper.rb +7 -0
  128. data/lib/active_support/file_update_checker.rb +4 -2
  129. data/lib/active_support/fork_tracker.rb +28 -11
  130. data/lib/active_support/gem_version.rb +4 -4
  131. data/lib/active_support/gzip.rb +2 -0
  132. data/lib/active_support/hash_with_indifferent_access.rb +44 -19
  133. data/lib/active_support/html_safe_translation.rb +53 -0
  134. data/lib/active_support/i18n.rb +2 -1
  135. data/lib/active_support/i18n_railtie.rb +21 -14
  136. data/lib/active_support/inflector/inflections.rb +25 -7
  137. data/lib/active_support/inflector/methods.rb +50 -64
  138. data/lib/active_support/inflector/transliterate.rb +4 -2
  139. data/lib/active_support/isolated_execution_state.rb +76 -0
  140. data/lib/active_support/json/decoding.rb +2 -1
  141. data/lib/active_support/json/encoding.rb +27 -45
  142. data/lib/active_support/key_generator.rb +31 -6
  143. data/lib/active_support/lazy_load_hooks.rb +33 -7
  144. data/lib/active_support/locale/en.yml +4 -2
  145. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  146. data/lib/active_support/log_subscriber.rb +97 -35
  147. data/lib/active_support/logger.rb +9 -60
  148. data/lib/active_support/logger_thread_safe_level.rb +11 -34
  149. data/lib/active_support/message_encryptor.rb +206 -56
  150. data/lib/active_support/message_encryptors.rb +141 -0
  151. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  152. data/lib/active_support/message_pack/extensions.rb +292 -0
  153. data/lib/active_support/message_pack/serializer.rb +63 -0
  154. data/lib/active_support/message_pack.rb +50 -0
  155. data/lib/active_support/message_verifier.rb +235 -84
  156. data/lib/active_support/message_verifiers.rb +135 -0
  157. data/lib/active_support/messages/codec.rb +65 -0
  158. data/lib/active_support/messages/metadata.rb +112 -46
  159. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  160. data/lib/active_support/messages/rotator.rb +34 -32
  161. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  162. data/lib/active_support/multibyte/chars.rb +12 -11
  163. data/lib/active_support/multibyte/unicode.rb +9 -49
  164. data/lib/active_support/multibyte.rb +1 -1
  165. data/lib/active_support/notifications/fanout.rb +304 -114
  166. data/lib/active_support/notifications/instrumenter.rb +117 -35
  167. data/lib/active_support/notifications.rb +25 -25
  168. data/lib/active_support/number_helper/number_converter.rb +14 -7
  169. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  170. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  171. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
  172. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  173. data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
  174. data/lib/active_support/number_helper/rounding_helper.rb +2 -6
  175. data/lib/active_support/number_helper.rb +379 -319
  176. data/lib/active_support/option_merger.rb +10 -18
  177. data/lib/active_support/ordered_hash.rb +4 -4
  178. data/lib/active_support/ordered_options.rb +15 -1
  179. data/lib/active_support/parameter_filter.rb +105 -81
  180. data/lib/active_support/proxy_object.rb +2 -0
  181. data/lib/active_support/railtie.rb +83 -21
  182. data/lib/active_support/reloader.rb +13 -5
  183. data/lib/active_support/rescuable.rb +18 -16
  184. data/lib/active_support/ruby_features.rb +7 -0
  185. data/lib/active_support/secure_compare_rotator.rb +18 -11
  186. data/lib/active_support/security_utils.rb +1 -1
  187. data/lib/active_support/string_inquirer.rb +3 -3
  188. data/lib/active_support/subscriber.rb +11 -40
  189. data/lib/active_support/syntax_error_proxy.rb +60 -0
  190. data/lib/active_support/tagged_logging.rb +65 -25
  191. data/lib/active_support/test_case.rb +166 -27
  192. data/lib/active_support/testing/assertions.rb +61 -15
  193. data/lib/active_support/testing/autorun.rb +0 -2
  194. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  195. data/lib/active_support/testing/deprecation.rb +53 -2
  196. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  197. data/lib/active_support/testing/isolation.rb +30 -29
  198. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  199. data/lib/active_support/testing/parallelization/server.rb +4 -0
  200. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  201. data/lib/active_support/testing/parallelization.rb +4 -0
  202. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  203. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  204. data/lib/active_support/testing/stream.rb +4 -6
  205. data/lib/active_support/testing/strict_warnings.rb +39 -0
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +49 -16
  208. data/lib/active_support/time_with_zone.rb +39 -28
  209. data/lib/active_support/values/time_zone.rb +50 -18
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +4 -11
  212. data/lib/active_support/xml_mini/libxml.rb +5 -5
  213. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  214. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  215. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  216. data/lib/active_support/xml_mini/rexml.rb +2 -2
  217. data/lib/active_support/xml_mini.rb +7 -6
  218. data/lib/active_support.rb +28 -1
  219. metadata +150 -18
  220. data/lib/active_support/core_ext/marshal.rb +0 -26
  221. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
  222. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  223. data/lib/active_support/core_ext/uri.rb +0 -29
  224. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
  225. data/lib/active_support/per_thread_registry.rb +0 -60
@@ -3,8 +3,11 @@
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
9
+ # = \Hash With Indifferent Access
10
+ #
8
11
  # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
9
12
  # to be the same.
10
13
  #
@@ -36,7 +39,7 @@ module ActiveSupport
36
39
  #
37
40
  # but this class is intended for use cases where strings or symbols are the
38
41
  # expected keys and it is convenient to understand both as the same. For
39
- # example the +params+ hash in Ruby on Rails.
42
+ # example the +params+ hash in Ruby on \Rails.
40
43
  #
41
44
  # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
42
45
  #
@@ -44,7 +47,7 @@ module ActiveSupport
44
47
  #
45
48
  # which may be handy.
46
49
  #
47
- # To access this class outside of Rails, require the core extension with:
50
+ # To access this class outside of \Rails, require the core extension with:
48
51
  #
49
52
  # require "active_support/core_ext/hash/indifferent_access"
50
53
  #
@@ -64,7 +67,7 @@ module ActiveSupport
64
67
  self
65
68
  end
66
69
 
67
- def initialize(constructor = {})
70
+ def initialize(constructor = nil)
68
71
  if constructor.respond_to?(:to_hash)
69
72
  super()
70
73
  update(constructor)
@@ -72,6 +75,8 @@ module ActiveSupport
72
75
  hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
73
76
  self.default = hash.default if hash.default
74
77
  self.default_proc = hash.default_proc if hash.default_proc
78
+ elsif constructor.nil?
79
+ super()
75
80
  else
76
81
  super(constructor)
77
82
  end
@@ -110,10 +115,10 @@ module ActiveSupport
110
115
  # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 }
111
116
  #
112
117
  # The arguments can be either an
113
- # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
118
+ # +ActiveSupport::HashWithIndifferentAccess+ or a regular +Hash+.
114
119
  # In either case the merge respects the semantics of indifferent access.
115
120
  #
116
- # If the argument is a regular hash with keys +:key+ and +"key"+ only one
121
+ # If the argument is a regular hash with keys +:key+ and <tt>"key"</tt> only one
117
122
  # of the values end up in the receiver, but which one is unspecified.
118
123
  #
119
124
  # When given a block, the value for duplicated keys will be determined
@@ -215,8 +220,12 @@ module ActiveSupport
215
220
  # hash.default # => nil
216
221
  # hash.default('foo') # => 'foo'
217
222
  # hash.default(:foo) # => 'foo'
218
- def default(*args)
219
- super(*args.map { |arg| convert_key(arg) })
223
+ def default(key = (no_key = true))
224
+ if no_key
225
+ super()
226
+ else
227
+ super(convert_key(key))
228
+ end
220
229
  end
221
230
 
222
231
  # Returns an array of the values at the specified indices:
@@ -226,7 +235,8 @@ module ActiveSupport
226
235
  # hash[:b] = 'y'
227
236
  # hash.values_at('a', 'b') # => ["x", "y"]
228
237
  def values_at(*keys)
229
- super(*keys.map { |key| convert_key(key) })
238
+ keys.map! { |key| convert_key(key) }
239
+ super
230
240
  end
231
241
 
232
242
  # Returns an array of the values at the specified indices, but also
@@ -239,7 +249,8 @@ module ActiveSupport
239
249
  # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
240
250
  # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
241
251
  def fetch_values(*indices, &block)
242
- super(*indices.map { |key| convert_key(key) }, &block)
252
+ indices.map! { |key| convert_key(key) }
253
+ super
243
254
  end
244
255
 
245
256
  # Returns a shallow copy of the hash.
@@ -293,8 +304,12 @@ module ActiveSupport
293
304
  super(convert_key(key))
294
305
  end
295
306
 
307
+ # Returns a hash with indifferent access that includes everything except given keys.
308
+ # hash = { a: "x", b: "y", c: 10 }.with_indifferent_access
309
+ # hash.except(:a, "b") # => {c: 10}.with_indifferent_access
310
+ # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access
296
311
  def except(*keys)
297
- slice(*self.keys - keys.map { |key| convert_key(key) })
312
+ dup.except!(*keys)
298
313
  end
299
314
  alias_method :without, :except
300
315
 
@@ -319,21 +334,31 @@ module ActiveSupport
319
334
  dup.tap { |hash| hash.reject!(*args, &block) }
320
335
  end
321
336
 
322
- def transform_values(*args, &block)
337
+ def transform_values(&block)
323
338
  return to_enum(:transform_values) unless block_given?
324
- dup.tap { |hash| hash.transform_values!(*args, &block) }
339
+ dup.tap { |hash| hash.transform_values!(&block) }
325
340
  end
326
341
 
327
- def transform_keys(*args, &block)
328
- return to_enum(:transform_keys) unless block_given?
329
- dup.tap { |hash| hash.transform_keys!(*args, &block) }
342
+ NOT_GIVEN = Object.new # :nodoc:
343
+
344
+ def transform_keys(hash = NOT_GIVEN, &block)
345
+ return to_enum(:transform_keys) if NOT_GIVEN.equal?(hash) && !block_given?
346
+ dup.tap { |h| h.transform_keys!(hash, &block) }
330
347
  end
331
348
 
332
- def transform_keys!
333
- return enum_for(:transform_keys!) { size } unless block_given?
334
- keys.each do |key|
335
- self[yield(key)] = delete(key)
349
+ def transform_keys!(hash = NOT_GIVEN, &block)
350
+ return to_enum(:transform_keys!) if NOT_GIVEN.equal?(hash) && !block_given?
351
+
352
+ if hash.nil?
353
+ super
354
+ elsif NOT_GIVEN.equal?(hash)
355
+ keys.each { |key| self[yield(key)] = delete(key) }
356
+ elsif block_given?
357
+ keys.each { |key| self[hash[key] || yield(key)] = delete(key) }
358
+ else
359
+ keys.each { |key| self[hash[key] || key] = delete(key) }
336
360
  end
361
+
337
362
  self
338
363
  end
339
364
 
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module HtmlSafeTranslation # :nodoc:
5
+ extend self
6
+
7
+ def translate(key, **options)
8
+ if html_safe_translation_key?(key)
9
+ html_safe_options = html_escape_translation_options(options)
10
+
11
+ exception = false
12
+ exception_handler = ->(*args) do
13
+ exception = true
14
+ I18n.exception_handler.call(*args)
15
+ end
16
+ translation = I18n.translate(key, **html_safe_options, exception_handler: exception_handler)
17
+ if exception
18
+ translation
19
+ else
20
+ html_safe_translation(translation)
21
+ end
22
+ else
23
+ I18n.translate(key, **options)
24
+ end
25
+ end
26
+
27
+ def html_safe_translation_key?(key)
28
+ /(?:_|\b)html\z/.match?(key)
29
+ end
30
+
31
+ private
32
+ def html_escape_translation_options(options)
33
+ options.each do |name, value|
34
+ unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
35
+ options[name] = ERB::Util.html_escape(value.to_s)
36
+ end
37
+ end
38
+ end
39
+
40
+ def i18n_option?(name)
41
+ (@i18n_option_names ||= I18n::RESERVED_KEYS.to_set).include?(name)
42
+ end
43
+
44
+
45
+ def html_safe_translation(translation)
46
+ if translation.respond_to?(:map)
47
+ translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
48
+ else
49
+ translation.respond_to?(:html_safe) ? translation.html_safe : translation
50
+ end
51
+ end
52
+ end
53
+ end
@@ -5,8 +5,9 @@ require "active_support/core_ext/hash/except"
5
5
  require "active_support/core_ext/hash/slice"
6
6
  begin
7
7
  require "i18n"
8
+ require "i18n/backend/fallbacks"
8
9
  rescue LoadError => e
9
- $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
10
+ warn "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
10
11
  raise e
11
12
  end
12
13
  require "active_support/lazy_load_hooks"
@@ -49,7 +49,7 @@ module I18n
49
49
  when :load_path
50
50
  I18n.load_path += value
51
51
  when :raise_on_missing_translations
52
- forward_raise_on_missing_translations_config(app)
52
+ setup_raise_on_missing_translations_config(app)
53
53
  else
54
54
  I18n.public_send("#{setting}=", value)
55
55
  end
@@ -60,28 +60,35 @@ module I18n
60
60
  # Restore available locales check so it will take place from now on.
61
61
  I18n.enforce_available_locales = enforce_available_locales
62
62
 
63
- directories = watched_dirs_with_extensions(reloadable_paths)
64
- reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
65
- I18n.load_path.keep_if { |p| File.exist?(p) }
66
- I18n.load_path |= reloadable_paths.flat_map(&:existent)
67
- end
63
+ if app.config.reloading_enabled?
64
+ directories = watched_dirs_with_extensions(reloadable_paths)
65
+ reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
66
+ I18n.load_path.keep_if { |p| File.exist?(p) }
67
+ I18n.load_path |= reloadable_paths.flat_map(&:existent)
68
+ end
68
69
 
69
- app.reloaders << reloader
70
- app.reloader.to_run do
71
- reloader.execute_if_updated { require_unload_lock! }
70
+ app.reloaders << reloader
71
+ app.reloader.to_run do
72
+ reloader.execute_if_updated { require_unload_lock! }
73
+ end
74
+ reloader.execute
72
75
  end
73
- reloader.execute
74
76
 
75
77
  @i18n_inited = true
76
78
  end
77
79
 
78
- def self.forward_raise_on_missing_translations_config(app)
80
+ def self.setup_raise_on_missing_translations_config(app)
79
81
  ActiveSupport.on_load(:action_view) do
80
- self.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
82
+ ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
81
83
  end
82
84
 
83
- ActiveSupport.on_load(:action_controller) do
84
- AbstractController::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
85
+ if app.config.i18n.raise_on_missing_translations &&
86
+ I18n.exception_handler.is_a?(I18n::ExceptionHandler) # Only override the i18n gem's default exception handler.
87
+
88
+ I18n.exception_handler = ->(exception, *) {
89
+ exception = exception.to_exception if exception.is_a?(I18n::MissingTranslation)
90
+ raise exception
91
+ }
85
92
  end
86
93
  end
87
94
 
@@ -7,6 +7,8 @@ module ActiveSupport
7
7
  module Inflector
8
8
  extend self
9
9
 
10
+ # = Active Support \Inflections
11
+ #
10
12
  # A singleton instance of this class is yielded by Inflector.inflections,
11
13
  # which can then be used to specify additional inflection rules. If passed
12
14
  # an optional locale, rules for other languages can be specified. The
@@ -16,13 +18,13 @@ module ActiveSupport
16
18
  # inflect.plural /^(ox)$/i, '\1\2en'
17
19
  # inflect.singular /^(ox)en/i, '\1'
18
20
  #
19
- # inflect.irregular 'octopus', 'octopi'
21
+ # inflect.irregular 'cactus', 'cacti'
20
22
  #
21
23
  # inflect.uncountable 'equipment'
22
24
  # end
23
25
  #
24
26
  # New rules are added at the top. So in the example above, the irregular
25
- # rule for octopus will now be the first of the pluralization and
27
+ # rule for cactus will now be the first of the pluralization and
26
28
  # singularization rules that is runs. This guarantees that your rules run
27
29
  # before any of the rules that may already have been loaded.
28
30
  class Inflections
@@ -64,6 +66,13 @@ module ActiveSupport
64
66
  @__instance__[locale] ||= new
65
67
  end
66
68
 
69
+ def self.instance_or_fallback(locale)
70
+ I18n.fallbacks[locale].each do |k|
71
+ return @__instance__[k] if @__instance__.key?(k)
72
+ end
73
+ instance(locale)
74
+ end
75
+
67
76
  attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
68
77
 
69
78
  attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
@@ -160,7 +169,7 @@ module ActiveSupport
160
169
  # regular expressions. You simply pass the irregular in singular and
161
170
  # plural form.
162
171
  #
163
- # irregular 'octopus', 'octopi'
172
+ # irregular 'cactus', 'cacti'
164
173
  # irregular 'person', 'people'
165
174
  def irregular(singular, plural)
166
175
  @uncountables.delete(singular)
@@ -215,15 +224,24 @@ module ActiveSupport
215
224
  # Clears the loaded inflections within a given scope (default is
216
225
  # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
217
226
  # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
218
- # <tt>:humans</tt>.
227
+ # <tt>:humans</tt>, <tt>:acronyms</tt>.
219
228
  #
220
229
  # clear :all
221
230
  # clear :plurals
222
231
  def clear(scope = :all)
223
232
  case scope
224
233
  when :all
225
- @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
226
- else
234
+ clear(:acronyms)
235
+ clear(:plurals)
236
+ clear(:singulars)
237
+ clear(:uncountables)
238
+ clear(:humans)
239
+ when :acronyms
240
+ @acronyms = {}
241
+ define_acronym_regex_patterns
242
+ when :uncountables
243
+ @uncountables = Uncountables.new
244
+ when :plurals, :singulars, :humans
227
245
  instance_variable_set "@#{scope}", []
228
246
  end
229
247
  end
@@ -248,7 +266,7 @@ module ActiveSupport
248
266
  if block_given?
249
267
  yield Inflections.instance(locale)
250
268
  else
251
- Inflections.instance(locale)
269
+ Inflections.instance_or_fallback(locale)
252
270
  end
253
271
  end
254
272
  end
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/inflections"
4
- require "active_support/core_ext/object/blank"
5
4
 
6
5
  module ActiveSupport
6
+ # = Active Support \Inflector
7
+ #
7
8
  # The Inflector transforms words from singular to plural, class names to table
8
9
  # names, modularized class names to ones without, and class names to foreign
9
10
  # keys. The default inflections for pluralization, singularization, and
10
11
  # uncountable words are kept in inflections.rb.
11
12
  #
12
- # The Rails core team has stated patches for the inflections library will not
13
+ # The \Rails core team has stated patches for the inflections library will not
13
14
  # be accepted in order to avoid breaking legacy applications which may be
14
15
  # relying on errant inflections. If you discover an incorrect inflection and
15
16
  # require it for your application or wish to define rules for languages other
@@ -68,13 +69,19 @@ module ActiveSupport
68
69
  # camelize(underscore('SSLError')) # => "SslError"
69
70
  def camelize(term, uppercase_first_letter = true)
70
71
  string = term.to_s
71
- if uppercase_first_letter
72
- string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
72
+ # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
73
+ if !uppercase_first_letter || uppercase_first_letter == :lower
74
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
75
+ elsif string.match?(/\A[a-z\d]*\z/)
76
+ return inflections.acronyms[string]&.dup || string.capitalize
73
77
  else
74
- string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
78
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
79
+ end
80
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
81
+ word = $2
82
+ substituted = inflections.acronyms[word] || word.capitalize! || word
83
+ $1 ? "::#{substituted}" : substituted
75
84
  end
76
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
77
- string.gsub!("/", "::")
78
85
  string
79
86
  end
80
87
 
@@ -90,11 +97,10 @@ module ActiveSupport
90
97
  #
91
98
  # camelize(underscore('SSLError')) # => "SslError"
92
99
  def underscore(camel_cased_word)
93
- return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
100
+ return camel_cased_word.to_s.dup unless /[A-Z-]|::/.match?(camel_cased_word)
94
101
  word = camel_cased_word.to_s.gsub("::", "/")
95
102
  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')
103
+ word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
98
104
  word.tr!("-", "_")
99
105
  word.downcase!
100
106
  word
@@ -106,7 +112,7 @@ module ActiveSupport
106
112
  #
107
113
  # * Applies human inflection rules to the argument.
108
114
  # * Deletes leading underscores, if any.
109
- # * Removes a "_id" suffix if present.
115
+ # * Removes an "_id" suffix if present.
110
116
  # * Replaces underscores with spaces, if any.
111
117
  # * Downcases all words except acronyms.
112
118
  # * Capitalizes the first word.
@@ -120,7 +126,7 @@ module ActiveSupport
120
126
  # humanize('author_id') # => "Author"
121
127
  # humanize('author_id', capitalize: false) # => "author"
122
128
  # humanize('_id') # => "Id"
123
- # humanize('author_id', keep_id_suffix: true) # => "Author Id"
129
+ # humanize('author_id', keep_id_suffix: true) # => "Author id"
124
130
  #
125
131
  # If "SSL" was defined to be an acronym:
126
132
  #
@@ -131,42 +137,53 @@ module ActiveSupport
131
137
 
132
138
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
133
139
 
134
- result.sub!(/\A_+/, "")
135
- unless keep_id_suffix
136
- result.delete_suffix!("_id")
137
- end
138
140
  result.tr!("_", " ")
141
+ result.lstrip!
142
+ if !keep_id_suffix && lower_case_and_underscored_word&.end_with?("_id")
143
+ result.delete_suffix!(" id")
144
+ end
139
145
 
140
- result.gsub!(/([a-z\d]*)/i) do |match|
141
- "#{inflections.acronyms[match.downcase] || match.downcase}"
146
+ result.gsub!(/([a-z\d]+)/i) do |match|
147
+ match.downcase!
148
+ inflections.acronyms[match] || match
142
149
  end
143
150
 
144
151
  if capitalize
145
- result.sub!(/\A\w/) { |match| match.upcase }
152
+ result.sub!(/\A\w/) do |match|
153
+ match.upcase!
154
+ match
155
+ end
146
156
  end
147
157
 
148
158
  result
149
159
  end
150
160
 
151
- # Converts just the first character to uppercase.
161
+ # Converts the first character in the string to uppercase.
152
162
  #
153
163
  # upcase_first('what a Lovely Day') # => "What a Lovely Day"
154
164
  # upcase_first('w') # => "W"
155
165
  # upcase_first('') # => ""
156
166
  def upcase_first(string)
157
- string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
167
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
168
+ end
169
+
170
+ # Converts the first character in the string to lowercase.
171
+ #
172
+ # downcase_first('If they enjoyed The Matrix') # => "if they enjoyed The Matrix"
173
+ # downcase_first('I') # => "i"
174
+ # downcase_first('') # => ""
175
+ def downcase_first(string)
176
+ string.length > 0 ? string[0].downcase.concat(string[1..-1]) : +""
158
177
  end
159
178
 
160
179
  # Capitalizes all the words and replaces some characters in the string to
161
180
  # create a nicer looking title. +titleize+ is meant for creating pretty
162
- # output. It is not used in the Rails internals.
181
+ # output. It is not used in the \Rails internals.
163
182
  #
164
183
  # The trailing '_id','Id'.. can be kept and capitalized by setting the
165
184
  # optional parameter +keep_id_suffix+ to true.
166
185
  # By default, this parameter is false.
167
186
  #
168
- # +titleize+ is also aliased as +titlecase+.
169
- #
170
187
  # titleize('man from the boondocks') # => "Man From The Boondocks"
171
188
  # titleize('x-men: the last stand') # => "X Men: The Last Stand"
172
189
  # titleize('TheManWithoutAPast') # => "The Man Without A Past"
@@ -178,7 +195,7 @@ module ActiveSupport
178
195
  end
179
196
  end
180
197
 
181
- # Creates the name of a table like Rails does for models to table names.
198
+ # Creates the name of a table like \Rails does for models to table names.
182
199
  # This method uses the #pluralize method on the last word in the string.
183
200
  #
184
201
  # tableize('RawScaledScorer') # => "raw_scaled_scorers"
@@ -188,9 +205,9 @@ module ActiveSupport
188
205
  pluralize(underscore(class_name))
189
206
  end
190
207
 
191
- # Creates a class name from a plural table name like Rails does for table
192
- # names to models. Note that this returns a string and not a Class (To
193
- # convert to an actual class follow +classify+ with #constantize).
208
+ # Creates a class name from a plural table name like \Rails does for table
209
+ # names to models. Note that this returns a string and not a Class. (To
210
+ # convert to an actual class follow +classify+ with #constantize.)
194
211
  #
195
212
  # classify('ham_and_eggs') # => "HamAndEgg"
196
213
  # classify('posts') # => "Post"
@@ -221,7 +238,7 @@ module ActiveSupport
221
238
  def demodulize(path)
222
239
  path = path.to_s
223
240
  if i = path.rindex("::")
224
- path[(i + 2)..-1]
241
+ path[(i + 2), path.length]
225
242
  else
226
243
  path
227
244
  end
@@ -270,38 +287,7 @@ module ActiveSupport
270
287
  # NameError is raised when the name is not in CamelCase or the constant is
271
288
  # unknown.
272
289
  def constantize(camel_cased_word)
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)
302
- end
303
- end
304
- end
290
+ Object.const_get(camel_cased_word)
305
291
  end
306
292
 
307
293
  # Tries to find a constant with the name specified in the argument string.
@@ -371,7 +357,7 @@ module ActiveSupport
371
357
  def const_regexp(camel_cased_word)
372
358
  parts = camel_cased_word.split("::")
373
359
 
374
- return Regexp.escape(camel_cased_word) if parts.blank?
360
+ return Regexp.escape(camel_cased_word) if parts.empty?
375
361
 
376
362
  last = parts.pop
377
363
 
@@ -385,8 +371,8 @@ module ActiveSupport
385
371
  # If passed an optional +locale+ parameter, the uncountables will be
386
372
  # found for that locale.
387
373
  #
388
- # apply_inflections('post', inflections.plurals, :en) # => "posts"
389
- # apply_inflections('posts', inflections.singulars, :en) # => "post"
374
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
375
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
390
376
  def apply_inflections(word, rules, locale = :en)
391
377
  result = word.to_s.dup
392
378
 
@@ -59,13 +59,15 @@ module ActiveSupport
59
59
  # transliterate('Jürgen', locale: :de)
60
60
  # # => "Juergen"
61
61
  #
62
- # Transliteration is restricted to UTF-8, US-ASCII and GB18030 strings
62
+ # Transliteration is restricted to UTF-8, US-ASCII, and GB18030 strings.
63
63
  # Other encodings will raise an ArgumentError.
64
64
  def transliterate(string, replacement = "?", locale: nil)
65
- string = string.dup if string.frozen?
66
65
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
67
66
  raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
68
67
 
68
+ return string.dup if string.ascii_only?
69
+ string = string.dup if string.frozen?
70
+
69
71
  input_encoding = string.encoding
70
72
 
71
73
  # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fiber"
4
+
5
+ module ActiveSupport
6
+ module IsolatedExecutionState # :nodoc:
7
+ @isolation_level = nil
8
+
9
+ Thread.attr_accessor :active_support_execution_state
10
+ Fiber.attr_accessor :active_support_execution_state
11
+
12
+ class << self
13
+ attr_reader :isolation_level, :scope
14
+
15
+ def isolation_level=(level)
16
+ return if level == @isolation_level
17
+
18
+ unless %i(thread fiber).include?(level)
19
+ raise ArgumentError, "isolation_level must be `:thread` or `:fiber`, got: `#{level.inspect}`"
20
+ end
21
+
22
+ clear if @isolation_level
23
+
24
+ @scope =
25
+ case level
26
+ when :thread; Thread
27
+ when :fiber; Fiber
28
+ end
29
+
30
+ @isolation_level = level
31
+ end
32
+
33
+ def unique_id
34
+ self[:__id__] ||= Object.new
35
+ end
36
+
37
+ def [](key)
38
+ state[key]
39
+ end
40
+
41
+ def []=(key, value)
42
+ state[key] = value
43
+ end
44
+
45
+ def key?(key)
46
+ state.key?(key)
47
+ end
48
+
49
+ def delete(key)
50
+ state.delete(key)
51
+ end
52
+
53
+ def clear
54
+ state.clear
55
+ end
56
+
57
+ def context
58
+ scope.current
59
+ end
60
+
61
+ def share_with(other)
62
+ # Action Controller streaming spawns a new thread and copy thread locals.
63
+ # We do the same here for backward compatibility, but this is very much a hack
64
+ # and streaming should be rethought.
65
+ context.active_support_execution_state = other.active_support_execution_state.dup
66
+ end
67
+
68
+ private
69
+ def state
70
+ context.active_support_execution_state ||= {}
71
+ end
72
+ end
73
+
74
+ self.isolation_level = :thread
75
+ end
76
+ end