activesupport 5.1.7 → 6.1.7

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 (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +434 -490
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +2 -0
  7. data/lib/active_support/array_inquirer.rb +6 -2
  8. data/lib/active_support/backtrace_cleaner.rb +31 -3
  9. data/lib/active_support/benchmarkable.rb +3 -1
  10. data/lib/active_support/builder.rb +2 -0
  11. data/lib/active_support/cache/file_store.rb +37 -36
  12. data/lib/active_support/cache/mem_cache_store.rb +72 -56
  13. data/lib/active_support/cache/memory_store.rb +61 -33
  14. data/lib/active_support/cache/null_store.rb +10 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +67 -21
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/cache.rb +310 -126
  19. data/lib/active_support/callbacks.rb +106 -100
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  22. data/lib/active_support/concurrency/share_lock.rb +2 -1
  23. data/lib/active_support/configurable.rb +12 -14
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +7 -5
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +2 -0
  30. data/lib/active_support/core_ext/array/inquiry.rb +2 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +3 -1
  33. data/lib/active_support/core_ext/benchmark.rb +4 -2
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +2 -0
  35. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  36. data/lib/active_support/core_ext/class/attribute.rb +50 -47
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -40
  39. data/lib/active_support/core_ext/class.rb +2 -0
  40. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  41. data/lib/active_support/core_ext/date/blank.rb +2 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +8 -5
  43. data/lib/active_support/core_ext/date/conversions.rb +12 -10
  44. data/lib/active_support/core_ext/date/zones.rb +2 -0
  45. data/lib/active_support/core_ext/date.rb +2 -0
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +61 -37
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  50. data/lib/active_support/core_ext/date_time/blank.rb +2 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  53. data/lib/active_support/core_ext/date_time/conversions.rb +2 -1
  54. data/lib/active_support/core_ext/date_time.rb +2 -0
  55. data/lib/active_support/core_ext/digest/uuid.rb +4 -1
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +174 -71
  58. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  59. data/lib/active_support/core_ext/file.rb +2 -0
  60. data/lib/active_support/core_ext/hash/conversions.rb +7 -5
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +2 -0
  65. data/lib/active_support/core_ext/hash/keys.rb +3 -30
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +3 -2
  69. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +7 -14
  72. data/lib/active_support/core_ext/integer.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +2 -1
  77. data/lib/active_support/core_ext/load_error.rb +3 -8
  78. data/lib/active_support/core_ext/marshal.rb +4 -0
  79. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +44 -56
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +18 -18
  84. data/lib/active_support/core_ext/module/concerning.rb +15 -10
  85. data/lib/active_support/core_ext/module/delegation.rb +103 -58
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +18 -15
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +3 -1
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +131 -129
  94. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  95. data/lib/active_support/core_ext/numeric.rb +2 -1
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +13 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  99. data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
  100. data/lib/active_support/core_ext/object/duplicable.rb +9 -114
  101. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +22 -2
  104. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  105. data/lib/active_support/core_ext/object/to_query.rb +2 -0
  106. data/lib/active_support/core_ext/object/try.rb +19 -7
  107. data/lib/active_support/core_ext/object/with_options.rb +4 -2
  108. data/lib/active_support/core_ext/object.rb +2 -0
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +4 -1
  115. data/lib/active_support/core_ext/regexp.rb +10 -5
  116. data/lib/active_support/core_ext/securerandom.rb +25 -3
  117. data/lib/active_support/core_ext/string/access.rb +7 -16
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +3 -0
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +44 -1
  122. data/lib/active_support/core_ext/string/indent.rb +2 -0
  123. data/lib/active_support/core_ext/string/inflections.rb +69 -16
  124. data/lib/active_support/core_ext/string/inquiry.rb +3 -0
  125. data/lib/active_support/core_ext/string/multibyte.rb +9 -4
  126. data/lib/active_support/core_ext/string/output_safety.rb +104 -20
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  129. data/lib/active_support/core_ext/string/zones.rb +2 -0
  130. data/lib/active_support/core_ext/string.rb +2 -0
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  134. data/lib/active_support/core_ext/time/calculations.rb +76 -18
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +4 -0
  137. data/lib/active_support/core_ext/time/zones.rb +6 -4
  138. data/lib/active_support/core_ext/time.rb +2 -0
  139. data/lib/active_support/core_ext/uri.rb +11 -6
  140. data/lib/active_support/core_ext.rb +3 -1
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +210 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +2 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +134 -60
  147. data/lib/active_support/deprecation/behaviors.rb +43 -11
  148. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +2 -1
  151. data/lib/active_support/deprecation/method_wrappers.rb +29 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +32 -6
  153. data/lib/active_support/deprecation/reporting.rb +54 -9
  154. data/lib/active_support/deprecation.rb +9 -2
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +22 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +6 -6
  158. data/lib/active_support/duration/iso8601_serializer.rb +20 -14
  159. data/lib/active_support/duration.rb +102 -45
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +84 -117
  164. data/lib/active_support/execution_wrapper.rb +19 -13
  165. data/lib/active_support/executor.rb +2 -0
  166. data/lib/active_support/file_update_checker.rb +2 -1
  167. data/lib/active_support/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +3 -1
  169. data/lib/active_support/gzip.rb +2 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +123 -41
  171. data/lib/active_support/i18n.rb +4 -1
  172. data/lib/active_support/i18n_railtie.rb +19 -14
  173. data/lib/active_support/inflections.rb +2 -0
  174. data/lib/active_support/inflector/inflections.rb +19 -8
  175. data/lib/active_support/inflector/methods.rb +87 -77
  176. data/lib/active_support/inflector/transliterate.rb +56 -18
  177. data/lib/active_support/inflector.rb +2 -0
  178. data/lib/active_support/json/decoding.rb +27 -26
  179. data/lib/active_support/json/encoding.rb +13 -3
  180. data/lib/active_support/json.rb +2 -0
  181. data/lib/active_support/key_generator.rb +3 -33
  182. data/lib/active_support/lazy_load_hooks.rb +7 -2
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +2 -0
  186. data/lib/active_support/log_subscriber.rb +42 -11
  187. data/lib/active_support/logger.rb +4 -17
  188. data/lib/active_support/logger_silence.rb +13 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +54 -7
  190. data/lib/active_support/message_encryptor.rb +100 -32
  191. data/lib/active_support/message_verifier.rb +85 -14
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +12 -68
  196. data/lib/active_support/multibyte/unicode.rb +17 -327
  197. data/lib/active_support/multibyte.rb +2 -0
  198. data/lib/active_support/notifications/fanout.rb +118 -16
  199. data/lib/active_support/notifications/instrumenter.rb +73 -9
  200. data/lib/active_support/notifications.rb +74 -8
  201. data/lib/active_support/number_helper/number_converter.rb +7 -6
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -2
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -3
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -2
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
  209. data/lib/active_support/number_helper/rounding_helper.rb +16 -30
  210. data/lib/active_support/number_helper.rb +40 -12
  211. data/lib/active_support/option_merger.rb +24 -3
  212. data/lib/active_support/ordered_hash.rb +3 -1
  213. data/lib/active_support/ordered_options.rb +17 -5
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +4 -1
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +3 -10
  218. data/lib/active_support/railtie.rb +60 -9
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +7 -6
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +6 -3
  224. data/lib/active_support/subscriber.rb +74 -24
  225. data/lib/active_support/tagged_logging.rb +44 -8
  226. data/lib/active_support/test_case.rb +94 -2
  227. data/lib/active_support/testing/assertions.rb +58 -20
  228. data/lib/active_support/testing/autorun.rb +2 -0
  229. data/lib/active_support/testing/constant_lookup.rb +2 -0
  230. data/lib/active_support/testing/declarative.rb +2 -0
  231. data/lib/active_support/testing/deprecation.rb +2 -1
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +4 -2
  234. data/lib/active_support/testing/method_call_assertions.rb +30 -1
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +12 -7
  239. data/lib/active_support/testing/stream.rb +3 -2
  240. data/lib/active_support/testing/tagged_logging.rb +2 -0
  241. data/lib/active_support/testing/time_helpers.rb +78 -13
  242. data/lib/active_support/time.rb +2 -0
  243. data/lib/active_support/time_with_zone.rb +113 -41
  244. data/lib/active_support/values/time_zone.rb +54 -25
  245. data/lib/active_support/version.rb +2 -0
  246. data/lib/active_support/xml_mini/jdom.rb +5 -4
  247. data/lib/active_support/xml_mini/libxml.rb +4 -2
  248. data/lib/active_support/xml_mini/libxmlsax.rb +6 -4
  249. data/lib/active_support/xml_mini/nokogiri.rb +4 -2
  250. data/lib/active_support/xml_mini/nokogirisax.rb +5 -3
  251. data/lib/active_support/xml_mini/rexml.rb +12 -3
  252. data/lib/active_support/xml_mini.rb +5 -11
  253. data/lib/active_support.rb +18 -13
  254. metadata +71 -32
  255. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  256. data/lib/active_support/core_ext/hash/compact.rb +0 -27
  257. data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
  258. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  259. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  260. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  261. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  262. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/inflections"
2
- require "active_support/core_ext/regexp"
4
+ require "active_support/core_ext/object/blank"
3
5
 
4
6
  module ActiveSupport
5
7
  # The Inflector transforms words from singular to plural, class names to table
@@ -28,7 +30,7 @@ module ActiveSupport
28
30
  # pluralize('CamelOctopus') # => "CamelOctopi"
29
31
  # pluralize('ley', :es) # => "leyes"
30
32
  def pluralize(word, locale = :en)
31
- apply_inflections(word, inflections(locale).plurals)
33
+ apply_inflections(word, inflections(locale).plurals, locale)
32
34
  end
33
35
 
34
36
  # The reverse of #pluralize, returns the singular form of a word in a
@@ -45,7 +47,7 @@ module ActiveSupport
45
47
  # singularize('CamelOctopi') # => "CamelOctopus"
46
48
  # singularize('leyes', :es) # => "ley"
47
49
  def singularize(word, locale = :en)
48
- apply_inflections(word, inflections(locale).singulars)
50
+ apply_inflections(word, inflections(locale).singulars, locale)
49
51
  end
50
52
 
51
53
  # Converts strings to UpperCamelCase.
@@ -69,10 +71,10 @@ module ActiveSupport
69
71
  if uppercase_first_letter
70
72
  string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
71
73
  else
72
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
74
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
73
75
  end
74
76
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
75
- string.gsub!("/".freeze, "::".freeze)
77
+ string.gsub!("/", "::")
76
78
  string
77
79
  end
78
80
 
@@ -89,11 +91,11 @@ module ActiveSupport
89
91
  # camelize(underscore('SSLError')) # => "SslError"
90
92
  def underscore(camel_cased_word)
91
93
  return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
92
- word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
93
- word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }
94
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
95
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
96
- 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!("-", "_")
97
99
  word.downcase!
98
100
  word
99
101
  end
@@ -108,33 +110,38 @@ module ActiveSupport
108
110
  # * Replaces underscores with spaces, if any.
109
111
  # * Downcases all words except acronyms.
110
112
  # * Capitalizes the first word.
111
- #
112
113
  # The capitalization of the first word can be turned off by setting the
113
114
  # +:capitalize+ option to false (default is true).
114
115
  #
115
- # humanize('employee_salary') # => "Employee salary"
116
- # humanize('author_id') # => "Author"
117
- # humanize('author_id', capitalize: false) # => "author"
118
- # humanize('_id') # => "Id"
116
+ # The trailing '_id' can be kept and capitalized by setting the
117
+ # optional parameter +keep_id_suffix+ to true (default is false).
118
+ #
119
+ # humanize('employee_salary') # => "Employee salary"
120
+ # humanize('author_id') # => "Author"
121
+ # humanize('author_id', capitalize: false) # => "author"
122
+ # humanize('_id') # => "Id"
123
+ # humanize('author_id', keep_id_suffix: true) # => "Author Id"
119
124
  #
120
125
  # If "SSL" was defined to be an acronym:
121
126
  #
122
127
  # humanize('ssl_error') # => "SSL error"
123
128
  #
124
- def humanize(lower_case_and_underscored_word, options = {})
129
+ def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
125
130
  result = lower_case_and_underscored_word.to_s.dup
126
131
 
127
132
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
128
133
 
129
- result.sub!(/\A_+/, "".freeze)
130
- result.sub!(/_id\z/, "".freeze)
131
- result.tr!("_".freeze, " ".freeze)
134
+ result.sub!(/\A_+/, "")
135
+ unless keep_id_suffix
136
+ result.delete_suffix!("_id")
137
+ end
138
+ result.tr!("_", " ")
132
139
 
133
140
  result.gsub!(/([a-z\d]*)/i) do |match|
134
- "#{inflections.acronyms[match] || match.downcase}"
141
+ "#{inflections.acronyms[match.downcase] || match.downcase}"
135
142
  end
136
143
 
137
- if options.fetch(:capitalize, true)
144
+ if capitalize
138
145
  result.sub!(/\A\w/) { |match| match.upcase }
139
146
  end
140
147
 
@@ -154,14 +161,21 @@ module ActiveSupport
154
161
  # create a nicer looking title. +titleize+ is meant for creating pretty
155
162
  # output. It is not used in the Rails internals.
156
163
  #
164
+ # The trailing '_id','Id'.. can be kept and capitalized by setting the
165
+ # optional parameter +keep_id_suffix+ to true.
166
+ # By default, this parameter is false.
167
+ #
157
168
  # +titleize+ is also aliased as +titlecase+.
158
169
  #
159
- # titleize('man from the boondocks') # => "Man From The Boondocks"
160
- # titleize('x-men: the last stand') # => "X Men: The Last Stand"
161
- # titleize('TheManWithoutAPast') # => "The Man Without A Past"
162
- # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
163
- def titleize(word)
164
- humanize(underscore(word)).gsub(/\b(?<!\w['’`])[a-z]/) { |match| match.capitalize }
170
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
171
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
172
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
173
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
174
+ # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
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|
177
+ match.capitalize
178
+ end
165
179
  end
166
180
 
167
181
  # Creates the name of a table like Rails does for models to table names.
@@ -183,17 +197,17 @@ module ActiveSupport
183
197
  #
184
198
  # Singular names are not handled correctly:
185
199
  #
186
- # classify('calculus') # => "Calculus"
200
+ # classify('calculus') # => "Calculu"
187
201
  def classify(table_name)
188
202
  # strip out any leading schema name
189
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
203
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
190
204
  end
191
205
 
192
206
  # Replaces underscores with dashes in the string.
193
207
  #
194
208
  # dasherize('puni_puni') # => "puni-puni"
195
209
  def dasherize(underscored_word)
196
- underscored_word.tr("_".freeze, "-".freeze)
210
+ underscored_word.tr("_", "-")
197
211
  end
198
212
 
199
213
  # Removes the module part from the expression in the string.
@@ -256,32 +270,36 @@ module ActiveSupport
256
270
  # NameError is raised when the name is not in CamelCase or the constant is
257
271
  # unknown.
258
272
  def constantize(camel_cased_word)
259
- names = camel_cased_word.split("::".freeze)
260
-
261
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
262
- Object.const_get(camel_cased_word) if names.empty?
263
-
264
- # Remove the first blank element in case of '::ClassName' notation.
265
- names.shift if names.size > 1 && names.first.empty?
266
-
267
- names.inject(Object) do |constant, name|
268
- if constant == Object
269
- constant.const_get(name)
270
- else
271
- candidate = constant.const_get(name)
272
- next candidate if constant.const_defined?(name, false)
273
- next candidate unless Object.const_defined?(name)
274
-
275
- # Go down the ancestors to check if it is owned directly. The check
276
- # stops when we reach Object or the end of ancestors tree.
277
- constant = constant.ancestors.inject(constant) do |const, ancestor|
278
- break const if ancestor == Object
279
- break ancestor if ancestor.const_defined?(name, false)
280
- 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)
281
302
  end
282
-
283
- # owner is in Object, so raise
284
- constant.const_get(name, false)
285
303
  end
286
304
  end
287
305
  end
@@ -313,8 +331,9 @@ module ActiveSupport
313
331
  rescue NameError => e
314
332
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
315
333
  e.name.to_s == camel_cased_word.to_s)
316
- rescue ArgumentError => e
317
- raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
334
+ rescue LoadError => e
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)
318
337
  end
319
338
 
320
339
  # Returns the suffix that should be added to a number to denote the position
@@ -327,18 +346,7 @@ module ActiveSupport
327
346
  # ordinal(-11) # => "th"
328
347
  # ordinal(-1021) # => "st"
329
348
  def ordinal(number)
330
- abs_number = number.to_i.abs
331
-
332
- if (11..13).include?(abs_number % 100)
333
- "th"
334
- else
335
- case abs_number % 10
336
- when 1; "st"
337
- when 2; "nd"
338
- when 3; "rd"
339
- else "th"
340
- end
341
- end
349
+ I18n.translate("number.nth.ordinals", number: number)
342
350
  end
343
351
 
344
352
  # Turns a number into an ordinal string used to denote the position in an
@@ -351,36 +359,38 @@ module ActiveSupport
351
359
  # ordinalize(-11) # => "-11th"
352
360
  # ordinalize(-1021) # => "-1021st"
353
361
  def ordinalize(number)
354
- "#{number}#{ordinal(number)}"
362
+ I18n.translate("number.nth.ordinalized", number: number)
355
363
  end
356
364
 
357
365
  private
358
-
359
366
  # Mounts a regular expression, returned as a string to ease interpolation,
360
367
  # that will match part by part the given constant.
361
368
  #
362
369
  # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
363
370
  # const_regexp("::") # => "::"
364
371
  def const_regexp(camel_cased_word)
365
- parts = camel_cased_word.split("::".freeze)
372
+ parts = camel_cased_word.split("::")
366
373
 
367
374
  return Regexp.escape(camel_cased_word) if parts.blank?
368
375
 
369
376
  last = parts.pop
370
377
 
371
- parts.reverse.inject(last) do |acc, part|
378
+ parts.reverse!.inject(last) do |acc, part|
372
379
  part.empty? ? acc : "#{part}(::#{acc})?"
373
380
  end
374
381
  end
375
382
 
376
383
  # Applies inflection rules for +singularize+ and +pluralize+.
377
384
  #
378
- # apply_inflections('post', inflections.plurals) # => "posts"
379
- # apply_inflections('posts', inflections.singulars) # => "post"
380
- def apply_inflections(word, rules)
385
+ # If passed an optional +locale+ parameter, the uncountables will be
386
+ # found for that locale.
387
+ #
388
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
389
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
390
+ def apply_inflections(word, rules, locale = :en)
381
391
  result = word.to_s.dup
382
392
 
383
- if word.empty? || inflections.uncountables.uncountable?(result)
393
+ if word.empty? || inflections(locale).uncountables.uncountable?(result)
384
394
  result
385
395
  else
386
396
  rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/string/multibyte"
2
4
  require "active_support/i18n"
3
5
 
4
6
  module ActiveSupport
5
7
  module Inflector
8
+ ALLOWED_ENCODINGS_FOR_TRANSLITERATE = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030].freeze
9
+
6
10
  # Replaces non-ASCII characters with an ASCII approximation, or if none
7
11
  # exists, a replacement character which defaults to "?".
8
12
  #
@@ -49,46 +53,80 @@ module ActiveSupport
49
53
  #
50
54
  # Now you can have different transliterations for each locale:
51
55
  #
52
- # I18n.locale = :en
53
- # transliterate('Jürgen')
56
+ # transliterate('Jürgen', locale: :en)
54
57
  # # => "Jurgen"
55
58
  #
56
- # I18n.locale = :de
57
- # transliterate('Jürgen')
59
+ # transliterate('Jürgen', locale: :de)
58
60
  # # => "Juergen"
59
- 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?
60
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
61
75
 
62
- I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
63
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
64
- replacement: replacement)
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
91
+
92
+ transliterated
65
93
  end
66
94
 
67
95
  # Replaces special characters in a string so that it may be used as part of
68
96
  # a 'pretty' URL.
69
97
  #
70
98
  # parameterize("Donald E. Knuth") # => "donald-e-knuth"
71
- # parameterize("^trés|Jolie-- ") # => "tres-jolie"
99
+ # parameterize("^très|Jolie-- ") # => "tres-jolie"
72
100
  #
73
- # To use a custom separator, override the `separator` argument.
101
+ # To use a custom separator, override the +separator+ argument.
74
102
  #
75
- # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
76
- # parameterize("^trés|Jolie-- ", separator: '_') # => "tres_jolie"
103
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
104
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
77
105
  #
78
- # To preserve the case of the characters in a string, use the `preserve_case` argument.
106
+ # To preserve the case of the characters in a string, use the +preserve_case+ argument.
79
107
  #
80
108
  # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
81
- # parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie"
109
+ # parameterize("^très|Jolie-- ", preserve_case: true) # => "tres-Jolie"
110
+ #
111
+ # It preserves dashes and underscores unless they are used as separators:
112
+ #
113
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
114
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
115
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
82
116
  #
83
- 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)
84
122
  # Replace accented chars with their ASCII equivalents.
85
- parameterized_string = transliterate(string)
123
+ parameterized_string = transliterate(string, locale: locale)
86
124
 
87
125
  # Turn unwanted chars into the separator.
88
126
  parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
89
127
 
90
128
  unless separator.nil? || separator.empty?
91
- if separator == "-".freeze
129
+ if separator == "-"
92
130
  re_duplicate_separator = /-{2,}/
93
131
  re_leading_trailing_separator = /^-|-$/i
94
132
  else
@@ -99,7 +137,7 @@ module ActiveSupport
99
137
  # No more than one of the separator in a row.
100
138
  parameterized_string.gsub!(re_duplicate_separator, separator)
101
139
  # Remove leading/trailing separator.
102
- parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
140
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
103
141
  end
104
142
 
105
143
  parameterized_string.downcase! unless preserve_case
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # in case active_support/inflector is required without the rest of active_support
2
4
  require "active_support/inflector/inflections"
3
5
  require "active_support/inflector/transliterate"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/attribute_accessors"
2
4
  require "active_support/core_ext/module/delegation"
3
5
  require "json"
@@ -8,8 +10,8 @@ module ActiveSupport
8
10
 
9
11
  module JSON
10
12
  # matches YAML-formatted dates
11
- DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
12
- 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/
13
15
 
14
16
  class << self
15
17
  # Parses a JSON string (JavaScript Object Notation) into a hash.
@@ -42,33 +44,32 @@ module ActiveSupport
42
44
  end
43
45
 
44
46
  private
45
-
46
- def convert_dates_from(data)
47
- case data
48
- when nil
49
- nil
50
- when DATE_REGEX
51
- begin
52
- Date.parse(data)
53
- rescue ArgumentError
54
- data
55
- end
56
- when DATETIME_REGEX
57
- begin
58
- Time.zone.parse(data)
59
- 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
60
70
  data
61
71
  end
62
- when Array
63
- data.map! { |d| convert_dates_from(d) }
64
- when Hash
65
- data.each do |key, value|
66
- data[key] = convert_dates_from(value)
67
- end
68
- else
69
- data
70
72
  end
71
- end
72
73
  end
73
74
  end
74
75
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/object/json"
2
4
  require "active_support/core_ext/module/delegation"
3
5
 
@@ -52,9 +54,13 @@ module ActiveSupport
52
54
  class EscapedString < String #:nodoc:
53
55
  def to_json(*)
54
56
  if Encoding.escape_html_entities_in_json
55
- super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
57
+ s = super
58
+ s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
59
+ s
56
60
  else
57
- super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
61
+ s = super
62
+ s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
63
+ s
58
64
  end
59
65
  end
60
66
 
@@ -87,7 +93,11 @@ module ActiveSupport
87
93
  when Numeric, NilClass, TrueClass, FalseClass
88
94
  value.as_json
89
95
  when Hash
90
- 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
91
101
  when Array
92
102
  value.map { |v| jsonify(v) }
93
103
  else
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/json/decoding"
2
4
  require "active_support/json/encoding"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "concurrent/map"
2
4
  require "openssl"
3
5
 
@@ -33,39 +35,7 @@ module ActiveSupport
33
35
 
34
36
  # Returns a derived key suitable for use.
35
37
  def generate_key(*args)
36
- @cache_keys[args.join] ||= @key_generator.generate_key(*args)
37
- end
38
- end
39
-
40
- class LegacyKeyGenerator # :nodoc:
41
- SECRET_MIN_LENGTH = 30 # Characters
42
-
43
- def initialize(secret)
44
- ensure_secret_secure(secret)
45
- @secret = secret
38
+ @cache_keys[args.join("|")] ||= @key_generator.generate_key(*args)
46
39
  end
47
-
48
- def generate_key(salt)
49
- @secret
50
- end
51
-
52
- private
53
-
54
- # To prevent users from using something insecure like "Password" we make sure that the
55
- # secret they've provided is at least 30 characters in length.
56
- def ensure_secret_secure(secret)
57
- if secret.blank?
58
- raise ArgumentError, "A secret is required to generate an integrity hash " \
59
- "for cookie session data. Set a secret_key_base of at least " \
60
- "#{SECRET_MIN_LENGTH} characters in config/secrets.yml."
61
- end
62
-
63
- if secret.length < SECRET_MIN_LENGTH
64
- raise ArgumentError, "Secret should be something secure, " \
65
- "like \"#{SecureRandom.hex(16)}\". The value you " \
66
- "provided, \"#{secret}\", is shorter than the minimum length " \
67
- "of #{SECRET_MIN_LENGTH} characters."
68
- end
69
- end
70
40
  end
71
41
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  # lazy_load_hooks allows Rails to lazily load a lot of components and thus
3
5
  # making the app boot faster. Because of this feature now there is no need to
@@ -52,7 +54,6 @@ module ActiveSupport
52
54
  end
53
55
 
54
56
  private
55
-
56
57
  def with_execution_control(name, block, once)
57
58
  unless @run_once[name].include?(block)
58
59
  @run_once[name] << block if once
@@ -66,7 +67,11 @@ module ActiveSupport
66
67
  if options[:yield]
67
68
  block.call(base)
68
69
  else
69
- base.instance_eval(&block)
70
+ if base.is_a?(Module)
71
+ base.class_eval(&block)
72
+ else
73
+ base.instance_eval(&block)
74
+ end
70
75
  end
71
76
  end
72
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
+ }