activesupport 4.0.13 → 5.2.5

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