activesupport 5.1.7 → 7.0.4.1

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

Potentially problematic release.


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

Files changed (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +259 -585
  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 +4 -2
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +2 -0
  11. data/lib/active_support/cache/file_store.rb +50 -43
  12. data/lib/active_support/cache/mem_cache_store.rb +194 -67
  13. data/lib/active_support/cache/memory_store.rb +70 -34
  14. data/lib/active_support/cache/null_store.rb +18 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +474 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +73 -50
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/cache.rb +556 -220
  19. data/lib/active_support/callbacks.rb +264 -159
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +81 -8
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  23. data/lib/active_support/concurrency/share_lock.rb +4 -3
  24. data/lib/active_support/configurable.rb +17 -16
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +18 -8
  27. data/lib/active_support/core_ext/array/conversions.rb +20 -17
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +8 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +4 -2
  33. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  34. data/lib/active_support/core_ext/array.rb +4 -1
  35. data/lib/active_support/core_ext/benchmark.rb +4 -2
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +3 -1
  37. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  38. data/lib/active_support/core_ext/class/attribute.rb +50 -47
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  40. data/lib/active_support/core_ext/class/subclasses.rb +10 -24
  41. data/lib/active_support/core_ext/class.rb +2 -0
  42. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  43. data/lib/active_support/core_ext/date/blank.rb +3 -1
  44. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  45. data/lib/active_support/core_ext/date/conversions.rb +24 -22
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +2 -0
  48. data/lib/active_support/core_ext/date.rb +3 -0
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +65 -41
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  54. data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  56. data/lib/active_support/core_ext/date_time/conversions.rb +15 -14
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +3 -0
  59. data/lib/active_support/core_ext/digest/uuid.rb +42 -14
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +244 -72
  62. data/lib/active_support/core_ext/file/atomic.rb +6 -2
  63. data/lib/active_support/core_ext/file.rb +2 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +7 -6
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +4 -2
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +4 -31
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  72. data/lib/active_support/core_ext/hash.rb +3 -2
  73. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +7 -14
  76. data/lib/active_support/core_ext/integer.rb +2 -0
  77. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +6 -4
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +3 -1
  80. data/lib/active_support/core_ext/kernel.rb +2 -1
  81. data/lib/active_support/core_ext/load_error.rb +3 -8
  82. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  83. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  84. data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +46 -56
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +36 -27
  87. data/lib/active_support/core_ext/module/concerning.rb +15 -10
  88. data/lib/active_support/core_ext/module/delegation.rb +97 -58
  89. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  90. data/lib/active_support/core_ext/module/introspection.rb +18 -15
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  93. data/lib/active_support/core_ext/module.rb +3 -1
  94. data/lib/active_support/core_ext/name_error.rb +30 -2
  95. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +134 -129
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  99. data/lib/active_support/core_ext/numeric.rb +3 -1
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +15 -5
  102. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  103. data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
  104. data/lib/active_support/core_ext/object/duplicable.rb +16 -110
  105. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  106. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  107. data/lib/active_support/core_ext/object/json.rb +51 -26
  108. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  109. data/lib/active_support/core_ext/object/to_query.rb +4 -2
  110. data/lib/active_support/core_ext/object/try.rb +26 -14
  111. data/lib/active_support/core_ext/object/with_options.rb +24 -3
  112. data/lib/active_support/core_ext/object.rb +2 -0
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +6 -3
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +3 -1
  121. data/lib/active_support/core_ext/range.rb +4 -1
  122. data/lib/active_support/core_ext/regexp.rb +10 -5
  123. data/lib/active_support/core_ext/securerandom.rb +25 -3
  124. data/lib/active_support/core_ext/string/access.rb +7 -16
  125. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  126. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  127. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  128. data/lib/active_support/core_ext/string/filters.rb +44 -1
  129. data/lib/active_support/core_ext/string/indent.rb +2 -0
  130. data/lib/active_support/core_ext/string/inflections.rb +69 -16
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +9 -4
  133. data/lib/active_support/core_ext/string/output_safety.rb +135 -27
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +5 -1
  136. data/lib/active_support/core_ext/string/zones.rb +2 -0
  137. data/lib/active_support/core_ext/string.rb +2 -0
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  141. data/lib/active_support/core_ext/time/calculations.rb +81 -24
  142. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  143. data/lib/active_support/core_ext/time/conversions.rb +17 -12
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +12 -25
  146. data/lib/active_support/core_ext/time.rb +3 -0
  147. data/lib/active_support/core_ext/uri.rb +4 -23
  148. data/lib/active_support/core_ext.rb +4 -1
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +2 -0
  152. data/lib/active_support/dependencies/interlock.rb +12 -18
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +59 -715
  155. data/lib/active_support/deprecation/behaviors.rb +48 -13
  156. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +2 -1
  159. data/lib/active_support/deprecation/method_wrappers.rb +29 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -8
  161. data/lib/active_support/deprecation/reporting.rb +54 -9
  162. data/lib/active_support/deprecation.rb +10 -3
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +9 -9
  166. data/lib/active_support/duration/iso8601_serializer.rb +29 -15
  167. data/lib/active_support/duration.rb +158 -72
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +129 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +87 -122
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +46 -21
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +2 -0
  178. data/lib/active_support/file_update_checker.rb +2 -1
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +7 -5
  181. data/lib/active_support/gzip.rb +2 -0
  182. data/lib/active_support/hash_with_indifferent_access.rb +126 -42
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +5 -1
  185. data/lib/active_support/i18n_railtie.rb +19 -14
  186. data/lib/active_support/inflections.rb +2 -0
  187. data/lib/active_support/inflector/inflections.rb +41 -14
  188. data/lib/active_support/inflector/methods.rb +73 -87
  189. data/lib/active_support/inflector/transliterate.rb +56 -18
  190. data/lib/active_support/inflector.rb +2 -0
  191. data/lib/active_support/isolated_execution_state.rb +72 -0
  192. data/lib/active_support/json/decoding.rb +27 -26
  193. data/lib/active_support/json/encoding.rb +16 -6
  194. data/lib/active_support/json.rb +2 -0
  195. data/lib/active_support/key_generator.rb +25 -38
  196. data/lib/active_support/lazy_load_hooks.rb +35 -6
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +8 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +4 -2
  200. data/lib/active_support/log_subscriber.rb +54 -13
  201. data/lib/active_support/logger.rb +4 -17
  202. data/lib/active_support/logger_silence.rb +13 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +48 -10
  204. data/lib/active_support/message_encryptor.rb +111 -37
  205. data/lib/active_support/message_verifier.rb +124 -21
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +19 -76
  210. data/lib/active_support/multibyte/unicode.rb +9 -331
  211. data/lib/active_support/multibyte.rb +3 -1
  212. data/lib/active_support/notifications/fanout.rb +165 -37
  213. data/lib/active_support/notifications/instrumenter.rb +92 -11
  214. data/lib/active_support/notifications.rb +96 -30
  215. data/lib/active_support/number_helper/number_converter.rb +8 -9
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -12
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +6 -3
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +7 -4
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +6 -3
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
  223. data/lib/active_support/number_helper/rounding_helper.rb +16 -34
  224. data/lib/active_support/number_helper.rb +38 -12
  225. data/lib/active_support/option_merger.rb +19 -6
  226. data/lib/active_support/ordered_hash.rb +4 -2
  227. data/lib/active_support/ordered_options.rb +18 -6
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +8 -1
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +3 -10
  232. data/lib/active_support/railtie.rb +112 -11
  233. data/lib/active_support/reloader.rb +12 -11
  234. data/lib/active_support/rescuable.rb +19 -18
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +26 -15
  238. data/lib/active_support/string_inquirer.rb +4 -3
  239. data/lib/active_support/subscriber.rb +81 -42
  240. data/lib/active_support/tagged_logging.rb +45 -9
  241. data/lib/active_support/test_case.rb +86 -2
  242. data/lib/active_support/testing/assertions.rb +89 -21
  243. data/lib/active_support/testing/autorun.rb +2 -0
  244. data/lib/active_support/testing/constant_lookup.rb +2 -0
  245. data/lib/active_support/testing/declarative.rb +2 -0
  246. data/lib/active_support/testing/deprecation.rb +54 -2
  247. data/lib/active_support/testing/file_fixtures.rb +4 -0
  248. data/lib/active_support/testing/isolation.rb +6 -4
  249. data/lib/active_support/testing/method_call_assertions.rb +34 -5
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +12 -7
  255. data/lib/active_support/testing/stream.rb +6 -7
  256. data/lib/active_support/testing/tagged_logging.rb +3 -1
  257. data/lib/active_support/testing/time_helpers.rb +91 -15
  258. data/lib/active_support/time.rb +2 -0
  259. data/lib/active_support/time_with_zone.rb +168 -56
  260. data/lib/active_support/values/time_zone.rb +85 -37
  261. data/lib/active_support/version.rb +3 -1
  262. data/lib/active_support/xml_mini/jdom.rb +6 -5
  263. data/lib/active_support/xml_mini/libxml.rb +9 -7
  264. data/lib/active_support/xml_mini/libxmlsax.rb +7 -5
  265. data/lib/active_support/xml_mini/nokogiri.rb +8 -6
  266. data/lib/active_support/xml_mini/nokogirisax.rb +6 -4
  267. data/lib/active_support/xml_mini/rexml.rb +13 -4
  268. data/lib/active_support/xml_mini.rb +10 -15
  269. data/lib/active_support.rb +30 -9
  270. metadata +76 -35
  271. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  272. data/lib/active_support/core_ext/hash/compact.rb +0 -27
  273. data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
  274. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  275. data/lib/active_support/core_ext/marshal.rb +0 -22
  276. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  277. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  278. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  279. 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.
@@ -66,13 +68,17 @@ module ActiveSupport
66
68
  # camelize(underscore('SSLError')) # => "SslError"
67
69
  def camelize(term, uppercase_first_letter = true)
68
70
  string = term.to_s
69
- if uppercase_first_letter
70
- 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 }
71
74
  else
72
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |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
73
81
  end
74
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
75
- string.gsub!("/".freeze, "::".freeze)
76
82
  string
77
83
  end
78
84
 
@@ -88,12 +94,11 @@ module ActiveSupport
88
94
  #
89
95
  # camelize(underscore('SSLError')) # => "SslError"
90
96
  def underscore(camel_cased_word)
91
- 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)
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!("-", "_")
97
102
  word.downcase!
98
103
  word
99
104
  end
@@ -104,38 +109,47 @@ module ActiveSupport
104
109
  #
105
110
  # * Applies human inflection rules to the argument.
106
111
  # * Deletes leading underscores, if any.
107
- # * Removes a "_id" suffix if present.
112
+ # * Removes an "_id" suffix if present.
108
113
  # * Replaces underscores with spaces, if any.
109
114
  # * Downcases all words except acronyms.
110
115
  # * Capitalizes the first word.
111
- #
112
116
  # The capitalization of the first word can be turned off by setting the
113
117
  # +:capitalize+ option to false (default is true).
114
118
  #
115
- # humanize('employee_salary') # => "Employee salary"
116
- # humanize('author_id') # => "Author"
117
- # humanize('author_id', capitalize: false) # => "author"
118
- # humanize('_id') # => "Id"
119
+ # The trailing '_id' can be kept and capitalized by setting the
120
+ # optional parameter +keep_id_suffix+ to true (default is false).
121
+ #
122
+ # humanize('employee_salary') # => "Employee salary"
123
+ # humanize('author_id') # => "Author"
124
+ # humanize('author_id', capitalize: false) # => "author"
125
+ # humanize('_id') # => "Id"
126
+ # humanize('author_id', keep_id_suffix: true) # => "Author id"
119
127
  #
120
128
  # If "SSL" was defined to be an acronym:
121
129
  #
122
130
  # humanize('ssl_error') # => "SSL error"
123
131
  #
124
- def humanize(lower_case_and_underscored_word, options = {})
132
+ def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
125
133
  result = lower_case_and_underscored_word.to_s.dup
126
134
 
127
135
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
128
136
 
129
- result.sub!(/\A_+/, "".freeze)
130
- result.sub!(/_id\z/, "".freeze)
131
- result.tr!("_".freeze, " ".freeze)
137
+ result.tr!("_", " ")
138
+ result.lstrip!
139
+ unless keep_id_suffix
140
+ result.delete_suffix!(" id")
141
+ end
132
142
 
133
- result.gsub!(/([a-z\d]*)/i) do |match|
134
- "#{inflections.acronyms[match] || match.downcase}"
143
+ result.gsub!(/([a-z\d]+)/i) do |match|
144
+ match.downcase!
145
+ inflections.acronyms[match] || match
135
146
  end
136
147
 
137
- if options.fetch(:capitalize, true)
138
- result.sub!(/\A\w/) { |match| match.upcase }
148
+ if capitalize
149
+ result.sub!(/\A\w/) do |match|
150
+ match.upcase!
151
+ match
152
+ end
139
153
  end
140
154
 
141
155
  result
@@ -154,14 +168,21 @@ module ActiveSupport
154
168
  # create a nicer looking title. +titleize+ is meant for creating pretty
155
169
  # output. It is not used in the Rails internals.
156
170
  #
171
+ # The trailing '_id','Id'.. can be kept and capitalized by setting the
172
+ # optional parameter +keep_id_suffix+ to true.
173
+ # By default, this parameter is false.
174
+ #
157
175
  # +titleize+ is also aliased as +titlecase+.
158
176
  #
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 }
177
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
178
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
179
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
180
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
181
+ # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
182
+ def titleize(word, keep_id_suffix: false)
183
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])[a-z]/) do |match|
184
+ match.capitalize
185
+ end
165
186
  end
166
187
 
167
188
  # Creates the name of a table like Rails does for models to table names.
@@ -183,17 +204,17 @@ module ActiveSupport
183
204
  #
184
205
  # Singular names are not handled correctly:
185
206
  #
186
- # classify('calculus') # => "Calculus"
207
+ # classify('calculus') # => "Calculu"
187
208
  def classify(table_name)
188
209
  # strip out any leading schema name
189
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
210
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
190
211
  end
191
212
 
192
213
  # Replaces underscores with dashes in the string.
193
214
  #
194
215
  # dasherize('puni_puni') # => "puni-puni"
195
216
  def dasherize(underscored_word)
196
- underscored_word.tr("_".freeze, "-".freeze)
217
+ underscored_word.tr("_", "-")
197
218
  end
198
219
 
199
220
  # Removes the module part from the expression in the string.
@@ -256,34 +277,7 @@ module ActiveSupport
256
277
  # NameError is raised when the name is not in CamelCase or the constant is
257
278
  # unknown.
258
279
  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
281
- end
282
-
283
- # owner is in Object, so raise
284
- constant.const_get(name, false)
285
- end
286
- end
280
+ Object.const_get(camel_cased_word)
287
281
  end
288
282
 
289
283
  # Tries to find a constant with the name specified in the argument string.
@@ -313,8 +307,9 @@ module ActiveSupport
313
307
  rescue NameError => e
314
308
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
315
309
  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)
310
+ rescue LoadError => e
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)
318
313
  end
319
314
 
320
315
  # Returns the suffix that should be added to a number to denote the position
@@ -327,18 +322,7 @@ module ActiveSupport
327
322
  # ordinal(-11) # => "th"
328
323
  # ordinal(-1021) # => "st"
329
324
  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
325
+ I18n.translate("number.nth.ordinals", number: number)
342
326
  end
343
327
 
344
328
  # Turns a number into an ordinal string used to denote the position in an
@@ -351,36 +335,38 @@ module ActiveSupport
351
335
  # ordinalize(-11) # => "-11th"
352
336
  # ordinalize(-1021) # => "-1021st"
353
337
  def ordinalize(number)
354
- "#{number}#{ordinal(number)}"
338
+ I18n.translate("number.nth.ordinalized", number: number)
355
339
  end
356
340
 
357
341
  private
358
-
359
342
  # Mounts a regular expression, returned as a string to ease interpolation,
360
343
  # that will match part by part the given constant.
361
344
  #
362
345
  # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
363
346
  # const_regexp("::") # => "::"
364
347
  def const_regexp(camel_cased_word)
365
- parts = camel_cased_word.split("::".freeze)
348
+ parts = camel_cased_word.split("::")
366
349
 
367
350
  return Regexp.escape(camel_cased_word) if parts.blank?
368
351
 
369
352
  last = parts.pop
370
353
 
371
- parts.reverse.inject(last) do |acc, part|
354
+ parts.reverse!.inject(last) do |acc, part|
372
355
  part.empty? ? acc : "#{part}(::#{acc})?"
373
356
  end
374
357
  end
375
358
 
376
359
  # Applies inflection rules for +singularize+ and +pluralize+.
377
360
  #
378
- # apply_inflections('post', inflections.plurals) # => "posts"
379
- # apply_inflections('posts', inflections.singulars) # => "post"
380
- def apply_inflections(word, rules)
361
+ # If passed an optional +locale+ parameter, the uncountables will be
362
+ # found for that locale.
363
+ #
364
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
365
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
366
+ def apply_inflections(word, rules, locale = :en)
381
367
  result = word.to_s.dup
382
368
 
383
- if word.empty? || inflections.uncountables.uncountable?(result)
369
+ if word.empty? || inflections(locale).uncountables.uncountable?(result)
384
370
  result
385
371
  else
386
372
  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"
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fiber"
4
+
5
+ module ActiveSupport
6
+ module IsolatedExecutionState # :nodoc:
7
+ @isolation_level = :thread
8
+
9
+ Thread.attr_accessor :active_support_execution_state
10
+ Fiber.attr_accessor :active_support_execution_state
11
+
12
+ class << self
13
+ attr_reader :isolation_level
14
+
15
+ def isolation_level=(level)
16
+ unless %i(thread fiber).include?(level)
17
+ raise ArgumentError, "isolation_level must be `:thread` or `:fiber`, got: `#{level.inspect}`"
18
+ end
19
+
20
+ if level != isolation_level
21
+ clear
22
+ singleton_class.alias_method(:current, "current_#{level}")
23
+ singleton_class.send(:private, :current)
24
+ @isolation_level = level
25
+ end
26
+ end
27
+
28
+ def unique_id
29
+ self[:__id__] ||= Object.new
30
+ end
31
+
32
+ def [](key)
33
+ current[key]
34
+ end
35
+
36
+ def []=(key, value)
37
+ current[key] = value
38
+ end
39
+
40
+ def key?(key)
41
+ current.key?(key)
42
+ end
43
+
44
+ def delete(key)
45
+ current.delete(key)
46
+ end
47
+
48
+ def clear
49
+ current.clear
50
+ end
51
+
52
+ def share_with(other)
53
+ # Action Controller streaming spawns a new thread and copy thread locals.
54
+ # We do the same here for backward compatibility, but this is very much a hack
55
+ # and streaming should be rethought.
56
+ context = @isolation_level == :thread ? Thread.current : Fiber.current
57
+ context.active_support_execution_state = other.active_support_execution_state.dup
58
+ end
59
+
60
+ private
61
+ def current_thread
62
+ Thread.current.active_support_execution_state ||= {}
63
+ end
64
+
65
+ def current_fiber
66
+ Fiber.current.active_support_execution_state ||= {}
67
+ end
68
+
69
+ alias_method :current, :current_thread
70
+ end
71
+ end
72
+ end
@@ -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
 
@@ -20,8 +22,8 @@ module ActiveSupport
20
22
  Encoding.json_encoder.new(options).encode(value)
21
23
  end
22
24
 
23
- module Encoding #:nodoc:
24
- class JSONGemEncoder #:nodoc:
25
+ module Encoding # :nodoc:
26
+ class JSONGemEncoder # :nodoc:
25
27
  attr_reader :options
26
28
 
27
29
  def initialize(options = nil)
@@ -49,12 +51,16 @@ module ActiveSupport
49
51
  ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
50
52
 
51
53
  # This class wraps all the strings we see and does the extra escaping
52
- class EscapedString < String #:nodoc:
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"