activesupport 5.2.7.1 → 6.1.4.6

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -434
  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/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  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 -41
  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 +51 -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 +117 -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/duration/iso8601_parser.rb +2 -4
  98. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  99. data/lib/active_support/duration.rb +78 -30
  100. data/lib/active_support/encrypted_configuration.rb +0 -4
  101. data/lib/active_support/encrypted_file.rb +22 -4
  102. data/lib/active_support/environment_inquirer.rb +20 -0
  103. data/lib/active_support/evented_file_update_checker.rb +82 -117
  104. data/lib/active_support/execution_wrapper.rb +2 -1
  105. data/lib/active_support/file_update_checker.rb +0 -1
  106. data/lib/active_support/fork_tracker.rb +64 -0
  107. data/lib/active_support/gem_version.rb +4 -4
  108. data/lib/active_support/hash_with_indifferent_access.rb +70 -42
  109. data/lib/active_support/i18n.rb +1 -0
  110. data/lib/active_support/i18n_railtie.rb +15 -8
  111. data/lib/active_support/inflector/inflections.rb +2 -7
  112. data/lib/active_support/inflector/methods.rb +49 -58
  113. data/lib/active_support/inflector/transliterate.rb +47 -18
  114. data/lib/active_support/json/decoding.rb +25 -26
  115. data/lib/active_support/json/encoding.rb +11 -3
  116. data/lib/active_support/key_generator.rb +1 -33
  117. data/lib/active_support/lazy_load_hooks.rb +5 -2
  118. data/lib/active_support/locale/en.rb +33 -0
  119. data/lib/active_support/locale/en.yml +7 -3
  120. data/lib/active_support/log_subscriber.rb +39 -9
  121. data/lib/active_support/logger.rb +2 -17
  122. data/lib/active_support/logger_silence.rb +11 -19
  123. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  124. data/lib/active_support/message_encryptor.rb +8 -13
  125. data/lib/active_support/message_verifier.rb +10 -10
  126. data/lib/active_support/messages/metadata.rb +11 -2
  127. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  128. data/lib/active_support/messages/rotator.rb +10 -9
  129. data/lib/active_support/multibyte/chars.rb +10 -68
  130. data/lib/active_support/multibyte/unicode.rb +15 -327
  131. data/lib/active_support/notifications/fanout.rb +116 -16
  132. data/lib/active_support/notifications/instrumenter.rb +71 -9
  133. data/lib/active_support/notifications.rb +72 -8
  134. data/lib/active_support/number_helper/number_converter.rb +5 -6
  135. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  136. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  137. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  138. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  139. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  140. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  141. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  142. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  143. data/lib/active_support/number_helper.rb +38 -12
  144. data/lib/active_support/option_merger.rb +22 -3
  145. data/lib/active_support/ordered_hash.rb +1 -1
  146. data/lib/active_support/ordered_options.rb +13 -3
  147. data/lib/active_support/parameter_filter.rb +133 -0
  148. data/lib/active_support/per_thread_registry.rb +1 -1
  149. data/lib/active_support/rails.rb +1 -10
  150. data/lib/active_support/railtie.rb +23 -1
  151. data/lib/active_support/reloader.rb +4 -5
  152. data/lib/active_support/rescuable.rb +4 -4
  153. data/lib/active_support/secure_compare_rotator.rb +51 -0
  154. data/lib/active_support/security_utils.rb +19 -12
  155. data/lib/active_support/string_inquirer.rb +4 -3
  156. data/lib/active_support/subscriber.rb +72 -28
  157. data/lib/active_support/tagged_logging.rb +42 -8
  158. data/lib/active_support/test_case.rb +91 -0
  159. data/lib/active_support/testing/assertions.rb +30 -9
  160. data/lib/active_support/testing/deprecation.rb +0 -1
  161. data/lib/active_support/testing/file_fixtures.rb +2 -0
  162. data/lib/active_support/testing/isolation.rb +2 -2
  163. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  164. data/lib/active_support/testing/parallelization/server.rb +78 -0
  165. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  166. data/lib/active_support/testing/parallelization.rb +51 -0
  167. data/lib/active_support/testing/stream.rb +1 -2
  168. data/lib/active_support/testing/time_helpers.rb +47 -12
  169. data/lib/active_support/time_with_zone.rb +81 -47
  170. data/lib/active_support/values/time_zone.rb +32 -17
  171. data/lib/active_support/xml_mini/jdom.rb +2 -3
  172. data/lib/active_support/xml_mini/libxml.rb +2 -2
  173. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  174. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  176. data/lib/active_support/xml_mini/rexml.rb +10 -3
  177. data/lib/active_support/xml_mini.rb +2 -10
  178. data/lib/active_support.rb +14 -1
  179. metadata +55 -29
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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
@@ -5,6 +5,8 @@ require "active_support/i18n"
5
5
 
6
6
  module ActiveSupport
7
7
  module Inflector
8
+ ALLOWED_ENCODINGS_FOR_TRANSLITERATE = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030].freeze
9
+
8
10
  # Replaces non-ASCII characters with an ASCII approximation, or if none
9
11
  # exists, a replacement character which defaults to "?".
10
12
  #
@@ -51,20 +53,43 @@ module ActiveSupport
51
53
  #
52
54
  # Now you can have different transliterations for each locale:
53
55
  #
54
- # I18n.locale = :en
55
- # transliterate('Jürgen')
56
+ # transliterate('Jürgen', locale: :en)
56
57
  # # => "Jurgen"
57
58
  #
58
- # I18n.locale = :de
59
- # transliterate('Jürgen')
59
+ # transliterate('Jürgen', locale: :de)
60
60
  # # => "Juergen"
61
- def transliterate(string, replacement = "?".freeze)
61
+ #
62
+ # Transliteration is restricted to UTF-8, US-ASCII and GB18030 strings
63
+ # Other encodings will raise an ArgumentError.
64
+ def transliterate(string, replacement = "?", locale: nil)
65
+ string = string.dup if string.frozen?
62
66
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
67
+ raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
68
+
69
+ input_encoding = string.encoding
70
+
71
+ # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
72
+ # US-ASCII is given. This way we can let tidy_bytes handle the string
73
+ # in the same way as we do for UTF-8
74
+ string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII
75
+
76
+ # GB18030 is Unicode compatible but is not a direct mapping so needs to be
77
+ # transcoded. Using invalid/undef :replace will result in loss of data in
78
+ # the event of invalid characters, but since tidy_bytes will replace
79
+ # invalid/undef with a "?" we're safe to do the same beforehand
80
+ string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030
81
+
82
+ transliterated = I18n.transliterate(
83
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
84
+ replacement: replacement,
85
+ locale: locale
86
+ )
87
+
88
+ # Restore the string encoding of the input if it was not UTF-8.
89
+ # Apply invalid/undef :replace as tidy_bytes does
90
+ transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding
63
91
 
64
- I18n.transliterate(
65
- ActiveSupport::Multibyte::Unicode.normalize(
66
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
67
- replacement: replacement)
92
+ transliterated
68
93
  end
69
94
 
70
95
  # Replaces special characters in a string so that it may be used as part of
@@ -75,8 +100,8 @@ module ActiveSupport
75
100
  #
76
101
  # To use a custom separator, override the +separator+ argument.
77
102
  #
78
- # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
79
- # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
103
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
104
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
80
105
  #
81
106
  # To preserve the case of the characters in a string, use the +preserve_case+ argument.
82
107
  #
@@ -85,19 +110,23 @@ module ActiveSupport
85
110
  #
86
111
  # It preserves dashes and underscores unless they are used as separators:
87
112
  #
88
- # parameterize("^très|Jolie__ ") # => "tres-jolie__"
89
- # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
90
- # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
113
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
114
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
115
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
91
116
  #
92
- def parameterize(string, separator: "-", preserve_case: false)
117
+ # If the optional parameter +locale+ is specified,
118
+ # the word will be parameterized as a word of that language.
119
+ # By default, this parameter is set to <tt>nil</tt> and it will use
120
+ # the configured <tt>I18n.locale</tt>.
121
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
93
122
  # Replace accented chars with their ASCII equivalents.
94
- parameterized_string = transliterate(string)
123
+ parameterized_string = transliterate(string, locale: locale)
95
124
 
96
125
  # Turn unwanted chars into the separator.
97
126
  parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
98
127
 
99
128
  unless separator.nil? || separator.empty?
100
- if separator == "-".freeze
129
+ if separator == "-"
101
130
  re_duplicate_separator = /-{2,}/
102
131
  re_leading_trailing_separator = /^-|-$/i
103
132
  else
@@ -108,7 +137,7 @@ module ActiveSupport
108
137
  # No more than one of the separator in a row.
109
138
  parameterized_string.gsub!(re_duplicate_separator, separator)
110
139
  # Remove leading/trailing separator.
111
- parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
140
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
112
141
  end
113
142
 
114
143
  parameterized_string.downcase! unless preserve_case
@@ -10,8 +10,8 @@ module ActiveSupport
10
10
 
11
11
  module JSON
12
12
  # matches YAML-formatted dates
13
- DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
14
- DATETIME_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)$/
13
+ DATE_REGEX = /\A\d{4}-\d{2}-\d{2}\z/
14
+ DATETIME_REGEX = /\A(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)\z/
15
15
 
16
16
  class << self
17
17
  # Parses a JSON string (JavaScript Object Notation) into a hash.
@@ -44,33 +44,32 @@ module ActiveSupport
44
44
  end
45
45
 
46
46
  private
47
-
48
- def convert_dates_from(data)
49
- case data
50
- when nil
51
- nil
52
- when DATE_REGEX
53
- begin
54
- Date.parse(data)
55
- rescue ArgumentError
56
- data
57
- end
58
- when DATETIME_REGEX
59
- begin
60
- Time.zone.parse(data)
61
- rescue ArgumentError
47
+ def convert_dates_from(data)
48
+ case data
49
+ when nil
50
+ nil
51
+ when DATE_REGEX
52
+ begin
53
+ Date.parse(data)
54
+ rescue ArgumentError
55
+ data
56
+ end
57
+ when DATETIME_REGEX
58
+ begin
59
+ Time.zone.parse(data)
60
+ rescue ArgumentError
61
+ data
62
+ end
63
+ when Array
64
+ data.map! { |d| convert_dates_from(d) }
65
+ when Hash
66
+ data.transform_values! do |value|
67
+ convert_dates_from(value)
68
+ end
69
+ else
62
70
  data
63
71
  end
64
- when Array
65
- data.map! { |d| convert_dates_from(d) }
66
- when Hash
67
- data.each do |key, value|
68
- data[key] = convert_dates_from(value)
69
- end
70
- else
71
- data
72
72
  end
73
- end
74
73
  end
75
74
  end
76
75
  end
@@ -54,9 +54,13 @@ module ActiveSupport
54
54
  class EscapedString < String #:nodoc:
55
55
  def to_json(*)
56
56
  if Encoding.escape_html_entities_in_json
57
- super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
57
+ s = super
58
+ s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
59
+ s
58
60
  else
59
- super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
61
+ s = super
62
+ s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
63
+ s
60
64
  end
61
65
  end
62
66
 
@@ -89,7 +93,11 @@ module ActiveSupport
89
93
  when Numeric, NilClass, TrueClass, FalseClass
90
94
  value.as_json
91
95
  when Hash
92
- Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
96
+ result = {}
97
+ value.each do |k, v|
98
+ result[jsonify(k)] = jsonify(v)
99
+ end
100
+ result
93
101
  when Array
94
102
  value.map { |v| jsonify(v) }
95
103
  else
@@ -35,39 +35,7 @@ module ActiveSupport
35
35
 
36
36
  # Returns a derived key suitable for use.
37
37
  def generate_key(*args)
38
- @cache_keys[args.join] ||= @key_generator.generate_key(*args)
38
+ @cache_keys[args.join("|")] ||= @key_generator.generate_key(*args)
39
39
  end
40
40
  end
41
-
42
- class LegacyKeyGenerator # :nodoc:
43
- SECRET_MIN_LENGTH = 30 # Characters
44
-
45
- def initialize(secret)
46
- ensure_secret_secure(secret)
47
- @secret = secret
48
- end
49
-
50
- def generate_key(salt)
51
- @secret
52
- end
53
-
54
- private
55
-
56
- # To prevent users from using something insecure like "Password" we make sure that the
57
- # secret they've provided is at least 30 characters in length.
58
- def ensure_secret_secure(secret)
59
- if secret.blank?
60
- raise ArgumentError, "A secret is required to generate an integrity hash " \
61
- "for cookie session data. Set a secret_key_base of at least " \
62
- "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`."
63
- end
64
-
65
- if secret.length < SECRET_MIN_LENGTH
66
- raise ArgumentError, "Secret should be something secure, " \
67
- "like \"#{SecureRandom.hex(16)}\". The value you " \
68
- "provided, \"#{secret}\", is shorter than the minimum length " \
69
- "of #{SECRET_MIN_LENGTH} characters."
70
- end
71
- end
72
- end
73
41
  end
@@ -54,7 +54,6 @@ module ActiveSupport
54
54
  end
55
55
 
56
56
  private
57
-
58
57
  def with_execution_control(name, block, once)
59
58
  unless @run_once[name].include?(block)
60
59
  @run_once[name] << block if once
@@ -68,7 +67,11 @@ module ActiveSupport
68
67
  if options[:yield]
69
68
  block.call(base)
70
69
  else
71
- base.instance_eval(&block)
70
+ if base.is_a?(Module)
71
+ base.class_eval(&block)
72
+ else
73
+ base.instance_eval(&block)
74
+ end
72
75
  end
73
76
  end
74
77
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ {
4
+ en: {
5
+ number: {
6
+ nth: {
7
+ ordinals: lambda do |_key, options|
8
+ number = options[:number]
9
+ case number
10
+ when 1; "st"
11
+ when 2; "nd"
12
+ when 3; "rd"
13
+ when 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; "th"
14
+ else
15
+ num_modulo = number.to_i.abs % 100
16
+ num_modulo %= 10 if num_modulo > 13
17
+ case num_modulo
18
+ when 1; "st"
19
+ when 2; "nd"
20
+ when 3; "rd"
21
+ else "th"
22
+ end
23
+ end
24
+ end,
25
+
26
+ ordinalized: lambda do |_key, options|
27
+ number = options[:number]
28
+ "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
29
+ end
30
+ }
31
+ }
32
+ }
33
+ }
@@ -44,10 +44,12 @@ en:
44
44
  delimiter: ","
45
45
  # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
46
46
  precision: 3
47
+ # Determine how rounding is performed (see BigDecimal::mode)
48
+ round_mode: default
47
49
  # If set to true, precision will mean the number of significant digits instead
48
50
  # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
49
51
  significant: false
50
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
52
+ # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
51
53
  strip_insignificant_zeros: false
52
54
 
53
55
  # Used in NumberHelper.number_to_currency()
@@ -56,10 +58,11 @@ en:
56
58
  # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
57
59
  format: "%u%n"
58
60
  unit: "$"
59
- # These five are to override number.format and are optional
61
+ # These six are to override number.format and are optional
60
62
  separator: "."
61
63
  delimiter: ","
62
64
  precision: 2
65
+ # round_mode:
63
66
  significant: false
64
67
  strip_insignificant_zeros: false
65
68
 
@@ -87,10 +90,11 @@ en:
87
90
  # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
88
91
  human:
89
92
  format:
90
- # These five are to override number.format and are optional
93
+ # These six are to override number.format and are optional
91
94
  # separator:
92
95
  delimiter: ""
93
96
  precision: 3
97
+ # round_mode:
94
98
  significant: true
95
99
  strip_insignificant_zeros: true
96
100
  # Used in number_to_human_size()
@@ -5,8 +5,8 @@ require "active_support/core_ext/class/attribute"
5
5
  require "active_support/subscriber"
6
6
 
7
7
  module ActiveSupport
8
- # ActiveSupport::LogSubscriber is an object set to consume
9
- # ActiveSupport::Notifications with the sole purpose of logging them.
8
+ # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
9
+ # <tt>ActiveSupport::Notifications</tt> with the sole purpose of logging them.
10
10
  # The log subscriber dispatches notifications to a registered object based
11
11
  # on its given namespace.
12
12
  #
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  # module ActiveRecord
17
17
  # class LogSubscriber < ActiveSupport::LogSubscriber
18
18
  # def sql(event)
19
- # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
19
+ # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
20
20
  # end
21
21
  # end
22
22
  # end
@@ -29,13 +29,39 @@ module ActiveSupport
29
29
  # subscriber, the line above should be called after your
30
30
  # <tt>ActiveRecord::LogSubscriber</tt> definition.
31
31
  #
32
- # After configured, whenever a "sql.active_record" notification is published,
33
- # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
34
- # the sql method.
32
+ # A logger also needs to be set with <tt>ActiveRecord::LogSubscriber.logger=</tt>.
33
+ # This is assigned automatically in a Rails environment.
34
+ #
35
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
36
+ # it will properly dispatch the event
37
+ # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
38
+ #
39
+ # Being an <tt>ActiveSupport::Notifications</tt> consumer,
40
+ # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
41
+ # instrumented code raises an exception. It is common to log a different
42
+ # message in case of an error, and this can be achieved by extending
43
+ # the previous example:
44
+ #
45
+ # module ActiveRecord
46
+ # class LogSubscriber < ActiveSupport::LogSubscriber
47
+ # def sql(event)
48
+ # exception = event.payload[:exception]
49
+ #
50
+ # if exception
51
+ # exception_object = event.payload[:exception_object]
52
+ #
53
+ # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \
54
+ # "(#{exception_object.backtrace.first})"
55
+ # else
56
+ # # standard logger code
57
+ # end
58
+ # end
59
+ # end
60
+ # end
35
61
  #
36
62
  # Log subscriber also has some helpers to deal with logging and automatically
37
- # flushes all logs when the request finishes (via action_dispatch.callback
38
- # notification) in a Rails environment.
63
+ # flushes all logs when the request finishes
64
+ # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment.
39
65
  class LogSubscriber < Subscriber
40
66
  # Embed in a String to clear all previous ANSI sequences.
41
67
  CLEAR = "\e[0m"
@@ -70,6 +96,11 @@ module ActiveSupport
70
96
  def flush_all!
71
97
  logger.flush if logger.respond_to?(:flush)
72
98
  end
99
+
100
+ private
101
+ def fetch_public_methods(subscriber, inherit_all)
102
+ subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true)
103
+ end
73
104
  end
74
105
 
75
106
  def logger
@@ -89,7 +120,6 @@ module ActiveSupport
89
120
  end
90
121
 
91
122
  private
92
-
93
123
  %w(info debug warn error fatal unknown).each do |level|
94
124
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
95
125
  def #{level}(progname = nil, &block)