activesupport 5.2.4.3 → 7.0.3

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +244 -459
  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 +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +31 -5
  8. data/lib/active_support/benchmarkable.rb +3 -3
  9. data/lib/active_support/cache/file_store.rb +47 -41
  10. data/lib/active_support/cache/mem_cache_store.rb +151 -40
  11. data/lib/active_support/cache/memory_store.rb +68 -34
  12. data/lib/active_support/cache/null_store.rb +16 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +103 -101
  14. data/lib/active_support/cache/strategy/local_cache.rb +56 -64
  15. data/lib/active_support/cache.rb +333 -116
  16. data/lib/active_support/callbacks.rb +244 -128
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +72 -5
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  20. data/lib/active_support/concurrency/share_lock.rb +2 -3
  21. data/lib/active_support/configurable.rb +15 -16
  22. data/lib/active_support/configuration_file.rb +51 -0
  23. data/lib/active_support/core_ext/array/access.rb +15 -7
  24. data/lib/active_support/core_ext/array/conversions.rb +18 -17
  25. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  26. data/lib/active_support/core_ext/array/extract.rb +21 -0
  27. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  28. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  29. data/lib/active_support/core_ext/array.rb +2 -1
  30. data/lib/active_support/core_ext/benchmark.rb +2 -2
  31. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  32. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  33. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  34. data/lib/active_support/core_ext/date/blank.rb +1 -1
  35. data/lib/active_support/core_ext/date/calculations.rb +15 -14
  36. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  37. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  38. data/lib/active_support/core_ext/date.rb +1 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  42. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  43. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  44. data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
  45. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  46. data/lib/active_support/core_ext/date_time.rb +1 -0
  47. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  48. data/lib/active_support/core_ext/enumerable.rb +241 -76
  49. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  50. data/lib/active_support/core_ext/hash/conversions.rb +3 -4
  51. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  52. data/lib/active_support/core_ext/hash/except.rb +2 -2
  53. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  54. data/lib/active_support/core_ext/hash/keys.rb +2 -31
  55. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  56. data/lib/active_support/core_ext/hash.rb +1 -2
  57. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  58. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  59. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  60. data/lib/active_support/core_ext/kernel.rb +0 -1
  61. data/lib/active_support/core_ext/load_error.rb +1 -1
  62. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  63. data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
  64. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
  65. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  66. data/lib/active_support/core_ext/module/delegation.rb +70 -33
  67. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  68. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  69. data/lib/active_support/core_ext/module.rb +0 -1
  70. data/lib/active_support/core_ext/name_error.rb +23 -2
  71. data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
  72. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  73. data/lib/active_support/core_ext/numeric.rb +1 -1
  74. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  75. data/lib/active_support/core_ext/object/blank.rb +3 -4
  76. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  77. data/lib/active_support/core_ext/object/duplicable.rb +14 -110
  78. data/lib/active_support/core_ext/object/json.rb +44 -27
  79. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  80. data/lib/active_support/core_ext/object/try.rb +24 -14
  81. data/lib/active_support/core_ext/object/with_options.rb +21 -2
  82. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  83. data/lib/active_support/core_ext/pathname.rb +3 -0
  84. data/lib/active_support/core_ext/range/compare_range.rb +23 -27
  85. data/lib/active_support/core_ext/range/conversions.rb +32 -30
  86. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  87. data/lib/active_support/core_ext/range/each.rb +1 -2
  88. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  89. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  90. data/lib/active_support/core_ext/range.rb +1 -1
  91. data/lib/active_support/core_ext/regexp.rb +8 -5
  92. data/lib/active_support/core_ext/securerandom.rb +23 -3
  93. data/lib/active_support/core_ext/string/access.rb +5 -16
  94. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  95. data/lib/active_support/core_ext/string/filters.rb +42 -1
  96. data/lib/active_support/core_ext/string/inflections.rb +46 -7
  97. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  98. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  99. data/lib/active_support/core_ext/string/output_safety.rb +129 -20
  100. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  101. data/lib/active_support/core_ext/string/strip.rb +3 -1
  102. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  103. data/lib/active_support/core_ext/symbol.rb +3 -0
  104. data/lib/active_support/core_ext/time/calculations.rb +59 -10
  105. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  106. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  107. data/lib/active_support/core_ext/time/zones.rb +7 -22
  108. data/lib/active_support/core_ext/time.rb +1 -0
  109. data/lib/active_support/core_ext/uri.rb +3 -22
  110. data/lib/active_support/core_ext.rb +2 -1
  111. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  112. data/lib/active_support/current_attributes.rb +47 -16
  113. data/lib/active_support/dependencies/interlock.rb +10 -18
  114. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  115. data/lib/active_support/dependencies.rb +60 -715
  116. data/lib/active_support/deprecation/behaviors.rb +21 -5
  117. data/lib/active_support/deprecation/disallowed.rb +56 -0
  118. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  119. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +31 -8
  121. data/lib/active_support/deprecation/reporting.rb +50 -7
  122. data/lib/active_support/deprecation.rb +7 -2
  123. data/lib/active_support/descendants_tracker.rb +190 -34
  124. data/lib/active_support/digest.rb +5 -3
  125. data/lib/active_support/duration/iso8601_parser.rb +5 -7
  126. data/lib/active_support/duration/iso8601_serializer.rb +27 -15
  127. data/lib/active_support/duration.rb +149 -67
  128. data/lib/active_support/encrypted_configuration.rb +12 -5
  129. data/lib/active_support/encrypted_file.rb +23 -5
  130. data/lib/active_support/environment_inquirer.rb +20 -0
  131. data/lib/active_support/error_reporter.rb +117 -0
  132. data/lib/active_support/evented_file_update_checker.rb +85 -122
  133. data/lib/active_support/execution_context/test_helper.rb +13 -0
  134. data/lib/active_support/execution_context.rb +53 -0
  135. data/lib/active_support/execution_wrapper.rb +44 -21
  136. data/lib/active_support/executor/test_helper.rb +7 -0
  137. data/lib/active_support/file_update_checker.rb +0 -1
  138. data/lib/active_support/fork_tracker.rb +71 -0
  139. data/lib/active_support/gem_version.rb +5 -5
  140. data/lib/active_support/hash_with_indifferent_access.rb +73 -43
  141. data/lib/active_support/html_safe_translation.rb +43 -0
  142. data/lib/active_support/i18n.rb +2 -0
  143. data/lib/active_support/i18n_railtie.rb +15 -8
  144. data/lib/active_support/inflector/inflections.rb +25 -14
  145. data/lib/active_support/inflector/methods.rb +38 -71
  146. data/lib/active_support/inflector/transliterate.rb +47 -18
  147. data/lib/active_support/isolated_execution_state.rb +72 -0
  148. data/lib/active_support/json/decoding.rb +25 -26
  149. data/lib/active_support/json/encoding.rb +14 -6
  150. data/lib/active_support/key_generator.rb +23 -38
  151. data/lib/active_support/lazy_load_hooks.rb +19 -5
  152. data/lib/active_support/locale/en.rb +33 -0
  153. data/lib/active_support/locale/en.yml +8 -4
  154. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  155. data/lib/active_support/log_subscriber.rb +51 -11
  156. data/lib/active_support/logger.rb +6 -22
  157. data/lib/active_support/logger_silence.rb +11 -19
  158. data/lib/active_support/logger_thread_safe_level.rb +45 -10
  159. data/lib/active_support/message_encryptor.rb +20 -19
  160. data/lib/active_support/message_verifier.rb +53 -21
  161. data/lib/active_support/messages/metadata.rb +13 -4
  162. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  163. data/lib/active_support/messages/rotator.rb +10 -9
  164. data/lib/active_support/multibyte/chars.rb +17 -76
  165. data/lib/active_support/multibyte/unicode.rb +7 -331
  166. data/lib/active_support/multibyte.rb +1 -1
  167. data/lib/active_support/notifications/fanout.rb +163 -37
  168. data/lib/active_support/notifications/instrumenter.rb +90 -11
  169. data/lib/active_support/notifications.rb +88 -30
  170. data/lib/active_support/number_helper/number_converter.rb +6 -9
  171. data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
  172. data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
  173. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  174. data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -4
  175. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  176. data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
  177. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  178. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  179. data/lib/active_support/number_helper.rb +36 -12
  180. data/lib/active_support/option_merger.rb +15 -4
  181. data/lib/active_support/ordered_hash.rb +2 -2
  182. data/lib/active_support/ordered_options.rb +14 -4
  183. data/lib/active_support/parameter_filter.rb +138 -0
  184. data/lib/active_support/per_thread_registry.rb +6 -1
  185. data/lib/active_support/rails.rb +1 -10
  186. data/lib/active_support/railtie.rb +77 -5
  187. data/lib/active_support/reloader.rb +5 -6
  188. data/lib/active_support/rescuable.rb +8 -8
  189. data/lib/active_support/ruby_features.rb +7 -0
  190. data/lib/active_support/secure_compare_rotator.rb +51 -0
  191. data/lib/active_support/security_utils.rb +19 -12
  192. data/lib/active_support/string_inquirer.rb +2 -3
  193. data/lib/active_support/subscriber.rb +79 -46
  194. data/lib/active_support/tagged_logging.rb +58 -9
  195. data/lib/active_support/test_case.rb +79 -0
  196. data/lib/active_support/testing/assertions.rb +62 -11
  197. data/lib/active_support/testing/deprecation.rb +52 -2
  198. data/lib/active_support/testing/file_fixtures.rb +2 -0
  199. data/lib/active_support/testing/isolation.rb +4 -4
  200. data/lib/active_support/testing/method_call_assertions.rb +32 -5
  201. data/lib/active_support/testing/parallelization/server.rb +82 -0
  202. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  203. data/lib/active_support/testing/parallelization.rb +55 -0
  204. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  205. data/lib/active_support/testing/stream.rb +4 -7
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +60 -14
  208. data/lib/active_support/time_with_zone.rb +139 -64
  209. data/lib/active_support/values/time_zone.rb +66 -30
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +3 -4
  212. data/lib/active_support/xml_mini/libxml.rb +7 -7
  213. data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
  214. data/lib/active_support/xml_mini/nokogiri.rb +6 -6
  215. data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
  216. data/lib/active_support/xml_mini/rexml.rb +11 -4
  217. data/lib/active_support/xml_mini.rb +7 -14
  218. data/lib/active_support.rb +30 -1
  219. metadata +64 -35
  220. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  221. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  222. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  223. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  224. data/lib/active_support/core_ext/marshal.rb +0 -24
  225. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  226. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  227. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  228. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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
@@ -63,14 +65,16 @@ module ActiveSupport
63
65
  self
64
66
  end
65
67
 
66
- def initialize(constructor = {})
68
+ def initialize(constructor = nil)
67
69
  if constructor.respond_to?(:to_hash)
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
76
+ elsif constructor.nil?
77
+ super()
74
78
  else
75
79
  super(constructor)
76
80
  end
@@ -90,12 +94,12 @@ module ActiveSupport
90
94
  #
91
95
  # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
92
96
  def []=(key, value)
93
- regular_writer(convert_key(key), convert_value(value, for: :assignment))
97
+ regular_writer(convert_key(key), convert_value(value, conversion: :assignment))
94
98
  end
95
99
 
96
100
  alias_method :store, :[]=
97
101
 
98
- # Updates the receiver in-place, merging in the hash passed as argument:
102
+ # Updates the receiver in-place, merging in the hashes passed as arguments:
99
103
  #
100
104
  # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
101
105
  # hash_1[:key] = 'value'
@@ -105,11 +109,14 @@ module ActiveSupport
105
109
  #
106
110
  # hash_1.update(hash_2) # => {"key"=>"New Value!"}
107
111
  #
108
- # The argument can be either an
112
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
113
+ # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 }
114
+ #
115
+ # The arguments can be either an
109
116
  # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
110
117
  # In either case the merge respects the semantics of indifferent access.
111
118
  #
112
- # If the argument is a regular hash with keys +:key+ and +"key"+ only one
119
+ # If the argument is a regular hash with keys +:key+ and <tt>"key"</tt> only one
113
120
  # of the values end up in the receiver, but which one is unspecified.
114
121
  #
115
122
  # When given a block, the value for duplicated keys will be determined
@@ -120,18 +127,15 @@ module ActiveSupport
120
127
  # hash_1[:key] = 10
121
128
  # hash_2['key'] = 12
122
129
  # 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)
130
+ def update(*other_hashes, &block)
131
+ if other_hashes.size == 1
132
+ update_with_single_argument(other_hashes.first, block)
126
133
  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))
134
+ other_hashes.each do |other_hash|
135
+ update_with_single_argument(other_hash, block)
132
136
  end
133
- self
134
137
  end
138
+ self
135
139
  end
136
140
 
137
141
  alias_method :merge!, :update
@@ -190,20 +194,18 @@ module ActiveSupport
190
194
  super(convert_key(key), *extras)
191
195
  end
192
196
 
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
197
+ # Same as <tt>Hash#dig</tt> where the key passed as argument can be
198
+ # either a string or a symbol:
199
+ #
200
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
201
+ # counters[:foo] = { bar: 1 }
202
+ #
203
+ # counters.dig('foo', 'bar') # => 1
204
+ # counters.dig(:foo, :bar) # => 1
205
+ # counters.dig(:zoo) # => nil
206
+ def dig(*args)
207
+ args[0] = convert_key(args[0]) if args.size > 0
208
+ super(*args)
207
209
  end
208
210
 
209
211
  # Same as <tt>Hash#default</tt> where the key passed as argument can be
@@ -226,8 +228,8 @@ module ActiveSupport
226
228
  # hash[:a] = 'x'
227
229
  # hash[:b] = 'y'
228
230
  # hash.values_at('a', 'b') # => ["x", "y"]
229
- def values_at(*indices)
230
- indices.collect { |key| self[convert_key(key)] }
231
+ def values_at(*keys)
232
+ super(*keys.map { |key| convert_key(key) })
231
233
  end
232
234
 
233
235
  # Returns an array of the values at the specified indices, but also
@@ -240,8 +242,8 @@ module ActiveSupport
240
242
  # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
241
243
  # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
242
244
  def fetch_values(*indices, &block)
243
- indices.collect { |key| fetch(key, &block) }
244
- end if Hash.method_defined?(:fetch_values)
245
+ super(*indices.map { |key| convert_key(key) }, &block)
246
+ end
245
247
 
246
248
  # Returns a shallow copy of the hash.
247
249
  #
@@ -260,8 +262,8 @@ module ActiveSupport
260
262
  # This method has the same semantics of +update+, except it does not
261
263
  # modify the receiver but rather returns a new hash with indifferent
262
264
  # access with the result of the merge.
263
- def merge(hash, &block)
264
- dup.update(hash, &block)
265
+ def merge(*hashes, &block)
266
+ dup.update(*hashes, &block)
265
267
  end
266
268
 
267
269
  # Like +merge+ but the other way around: Merges the receiver into the
@@ -294,6 +296,15 @@ module ActiveSupport
294
296
  super(convert_key(key))
295
297
  end
296
298
 
299
+ # Returns a hash with indifferent access that includes everything except given keys.
300
+ # hash = { a: "x", b: "y", c: 10 }.with_indifferent_access
301
+ # hash.except(:a, "b") # => {c: 10}.with_indifferent_access
302
+ # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access
303
+ def except(*keys)
304
+ slice(*self.keys - keys.map { |key| convert_key(key) })
305
+ end
306
+ alias_method :without, :except
307
+
297
308
  def stringify_keys!; self end
298
309
  def deep_stringify_keys!; self end
299
310
  def stringify_keys; dup end
@@ -353,40 +364,59 @@ module ActiveSupport
353
364
  set_defaults(_new_hash)
354
365
 
355
366
  each do |key, value|
356
- _new_hash[key] = convert_value(value, for: :to_hash)
367
+ _new_hash[key] = convert_value(value, conversion: :to_hash)
357
368
  end
358
369
  _new_hash
359
370
  end
360
371
 
361
372
  private
362
- def convert_key(key) # :doc:
363
- key.kind_of?(Symbol) ? key.to_s : key
373
+ if Symbol.method_defined?(:name)
374
+ def convert_key(key)
375
+ key.kind_of?(Symbol) ? key.name : key
376
+ end
377
+ else
378
+ def convert_key(key)
379
+ key.kind_of?(Symbol) ? key.to_s : key
380
+ end
364
381
  end
365
382
 
366
- def convert_value(value, options = {}) # :doc:
383
+ def convert_value(value, conversion: nil)
367
384
  if value.is_a? Hash
368
- if options[:for] == :to_hash
385
+ if conversion == :to_hash
369
386
  value.to_hash
370
387
  else
371
388
  value.nested_under_indifferent_access
372
389
  end
373
390
  elsif value.is_a?(Array)
374
- if options[:for] != :assignment || value.frozen?
391
+ if conversion != :assignment || value.frozen?
375
392
  value = value.dup
376
393
  end
377
- value.map! { |e| convert_value(e, options) }
394
+ value.map! { |e| convert_value(e, conversion: conversion) }
378
395
  else
379
396
  value
380
397
  end
381
398
  end
382
399
 
383
- def set_defaults(target) # :doc:
400
+ def set_defaults(target)
384
401
  if default_proc
385
402
  target.default_proc = default_proc.dup
386
403
  else
387
404
  target.default = default
388
405
  end
389
406
  end
407
+
408
+ def update_with_single_argument(other_hash, block)
409
+ if other_hash.is_a? HashWithIndifferentAccess
410
+ regular_update(other_hash, &block)
411
+ else
412
+ other_hash.to_hash.each_pair do |key, value|
413
+ if block && key?(key)
414
+ value = block.call(convert_key(key), self[key], value)
415
+ end
416
+ regular_writer(convert_key(key), convert_value(value))
417
+ end
418
+ end
419
+ end
390
420
  end
391
421
  end
392
422
 
@@ -0,0 +1,43 @@
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
+ translation = I18n.translate(key, **html_safe_options)
11
+ html_safe_translation(translation)
12
+ else
13
+ I18n.translate(key, **options)
14
+ end
15
+ end
16
+
17
+ private
18
+ def html_safe_translation_key?(key)
19
+ /(?:_|\b)html\z/.match?(key)
20
+ end
21
+
22
+ def html_escape_translation_options(options)
23
+ options.each do |name, value|
24
+ unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
25
+ options[name] = ERB::Util.html_escape(value.to_s)
26
+ end
27
+ end
28
+ end
29
+
30
+ def i18n_option?(name)
31
+ (@i18n_option_names ||= I18n::RESERVED_KEYS.to_set).include?(name)
32
+ end
33
+
34
+
35
+ def html_safe_translation(translation)
36
+ if translation.respond_to?(:map)
37
+ translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
38
+ else
39
+ translation.respond_to?(:html_safe) ? translation.html_safe : translation
40
+ end
41
+ end
42
+ end
43
+ end
@@ -5,6 +5,7 @@ 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
10
  $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
10
11
  raise e
@@ -13,3 +14,4 @@ require "active_support/lazy_load_hooks"
13
14
 
14
15
  ActiveSupport.run_load_hooks(:i18n)
15
16
  I18n.load_path << File.expand_path("locale/en.yml", __dir__)
17
+ 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
+ ActionView::Helpers::TranslationHelper.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
@@ -19,13 +16,13 @@ module ActiveSupport
19
16
  # inflect.plural /^(ox)$/i, '\1\2en'
20
17
  # inflect.singular /^(ox)en/i, '\1'
21
18
  #
22
- # inflect.irregular 'octopus', 'octopi'
19
+ # inflect.irregular 'cactus', 'cacti'
23
20
  #
24
21
  # inflect.uncountable 'equipment'
25
22
  # end
26
23
  #
27
24
  # New rules are added at the top. So in the example above, the irregular
28
- # rule for octopus will now be the first of the pluralization and
25
+ # rule for cactus will now be the first of the pluralization and
29
26
  # singularization rules that is runs. This guarantees that your rules run
30
27
  # before any of the rules that may already have been loaded.
31
28
  class Inflections
@@ -67,8 +64,14 @@ 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
+ def self.instance_or_fallback(locale)
68
+ I18n.fallbacks[locale].each do |k|
69
+ return @__instance__[k] if @__instance__.key?(k)
70
+ end
71
+ instance(locale)
72
+ end
73
+
74
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
72
75
 
73
76
  attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
74
77
 
@@ -80,7 +83,7 @@ module ActiveSupport
80
83
  # Private, for the test suite.
81
84
  def initialize_dup(orig) # :nodoc:
82
85
  %w(plurals singulars uncountables humans acronyms).each do |scope|
83
- instance_variable_set("@#{scope}", orig.send(scope).dup)
86
+ instance_variable_set("@#{scope}", orig.public_send(scope).dup)
84
87
  end
85
88
  define_acronym_regex_patterns
86
89
  end
@@ -164,7 +167,7 @@ module ActiveSupport
164
167
  # regular expressions. You simply pass the irregular in singular and
165
168
  # plural form.
166
169
  #
167
- # irregular 'octopus', 'octopi'
170
+ # irregular 'cactus', 'cacti'
168
171
  # irregular 'person', 'people'
169
172
  def irregular(singular, plural)
170
173
  @uncountables.delete(singular)
@@ -219,21 +222,29 @@ module ActiveSupport
219
222
  # Clears the loaded inflections within a given scope (default is
220
223
  # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
221
224
  # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
222
- # <tt>:humans</tt>.
225
+ # <tt>:humans</tt>, <tt>:acronyms</tt>.
223
226
  #
224
227
  # clear :all
225
228
  # clear :plurals
226
229
  def clear(scope = :all)
227
230
  case scope
228
231
  when :all
229
- @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
230
- else
232
+ clear(:acronyms)
233
+ clear(:plurals)
234
+ clear(:singulars)
235
+ clear(:uncountables)
236
+ clear(:humans)
237
+ when :acronyms
238
+ @acronyms = {}
239
+ define_acronym_regex_patterns
240
+ when :uncountables
241
+ @uncountables = Uncountables.new
242
+ when :plurals, :singulars, :humans
231
243
  instance_variable_set "@#{scope}", []
232
244
  end
233
245
  end
234
246
 
235
247
  private
236
-
237
248
  def define_acronym_regex_patterns
238
249
  @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
239
250
  @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
@@ -253,7 +264,7 @@ module ActiveSupport
253
264
  if block_given?
254
265
  yield Inflections.instance(locale)
255
266
  else
256
- Inflections.instance(locale)
267
+ Inflections.instance_or_fallback(locale)
257
268
  end
258
269
  end
259
270
  end
@@ -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
@@ -68,13 +68,17 @@ module ActiveSupport
68
68
  # camelize(underscore('SSLError')) # => "SslError"
69
69
  def camelize(term, uppercase_first_letter = true)
70
70
  string = term.to_s
71
- if uppercase_first_letter
72
- string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
71
+ # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
72
+ if !uppercase_first_letter || uppercase_first_letter == :lower
73
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
73
74
  else
74
- string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
75
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
76
+ end
77
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
78
+ word = $2
79
+ substituted = inflections.acronyms[word] || word.capitalize! || word
80
+ $1 ? "::#{substituted}" : substituted
75
81
  end
76
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
77
- string.gsub!("/".freeze, "::".freeze)
78
82
  string
79
83
  end
80
84
 
@@ -90,12 +94,11 @@ module ActiveSupport
90
94
  #
91
95
  # camelize(underscore('SSLError')) # => "SslError"
92
96
  def underscore(camel_cased_word)
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)
97
+ return camel_cased_word.to_s unless /[A-Z-]|::/.match?(camel_cased_word)
98
+ word = camel_cased_word.to_s.gsub("::", "/")
99
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
100
+ word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
101
+ word.tr!("-", "_")
99
102
  word.downcase!
100
103
  word
101
104
  end
@@ -106,7 +109,7 @@ module ActiveSupport
106
109
  #
107
110
  # * Applies human inflection rules to the argument.
108
111
  # * Deletes leading underscores, if any.
109
- # * Removes a "_id" suffix if present.
112
+ # * Removes an "_id" suffix if present.
110
113
  # * Replaces underscores with spaces, if any.
111
114
  # * Downcases all words except acronyms.
112
115
  # * Capitalizes the first word.
@@ -120,7 +123,7 @@ module ActiveSupport
120
123
  # humanize('author_id') # => "Author"
121
124
  # humanize('author_id', capitalize: false) # => "author"
122
125
  # humanize('_id') # => "Id"
123
- # humanize('author_id', keep_id_suffix: true) # => "Author Id"
126
+ # humanize('author_id', keep_id_suffix: true) # => "Author id"
124
127
  #
125
128
  # If "SSL" was defined to be an acronym:
126
129
  #
@@ -131,18 +134,22 @@ module ActiveSupport
131
134
 
132
135
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
133
136
 
134
- result.sub!(/\A_+/, "".freeze)
137
+ result.tr!("_", " ")
138
+ result.lstrip!
135
139
  unless keep_id_suffix
136
- result.sub!(/_id\z/, "".freeze)
140
+ result.delete_suffix!(" id")
137
141
  end
138
- result.tr!("_".freeze, " ".freeze)
139
142
 
140
- result.gsub!(/([a-z\d]*)/i) do |match|
141
- "#{inflections.acronyms[match.downcase] || match.downcase}"
143
+ result.gsub!(/([a-z\d]+)/i) do |match|
144
+ match.downcase!
145
+ inflections.acronyms[match] || match
142
146
  end
143
147
 
144
148
  if capitalize
145
- result.sub!(/\A\w/) { |match| match.upcase }
149
+ result.sub!(/\A\w/) do |match|
150
+ match.upcase!
151
+ match
152
+ end
146
153
  end
147
154
 
148
155
  result
@@ -173,7 +180,7 @@ module ActiveSupport
173
180
  # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
174
181
  # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
175
182
  def titleize(word, keep_id_suffix: false)
176
- humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`])[a-z]/) do |match|
183
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])[a-z]/) do |match|
177
184
  match.capitalize
178
185
  end
179
186
  end
@@ -197,17 +204,17 @@ module ActiveSupport
197
204
  #
198
205
  # Singular names are not handled correctly:
199
206
  #
200
- # classify('calculus') # => "Calculus"
207
+ # classify('calculus') # => "Calculu"
201
208
  def classify(table_name)
202
209
  # strip out any leading schema name
203
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
210
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
204
211
  end
205
212
 
206
213
  # Replaces underscores with dashes in the string.
207
214
  #
208
215
  # dasherize('puni_puni') # => "puni-puni"
209
216
  def dasherize(underscored_word)
210
- underscored_word.tr("_".freeze, "-".freeze)
217
+ underscored_word.tr("_", "-")
211
218
  end
212
219
 
213
220
  # Removes the module part from the expression in the string.
@@ -270,34 +277,7 @@ module ActiveSupport
270
277
  # NameError is raised when the name is not in CamelCase or the constant is
271
278
  # unknown.
272
279
  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
295
- end
296
-
297
- # owner is in Object, so raise
298
- constant.const_get(name, false)
299
- end
300
- end
280
+ Object.const_get(camel_cased_word)
301
281
  end
302
282
 
303
283
  # Tries to find a constant with the name specified in the argument string.
@@ -327,10 +307,9 @@ module ActiveSupport
327
307
  rescue NameError => e
328
308
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
329
309
  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
310
  rescue LoadError => e
333
- raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
311
+ message = e.respond_to?(:original_message) ? e.original_message : e.message
312
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message)
334
313
  end
335
314
 
336
315
  # Returns the suffix that should be added to a number to denote the position
@@ -343,18 +322,7 @@ module ActiveSupport
343
322
  # ordinal(-11) # => "th"
344
323
  # ordinal(-1021) # => "st"
345
324
  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
325
+ I18n.translate("number.nth.ordinals", number: number)
358
326
  end
359
327
 
360
328
  # Turns a number into an ordinal string used to denote the position in an
@@ -367,24 +335,23 @@ module ActiveSupport
367
335
  # ordinalize(-11) # => "-11th"
368
336
  # ordinalize(-1021) # => "-1021st"
369
337
  def ordinalize(number)
370
- "#{number}#{ordinal(number)}"
338
+ I18n.translate("number.nth.ordinalized", number: number)
371
339
  end
372
340
 
373
341
  private
374
-
375
342
  # Mounts a regular expression, returned as a string to ease interpolation,
376
343
  # that will match part by part the given constant.
377
344
  #
378
345
  # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
379
346
  # const_regexp("::") # => "::"
380
347
  def const_regexp(camel_cased_word)
381
- parts = camel_cased_word.split("::".freeze)
348
+ parts = camel_cased_word.split("::")
382
349
 
383
350
  return Regexp.escape(camel_cased_word) if parts.blank?
384
351
 
385
352
  last = parts.pop
386
353
 
387
- parts.reverse.inject(last) do |acc, part|
354
+ parts.reverse!.inject(last) do |acc, part|
388
355
  part.empty? ? acc : "#{part}(::#{acc})?"
389
356
  end
390
357
  end