activesupport 4.2.11.1 → 6.1.7.3

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

Potentially problematic release.


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

Files changed (272) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +464 -391
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -7
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +34 -6
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +61 -55
  12. data/lib/active_support/cache/mem_cache_store.rb +115 -100
  13. data/lib/active_support/cache/memory_store.rb +81 -58
  14. data/lib/active_support/cache/null_store.rb +11 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +90 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +386 -225
  19. data/lib/active_support/callbacks.rb +661 -594
  20. data/lib/active_support/concern.rb +80 -7
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +226 -0
  23. data/lib/active_support/configurable.rb +16 -17
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +41 -1
  26. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  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 +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  32. data/lib/active_support/core_ext/array.rb +9 -6
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -48
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -25
  39. data/lib/active_support/core_ext/class.rb +4 -3
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +14 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  43. data/lib/active_support/core_ext/date/conversions.rb +27 -24
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +6 -4
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +167 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +19 -3
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -13
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +37 -19
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
  53. data/lib/active_support/core_ext/date_time/conversions.rb +16 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +8 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +186 -22
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +13 -10
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +20 -43
  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 +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +10 -8
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +63 -69
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +148 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +19 -14
  85. data/lib/active_support/core_ext/module/delegation.rb +164 -51
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -22
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  90. data/lib/active_support/core_ext/module.rb +13 -11
  91. data/lib/active_support/core_ext/name_error.rb +51 -4
  92. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +133 -136
  94. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  95. data/lib/active_support/core_ext/numeric.rb +5 -3
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +27 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  101. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  102. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  103. data/lib/active_support/core_ext/object/json.rb +63 -21
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +81 -23
  107. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  108. data/lib/active_support/core_ext/object.rb +14 -13
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  111. data/lib/active_support/core_ext/range/each.rb +18 -17
  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 +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +45 -0
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  119. data/lib/active_support/core_ext/string/conversions.rb +8 -4
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +48 -6
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +102 -26
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  126. data/lib/active_support/core_ext/string/output_safety.rb +125 -40
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +6 -5
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  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 +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +137 -53
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +22 -13
  137. data/lib/active_support/core_ext/time/zones.rb +41 -7
  138. data/lib/active_support/core_ext/time.rb +7 -6
  139. data/lib/active_support/core_ext/uri.rb +11 -8
  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 +57 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +241 -175
  147. data/lib/active_support/deprecation/behaviors.rb +58 -12
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  153. data/lib/active_support/deprecation/reporting.rb +81 -18
  154. data/lib/active_support/deprecation.rb +17 -9
  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 +123 -0
  158. data/lib/active_support/duration/iso8601_serializer.rb +59 -0
  159. data/lib/active_support/duration.rb +364 -39
  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 +170 -0
  164. data/lib/active_support/execution_wrapper.rb +132 -0
  165. data/lib/active_support/executor.rb +8 -0
  166. data/lib/active_support/file_update_checker.rb +62 -37
  167. data/lib/active_support/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +7 -5
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +171 -48
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +47 -16
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +58 -14
  175. data/lib/active_support/inflector/methods.rb +186 -169
  176. data/lib/active_support/inflector/transliterate.rb +83 -33
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +32 -30
  179. data/lib/active_support/json/encoding.rb +22 -61
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +11 -43
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +9 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  186. data/lib/active_support/log_subscriber.rb +52 -19
  187. data/lib/active_support/logger.rb +10 -24
  188. data/lib/active_support/logger_silence.rb +14 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -10
  190. data/lib/active_support/message_encryptor.rb +167 -57
  191. data/lib/active_support/message_verifier.rb +151 -18
  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 +35 -80
  196. data/lib/active_support/multibyte/unicode.rb +22 -330
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +125 -23
  199. data/lib/active_support/notifications/instrumenter.rb +98 -16
  200. data/lib/active_support/notifications.rb +82 -14
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -14
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +14 -11
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +12 -10
  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 +15 -5
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +29 -57
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +123 -71
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +7 -5
  213. data/lib/active_support/ordered_options.rb +35 -7
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +10 -4
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +10 -11
  218. data/lib/active_support/railtie.rb +66 -10
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +112 -57
  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 +13 -4
  224. data/lib/active_support/subscriber.rb +80 -31
  225. data/lib/active_support/tagged_logging.rb +54 -17
  226. data/lib/active_support/test_case.rb +107 -44
  227. data/lib/active_support/testing/assertions.rb +158 -20
  228. data/lib/active_support/testing/autorun.rb +4 -2
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +13 -10
  232. data/lib/active_support/testing/file_fixtures.rb +38 -0
  233. data/lib/active_support/testing/isolation.rb +35 -26
  234. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  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 +13 -8
  239. data/lib/active_support/testing/stream.rb +43 -0
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +121 -20
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +215 -51
  244. data/lib/active_support/values/time_zone.rb +223 -71
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +116 -115
  247. data/lib/active_support/xml_mini/libxml.rb +16 -13
  248. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  249. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  250. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +38 -46
  253. data/lib/active_support.rb +25 -11
  254. metadata +100 -43
  255. data/lib/active_support/concurrency/latch.rb +0 -27
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  258. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  259. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  260. data/lib/active_support/core_ext/hash/compact.rb +0 -24
  261. data/lib/active_support/core_ext/hash/transform_values.rb +0 -23
  262. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  263. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  264. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  265. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  266. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  267. data/lib/active_support/core_ext/object/itself.rb +0 -15
  268. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  269. data/lib/active_support/core_ext/struct.rb +0 -6
  270. data/lib/active_support/core_ext/thread.rb +0 -86
  271. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  272. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,6 +1,7 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'active_support/inflections'
3
+ require "active_support/inflections"
4
+ require "active_support/core_ext/object/blank"
4
5
 
5
6
  module ActiveSupport
6
7
  # The Inflector transforms words from singular to plural, class names to table
@@ -22,58 +23,58 @@ module ActiveSupport
22
23
  # pluralized using rules defined for that language. By default,
23
24
  # this parameter is set to <tt>:en</tt>.
24
25
  #
25
- # 'post'.pluralize # => "posts"
26
- # 'octopus'.pluralize # => "octopi"
27
- # 'sheep'.pluralize # => "sheep"
28
- # 'words'.pluralize # => "words"
29
- # 'CamelOctopus'.pluralize # => "CamelOctopi"
30
- # 'ley'.pluralize(:es) # => "leyes"
26
+ # pluralize('post') # => "posts"
27
+ # pluralize('octopus') # => "octopi"
28
+ # pluralize('sheep') # => "sheep"
29
+ # pluralize('words') # => "words"
30
+ # pluralize('CamelOctopus') # => "CamelOctopi"
31
+ # pluralize('ley', :es) # => "leyes"
31
32
  def pluralize(word, locale = :en)
32
- apply_inflections(word, inflections(locale).plurals)
33
+ apply_inflections(word, inflections(locale).plurals, locale)
33
34
  end
34
35
 
35
- # The reverse of +pluralize+, returns the singular form of a word in a
36
+ # The reverse of #pluralize, returns the singular form of a word in a
36
37
  # string.
37
38
  #
38
39
  # If passed an optional +locale+ parameter, the word will be
39
40
  # singularized using rules defined for that language. By default,
40
41
  # this parameter is set to <tt>:en</tt>.
41
42
  #
42
- # 'posts'.singularize # => "post"
43
- # 'octopi'.singularize # => "octopus"
44
- # 'sheep'.singularize # => "sheep"
45
- # 'word'.singularize # => "word"
46
- # 'CamelOctopi'.singularize # => "CamelOctopus"
47
- # 'leyes'.singularize(:es) # => "ley"
43
+ # singularize('posts') # => "post"
44
+ # singularize('octopi') # => "octopus"
45
+ # singularize('sheep') # => "sheep"
46
+ # singularize('word') # => "word"
47
+ # singularize('CamelOctopi') # => "CamelOctopus"
48
+ # singularize('leyes', :es) # => "ley"
48
49
  def singularize(word, locale = :en)
49
- apply_inflections(word, inflections(locale).singulars)
50
+ apply_inflections(word, inflections(locale).singulars, locale)
50
51
  end
51
52
 
52
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument
53
- # to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
53
+ # Converts strings to UpperCamelCase.
54
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
54
55
  # lowerCamelCase.
55
56
  #
56
- # +camelize+ will also convert '/' to '::' which is useful for converting
57
+ # Also converts '/' to '::' which is useful for converting
57
58
  # paths to namespaces.
58
59
  #
59
- # 'active_model'.camelize # => "ActiveModel"
60
- # 'active_model'.camelize(:lower) # => "activeModel"
61
- # 'active_model/errors'.camelize # => "ActiveModel::Errors"
62
- # 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
60
+ # camelize('active_model') # => "ActiveModel"
61
+ # camelize('active_model', false) # => "activeModel"
62
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
63
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
63
64
  #
64
65
  # As a rule of thumb you can think of +camelize+ as the inverse of
65
- # +underscore+, though there are cases where that does not hold:
66
+ # #underscore, though there are cases where that does not hold:
66
67
  #
67
- # 'SSLError'.underscore.camelize # => "SslError"
68
+ # camelize(underscore('SSLError')) # => "SslError"
68
69
  def camelize(term, uppercase_first_letter = true)
69
70
  string = term.to_s
70
71
  if uppercase_first_letter
71
- string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
72
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
72
73
  else
73
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
74
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
74
75
  end
75
76
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
76
- string.gsub!(/\//, '::')
77
+ string.gsub!("/", "::")
77
78
  string
78
79
  end
79
80
 
@@ -81,19 +82,18 @@ module ActiveSupport
81
82
  #
82
83
  # Changes '::' to '/' to convert namespaces to paths.
83
84
  #
84
- # 'ActiveModel'.underscore # => "active_model"
85
- # 'ActiveModel::Errors'.underscore # => "active_model/errors"
85
+ # underscore('ActiveModel') # => "active_model"
86
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
86
87
  #
87
88
  # As a rule of thumb you can think of +underscore+ as the inverse of
88
- # +camelize+, though there are cases where that does not hold:
89
+ # #camelize, though there are cases where that does not hold:
89
90
  #
90
- # 'SSLError'.underscore.camelize # => "SslError"
91
+ # camelize(underscore('SSLError')) # => "SslError"
91
92
  def underscore(camel_cased_word)
92
- return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
93
- word = camel_cased_word.to_s.gsub(/::/, '/')
94
- word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'}#{$2.downcase}" }
95
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
96
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
93
+ return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
94
+ word = camel_cased_word.to_s.gsub("::", "/")
95
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
96
+ word.gsub!(/([A-Z])(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
97
97
  word.tr!("-", "_")
98
98
  word.downcase!
99
99
  word
@@ -101,105 +101,126 @@ module ActiveSupport
101
101
 
102
102
  # Tweaks an attribute name for display to end users.
103
103
  #
104
- # Specifically, +humanize+ performs these transformations:
105
- #
106
- # * Applies human inflection rules to the argument.
107
- # * Deletes leading underscores, if any.
108
- # * Removes a "_id" suffix if present.
109
- # * Replaces underscores with spaces, if any.
110
- # * Downcases all words except acronyms.
111
- # * Capitalizes the first word.
104
+ # Specifically, performs these transformations:
112
105
  #
106
+ # * Applies human inflection rules to the argument.
107
+ # * Deletes leading underscores, if any.
108
+ # * Removes a "_id" suffix if present.
109
+ # * Replaces underscores with spaces, if any.
110
+ # * Downcases all words except acronyms.
111
+ # * Capitalizes the first word.
113
112
  # The capitalization of the first word can be turned off by setting the
114
113
  # +:capitalize+ option to false (default is true).
115
114
  #
116
- # humanize('employee_salary') # => "Employee salary"
117
- # humanize('author_id') # => "Author"
118
- # humanize('author_id', capitalize: false) # => "author"
119
- # humanize('_id') # => "Id"
115
+ # The trailing '_id' can be kept and capitalized by setting the
116
+ # optional parameter +keep_id_suffix+ to true (default is false).
117
+ #
118
+ # humanize('employee_salary') # => "Employee salary"
119
+ # humanize('author_id') # => "Author"
120
+ # humanize('author_id', capitalize: false) # => "author"
121
+ # humanize('_id') # => "Id"
122
+ # humanize('author_id', keep_id_suffix: true) # => "Author Id"
120
123
  #
121
124
  # If "SSL" was defined to be an acronym:
122
125
  #
123
126
  # humanize('ssl_error') # => "SSL error"
124
127
  #
125
- def humanize(lower_case_and_underscored_word, options = {})
128
+ def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
126
129
  result = lower_case_and_underscored_word.to_s.dup
127
130
 
128
131
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
129
132
 
130
- result.sub!(/\A_+/, '')
131
- result.sub!(/_id\z/, '')
132
- result.tr!('_', ' ')
133
+ result.sub!(/\A_+/, "")
134
+ unless keep_id_suffix
135
+ result.delete_suffix!("_id")
136
+ end
137
+ result.tr!("_", " ")
133
138
 
134
139
  result.gsub!(/([a-z\d]*)/i) do |match|
135
- "#{inflections.acronyms[match] || match.downcase}"
140
+ "#{inflections.acronyms[match.downcase] || match.downcase}"
136
141
  end
137
142
 
138
- if options.fetch(:capitalize, true)
143
+ if capitalize
139
144
  result.sub!(/\A\w/) { |match| match.upcase }
140
145
  end
141
146
 
142
147
  result
143
148
  end
144
149
 
150
+ # Converts just the first character to uppercase.
151
+ #
152
+ # upcase_first('what a Lovely Day') # => "What a Lovely Day"
153
+ # upcase_first('w') # => "W"
154
+ # upcase_first('') # => ""
155
+ def upcase_first(string)
156
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
157
+ end
158
+
145
159
  # Capitalizes all the words and replaces some characters in the string to
146
160
  # create a nicer looking title. +titleize+ is meant for creating pretty
147
161
  # output. It is not used in the Rails internals.
148
162
  #
163
+ # The trailing '_id','Id'.. can be kept and capitalized by setting the
164
+ # optional parameter +keep_id_suffix+ to true.
165
+ # By default, this parameter is false.
166
+ #
149
167
  # +titleize+ is also aliased as +titlecase+.
150
168
  #
151
- # 'man from the boondocks'.titleize # => "Man From The Boondocks"
152
- # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
153
- # 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
154
- # 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
155
- def titleize(word)
156
- humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
169
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
170
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
171
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
172
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
173
+ # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
174
+ def titleize(word, keep_id_suffix: false)
175
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])[a-z]/) do |match|
176
+ match.capitalize
177
+ end
157
178
  end
158
179
 
159
- # Create the name of a table like Rails does for models to table names. This
160
- # method uses the +pluralize+ method on the last word in the string.
180
+ # Creates the name of a table like Rails does for models to table names.
181
+ # This method uses the #pluralize method on the last word in the string.
161
182
  #
162
- # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
163
- # 'egg_and_ham'.tableize # => "egg_and_hams"
164
- # 'fancyCategory'.tableize # => "fancy_categories"
183
+ # tableize('RawScaledScorer') # => "raw_scaled_scorers"
184
+ # tableize('ham_and_egg') # => "ham_and_eggs"
185
+ # tableize('fancyCategory') # => "fancy_categories"
165
186
  def tableize(class_name)
166
187
  pluralize(underscore(class_name))
167
188
  end
168
189
 
169
- # Create a class name from a plural table name like Rails does for table
190
+ # Creates a class name from a plural table name like Rails does for table
170
191
  # names to models. Note that this returns a string and not a Class (To
171
- # convert to an actual class follow +classify+ with +constantize+).
192
+ # convert to an actual class follow +classify+ with #constantize).
172
193
  #
173
- # 'egg_and_hams'.classify # => "EggAndHam"
174
- # 'posts'.classify # => "Post"
194
+ # classify('ham_and_eggs') # => "HamAndEgg"
195
+ # classify('posts') # => "Post"
175
196
  #
176
197
  # Singular names are not handled correctly:
177
198
  #
178
- # 'calculus'.classify # => "Calculu"
199
+ # classify('calculus') # => "Calculu"
179
200
  def classify(table_name)
180
201
  # strip out any leading schema name
181
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
202
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
182
203
  end
183
204
 
184
205
  # Replaces underscores with dashes in the string.
185
206
  #
186
- # 'puni_puni'.dasherize # => "puni-puni"
207
+ # dasherize('puni_puni') # => "puni-puni"
187
208
  def dasherize(underscored_word)
188
- underscored_word.tr('_', '-')
209
+ underscored_word.tr("_", "-")
189
210
  end
190
211
 
191
212
  # Removes the module part from the expression in the string.
192
213
  #
193
- # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
194
- # 'Inflections'.demodulize # => "Inflections"
195
- # '::Inflections'.demodulize # => "Inflections"
196
- # ''.demodulize # => ""
214
+ # demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
215
+ # demodulize('Inflections') # => "Inflections"
216
+ # demodulize('::Inflections') # => "Inflections"
217
+ # demodulize('') # => ""
197
218
  #
198
- # See also +deconstantize+.
219
+ # See also #deconstantize.
199
220
  def demodulize(path)
200
221
  path = path.to_s
201
- if i = path.rindex('::')
202
- path[(i+2)..-1]
222
+ if i = path.rindex("::")
223
+ path[(i + 2)..-1]
203
224
  else
204
225
  path
205
226
  end
@@ -207,32 +228,32 @@ module ActiveSupport
207
228
 
208
229
  # Removes the rightmost segment from the constant expression in the string.
209
230
  #
210
- # 'Net::HTTP'.deconstantize # => "Net"
211
- # '::Net::HTTP'.deconstantize # => "::Net"
212
- # 'String'.deconstantize # => ""
213
- # '::String'.deconstantize # => ""
214
- # ''.deconstantize # => ""
231
+ # deconstantize('Net::HTTP') # => "Net"
232
+ # deconstantize('::Net::HTTP') # => "::Net"
233
+ # deconstantize('String') # => ""
234
+ # deconstantize('::String') # => ""
235
+ # deconstantize('') # => ""
215
236
  #
216
- # See also +demodulize+.
237
+ # See also #demodulize.
217
238
  def deconstantize(path)
218
- path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
239
+ path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
219
240
  end
220
241
 
221
242
  # Creates a foreign key name from a class name.
222
243
  # +separate_class_name_and_id_with_underscore+ sets whether
223
244
  # the method should put '_' between the name and 'id'.
224
245
  #
225
- # 'Message'.foreign_key # => "message_id"
226
- # 'Message'.foreign_key(false) # => "messageid"
227
- # 'Admin::Post'.foreign_key # => "post_id"
246
+ # foreign_key('Message') # => "message_id"
247
+ # foreign_key('Message', false) # => "messageid"
248
+ # foreign_key('Admin::Post') # => "post_id"
228
249
  def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
229
250
  underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
230
251
  end
231
252
 
232
253
  # Tries to find a constant with the name specified in the argument string.
233
254
  #
234
- # 'Module'.constantize # => Module
235
- # 'Test::Unit'.constantize # => Test::Unit
255
+ # constantize('Module') # => Module
256
+ # constantize('Foo::Bar') # => Foo::Bar
236
257
  #
237
258
  # The name is assumed to be the one of a top-level constant, no matter
238
259
  # whether it starts with "::" or not. No lexical context is taken into
@@ -241,47 +262,51 @@ module ActiveSupport
241
262
  # C = 'outside'
242
263
  # module M
243
264
  # C = 'inside'
244
- # C # => 'inside'
245
- # 'C'.constantize # => 'outside', same as ::C
265
+ # C # => 'inside'
266
+ # constantize('C') # => 'outside', same as ::C
246
267
  # end
247
268
  #
248
269
  # NameError is raised when the name is not in CamelCase or the constant is
249
270
  # unknown.
250
271
  def constantize(camel_cased_word)
251
- names = camel_cased_word.split('::')
252
-
253
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
254
- Object.const_get(camel_cased_word) if names.empty?
255
-
256
- # Remove the first blank element in case of '::ClassName' notation.
257
- names.shift if names.size > 1 && names.first.empty?
258
-
259
- names.inject(Object) do |constant, name|
260
- if constant == Object
261
- constant.const_get(name)
262
- else
263
- candidate = constant.const_get(name)
264
- next candidate if constant.const_defined?(name, false)
265
- next candidate unless Object.const_defined?(name)
266
-
267
- # Go down the ancestors to check if it is owned directly. The check
268
- # stops when we reach Object or the end of ancestors tree.
269
- constant = constant.ancestors.inject do |const, ancestor|
270
- break const if ancestor == Object
271
- break ancestor if ancestor.const_defined?(name, false)
272
- const
272
+ if camel_cased_word.blank? || !camel_cased_word.include?("::")
273
+ Object.const_get(camel_cased_word)
274
+ else
275
+ names = camel_cased_word.split("::")
276
+
277
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
278
+ Object.const_get(camel_cased_word) if names.empty?
279
+
280
+ # Remove the first blank element in case of '::ClassName' notation.
281
+ names.shift if names.size > 1 && names.first.empty?
282
+
283
+ names.inject(Object) do |constant, name|
284
+ if constant == Object
285
+ constant.const_get(name)
286
+ else
287
+ candidate = constant.const_get(name)
288
+ next candidate if constant.const_defined?(name, false)
289
+ next candidate unless Object.const_defined?(name)
290
+
291
+ # Go down the ancestors to check if it is owned directly. The check
292
+ # stops when we reach Object or the end of ancestors tree.
293
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
294
+ break const if ancestor == Object
295
+ break ancestor if ancestor.const_defined?(name, false)
296
+ const
297
+ end
298
+
299
+ # owner is in Object, so raise
300
+ constant.const_get(name, false)
273
301
  end
274
-
275
- # owner is in Object, so raise
276
- constant.const_get(name, false)
277
302
  end
278
303
  end
279
304
  end
280
305
 
281
306
  # Tries to find a constant with the name specified in the argument string.
282
307
  #
283
- # 'Module'.safe_constantize # => Module
284
- # 'Test::Unit'.safe_constantize # => Test::Unit
308
+ # safe_constantize('Module') # => Module
309
+ # safe_constantize('Foo::Bar') # => Foo::Bar
285
310
  #
286
311
  # The name is assumed to be the one of a top-level constant, no matter
287
312
  # whether it starts with "::" or not. No lexical context is taken into
@@ -290,23 +315,24 @@ module ActiveSupport
290
315
  # C = 'outside'
291
316
  # module M
292
317
  # C = 'inside'
293
- # C # => 'inside'
294
- # 'C'.safe_constantize # => 'outside', same as ::C
318
+ # C # => 'inside'
319
+ # safe_constantize('C') # => 'outside', same as ::C
295
320
  # end
296
321
  #
297
322
  # +nil+ is returned when the name is not in CamelCase or the constant (or
298
323
  # part of it) is unknown.
299
324
  #
300
- # 'blargle'.safe_constantize # => nil
301
- # 'UnknownModule'.safe_constantize # => nil
302
- # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
325
+ # safe_constantize('blargle') # => nil
326
+ # safe_constantize('UnknownModule') # => nil
327
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
303
328
  def safe_constantize(camel_cased_word)
304
329
  constantize(camel_cased_word)
305
330
  rescue NameError => e
306
331
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
307
332
  e.name.to_s == camel_cased_word.to_s)
308
- rescue ArgumentError => e
309
- raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
333
+ rescue LoadError => e
334
+ message = e.respond_to?(:original_message) ? e.original_message : e.message
335
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message)
310
336
  end
311
337
 
312
338
  # Returns the suffix that should be added to a number to denote the position
@@ -319,18 +345,7 @@ module ActiveSupport
319
345
  # ordinal(-11) # => "th"
320
346
  # ordinal(-1021) # => "st"
321
347
  def ordinal(number)
322
- abs_number = number.to_i.abs
323
-
324
- if (11..13).include?(abs_number % 100)
325
- "th"
326
- else
327
- case abs_number % 10
328
- when 1; "st"
329
- when 2; "nd"
330
- when 3; "rd"
331
- else "th"
332
- end
333
- end
348
+ I18n.translate("number.nth.ordinals", number: number)
334
349
  end
335
350
 
336
351
  # Turns a number into an ordinal string used to denote the position in an
@@ -343,41 +358,43 @@ module ActiveSupport
343
358
  # ordinalize(-11) # => "-11th"
344
359
  # ordinalize(-1021) # => "-1021st"
345
360
  def ordinalize(number)
346
- "#{number}#{ordinal(number)}"
361
+ I18n.translate("number.nth.ordinalized", number: number)
347
362
  end
348
363
 
349
364
  private
365
+ # Mounts a regular expression, returned as a string to ease interpolation,
366
+ # that will match part by part the given constant.
367
+ #
368
+ # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
369
+ # const_regexp("::") # => "::"
370
+ def const_regexp(camel_cased_word)
371
+ parts = camel_cased_word.split("::")
350
372
 
351
- # Mounts a regular expression, returned as a string to ease interpolation,
352
- # that will match part by part the given constant.
353
- #
354
- # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
355
- # const_regexp("::") # => "::"
356
- def const_regexp(camel_cased_word) #:nodoc:
357
- parts = camel_cased_word.split("::")
358
-
359
- return Regexp.escape(camel_cased_word) if parts.blank?
373
+ return Regexp.escape(camel_cased_word) if parts.blank?
360
374
 
361
- last = parts.pop
375
+ last = parts.pop
362
376
 
363
- parts.reverse.inject(last) do |acc, part|
364
- part.empty? ? acc : "#{part}(::#{acc})?"
377
+ parts.reverse!.inject(last) do |acc, part|
378
+ part.empty? ? acc : "#{part}(::#{acc})?"
379
+ end
365
380
  end
366
- end
367
-
368
- # Applies inflection rules for +singularize+ and +pluralize+.
369
- #
370
- # apply_inflections('post', inflections.plurals) # => "posts"
371
- # apply_inflections('posts', inflections.singulars) # => "post"
372
- def apply_inflections(word, rules)
373
- result = word.to_s.dup
374
381
 
375
- if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
376
- result
377
- else
378
- rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
379
- result
382
+ # Applies inflection rules for +singularize+ and +pluralize+.
383
+ #
384
+ # If passed an optional +locale+ parameter, the uncountables will be
385
+ # found for that locale.
386
+ #
387
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
388
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
389
+ def apply_inflections(word, rules, locale = :en)
390
+ result = word.to_s.dup
391
+
392
+ if word.empty? || inflections(locale).uncountables.uncountable?(result)
393
+ result
394
+ else
395
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
396
+ result
397
+ end
380
398
  end
381
- end
382
399
  end
383
400
  end