activesupport 4.2.11.3 → 5.2.8.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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +435 -403
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +4 -5
  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 +7 -5
  8. data/lib/active_support/benchmarkable.rb +6 -4
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +41 -35
  11. data/lib/active_support/cache/mem_cache_store.rb +91 -91
  12. data/lib/active_support/cache/memory_store.rb +27 -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 +67 -34
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  17. data/lib/active_support/cache.rb +287 -196
  18. data/lib/active_support/callbacks.rb +640 -590
  19. data/lib/active_support/concern.rb +11 -5
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -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 +29 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +22 -18
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -6
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -22
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -6
  37. data/lib/active_support/core_ext/class.rb +4 -3
  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 +11 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +25 -23
  42. data/lib/active_support/core_ext/date/zones.rb +4 -2
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +170 -58
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -3
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
  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 +36 -18
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
  51. data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
  52. data/lib/active_support/core_ext/date_time.rb +7 -5
  53. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +101 -33
  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 +14 -9
  59. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +11 -8
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +33 -27
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +8 -8
  66. data/lib/active_support/core_ext/hash/transform_values.rb +14 -5
  67. data/lib/active_support/core_ext/hash.rb +11 -9
  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 -18
  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 +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 +6 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +8 -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 +43 -40
  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 +11 -12
  85. data/lib/active_support/core_ext/module/delegation.rb +98 -36
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -9
  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 -11
  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 +78 -81
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  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 +27 -2
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  102. data/lib/active_support/core_ext/object/duplicable.rb +41 -14
  103. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +49 -19
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  108. data/lib/active_support/core_ext/object/try.rb +69 -21
  109. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  110. data/lib/active_support/core_ext/object.rb +14 -13
  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 +8 -6
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +7 -4
  123. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  124. data/lib/active_support/core_ext/string/filters.rb +6 -5
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +61 -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 +62 -38
  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 -2
  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 +85 -51
  136. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  137. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  138. data/lib/active_support/core_ext/time/zones.rb +41 -7
  139. data/lib/active_support/core_ext/time.rb +7 -6
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -1
  142. data/lib/active_support/current_attributes.rb +195 -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.rb +152 -161
  146. data/lib/active_support/deprecation/behaviors.rb +44 -11
  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 +12 -9
  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 +314 -38
  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 +131 -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 +6 -4
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +123 -28
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +37 -13
  169. data/lib/active_support/inflections.rb +13 -11
  170. data/lib/active_support/inflector/inflections.rb +61 -12
  171. data/lib/active_support/inflector/methods.rb +163 -136
  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 +16 -13
  175. data/lib/active_support/json/encoding.rb +11 -58
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +25 -25
  178. data/lib/active_support/lazy_load_hooks.rb +50 -20
  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 +8 -7
  183. data/lib/active_support/logger_silence.rb +6 -4
  184. data/lib/active_support/logger_thread_safe_level.rb +7 -5
  185. data/lib/active_support/message_encryptor.rb +168 -53
  186. data/lib/active_support/message_verifier.rb +150 -17
  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 +36 -23
  191. data/lib/active_support/multibyte/unicode.rb +100 -96
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +11 -9
  194. data/lib/active_support/notifications/instrumenter.rb +27 -7
  195. data/lib/active_support/notifications.rb +11 -7
  196. data/lib/active_support/number_helper/number_converter.rb +13 -11
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +94 -68
  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 +23 -5
  209. data/lib/active_support/per_thread_registry.rb +9 -4
  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 +15 -11
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +21 -16
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +19 -47
  220. data/lib/active_support/testing/assertions.rb +137 -20
  221. data/lib/active_support/testing/autorun.rb +4 -2
  222. data/lib/active_support/testing/constant_lookup.rb +2 -1
  223. data/lib/active_support/testing/declarative.rb +3 -1
  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 +34 -25
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +3 -1
  231. data/lib/active_support/testing/time_helpers.rb +81 -15
  232. data/lib/active_support/time.rb +14 -12
  233. data/lib/active_support/time_with_zone.rb +169 -39
  234. data/lib/active_support/values/time_zone.rb +196 -61
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +3 -1
  237. data/lib/active_support/xml_mini/jdom.rb +116 -114
  238. data/lib/active_support/xml_mini/libxml.rb +16 -13
  239. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  240. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  241. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  242. data/lib/active_support/xml_mini/rexml.rb +11 -9
  243. data/lib/active_support/xml_mini.rb +37 -37
  244. data/lib/active_support.rb +12 -11
  245. metadata +57 -27
  246. data/lib/active_support/concurrency/latch.rb +0 -27
  247. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  248. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  249. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  250. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  251. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  252. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  253. data/lib/active_support/core_ext/object/itself.rb +0 -15
  254. data/lib/active_support/core_ext/struct.rb +0 -6
  255. data/lib/active_support/core_ext/thread.rb +0 -86
  256. data/lib/active_support/core_ext/time/marshal.rb +0 -30
@@ -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/regexp"
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!("/".freeze, "::".freeze)
77
78
  string
78
79
  end
79
80
 
@@ -81,125 +82,146 @@ 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')
97
- 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)
98
99
  word.downcase!
99
100
  word
100
101
  end
101
102
 
102
103
  # Tweaks an attribute name for display to end users.
103
104
  #
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.
105
+ # Specifically, performs these transformations:
112
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
113
  # The capitalization of the first word can be turned off by setting the
114
114
  # +:capitalize+ option to false (default is true).
115
115
  #
116
- # humanize('employee_salary') # => "Employee salary"
117
- # humanize('author_id') # => "Author"
118
- # humanize('author_id', capitalize: false) # => "author"
119
- # humanize('_id') # => "Id"
116
+ # The trailing '_id' can be kept and capitalized by setting the
117
+ # optional parameter +keep_id_suffix+ to true (default is false).
118
+ #
119
+ # humanize('employee_salary') # => "Employee salary"
120
+ # humanize('author_id') # => "Author"
121
+ # humanize('author_id', capitalize: false) # => "author"
122
+ # humanize('_id') # => "Id"
123
+ # humanize('author_id', keep_id_suffix: true) # => "Author Id"
120
124
  #
121
125
  # If "SSL" was defined to be an acronym:
122
126
  #
123
127
  # humanize('ssl_error') # => "SSL error"
124
128
  #
125
- def humanize(lower_case_and_underscored_word, options = {})
129
+ def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
126
130
  result = lower_case_and_underscored_word.to_s.dup
127
131
 
128
132
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
129
133
 
130
- result.sub!(/\A_+/, '')
131
- result.sub!(/_id\z/, '')
132
- result.tr!('_', ' ')
134
+ result.sub!(/\A_+/, "".freeze)
135
+ unless keep_id_suffix
136
+ result.sub!(/_id\z/, "".freeze)
137
+ end
138
+ result.tr!("_".freeze, " ".freeze)
133
139
 
134
140
  result.gsub!(/([a-z\d]*)/i) do |match|
135
- "#{inflections.acronyms[match] || match.downcase}"
141
+ "#{inflections.acronyms[match.downcase] || match.downcase}"
136
142
  end
137
143
 
138
- if options.fetch(:capitalize, true)
144
+ if capitalize
139
145
  result.sub!(/\A\w/) { |match| match.upcase }
140
146
  end
141
147
 
142
148
  result
143
149
  end
144
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]) : ""
158
+ end
159
+
145
160
  # Capitalizes all the words and replaces some characters in the string to
146
161
  # create a nicer looking title. +titleize+ is meant for creating pretty
147
162
  # output. It is not used in the Rails internals.
148
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
+ #
149
168
  # +titleize+ is also aliased as +titlecase+.
150
169
  #
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 }
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
157
179
  end
158
180
 
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.
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.
161
183
  #
162
- # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
163
- # 'egg_and_ham'.tableize # => "egg_and_hams"
164
- # 'fancyCategory'.tableize # => "fancy_categories"
184
+ # tableize('RawScaledScorer') # => "raw_scaled_scorers"
185
+ # tableize('ham_and_egg') # => "ham_and_eggs"
186
+ # tableize('fancyCategory') # => "fancy_categories"
165
187
  def tableize(class_name)
166
188
  pluralize(underscore(class_name))
167
189
  end
168
190
 
169
- # 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
170
192
  # names to models. Note that this returns a string and not a Class (To
171
- # convert to an actual class follow +classify+ with +constantize+).
193
+ # convert to an actual class follow +classify+ with #constantize).
172
194
  #
173
- # 'egg_and_hams'.classify # => "EggAndHam"
174
- # 'posts'.classify # => "Post"
195
+ # classify('ham_and_eggs') # => "HamAndEgg"
196
+ # classify('posts') # => "Post"
175
197
  #
176
198
  # Singular names are not handled correctly:
177
199
  #
178
- # 'calculus'.classify # => "Calculu"
200
+ # classify('calculus') # => "Calculus"
179
201
  def classify(table_name)
180
202
  # strip out any leading schema name
181
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
203
+ camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
182
204
  end
183
205
 
184
206
  # Replaces underscores with dashes in the string.
185
207
  #
186
- # 'puni_puni'.dasherize # => "puni-puni"
208
+ # dasherize('puni_puni') # => "puni-puni"
187
209
  def dasherize(underscored_word)
188
- underscored_word.tr('_', '-')
210
+ underscored_word.tr("_".freeze, "-".freeze)
189
211
  end
190
212
 
191
213
  # Removes the module part from the expression in the string.
192
214
  #
193
- # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
194
- # 'Inflections'.demodulize # => "Inflections"
195
- # '::Inflections'.demodulize # => "Inflections"
196
- # ''.demodulize # => ""
215
+ # demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
216
+ # demodulize('Inflections') # => "Inflections"
217
+ # demodulize('::Inflections') # => "Inflections"
218
+ # demodulize('') # => ""
197
219
  #
198
- # See also +deconstantize+.
220
+ # See also #deconstantize.
199
221
  def demodulize(path)
200
222
  path = path.to_s
201
- if i = path.rindex('::')
202
- path[(i+2)..-1]
223
+ if i = path.rindex("::")
224
+ path[(i + 2)..-1]
203
225
  else
204
226
  path
205
227
  end
@@ -207,32 +229,32 @@ module ActiveSupport
207
229
 
208
230
  # Removes the rightmost segment from the constant expression in the string.
209
231
  #
210
- # 'Net::HTTP'.deconstantize # => "Net"
211
- # '::Net::HTTP'.deconstantize # => "::Net"
212
- # 'String'.deconstantize # => ""
213
- # '::String'.deconstantize # => ""
214
- # ''.deconstantize # => ""
232
+ # deconstantize('Net::HTTP') # => "Net"
233
+ # deconstantize('::Net::HTTP') # => "::Net"
234
+ # deconstantize('String') # => ""
235
+ # deconstantize('::String') # => ""
236
+ # deconstantize('') # => ""
215
237
  #
216
- # See also +demodulize+.
238
+ # See also #demodulize.
217
239
  def deconstantize(path)
218
- 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
219
241
  end
220
242
 
221
243
  # Creates a foreign key name from a class name.
222
244
  # +separate_class_name_and_id_with_underscore+ sets whether
223
245
  # the method should put '_' between the name and 'id'.
224
246
  #
225
- # 'Message'.foreign_key # => "message_id"
226
- # 'Message'.foreign_key(false) # => "messageid"
227
- # '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"
228
250
  def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
229
251
  underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
230
252
  end
231
253
 
232
254
  # Tries to find a constant with the name specified in the argument string.
233
255
  #
234
- # 'Module'.constantize # => Module
235
- # 'Test::Unit'.constantize # => Test::Unit
256
+ # constantize('Module') # => Module
257
+ # constantize('Foo::Bar') # => Foo::Bar
236
258
  #
237
259
  # The name is assumed to be the one of a top-level constant, no matter
238
260
  # whether it starts with "::" or not. No lexical context is taken into
@@ -241,14 +263,14 @@ module ActiveSupport
241
263
  # C = 'outside'
242
264
  # module M
243
265
  # C = 'inside'
244
- # C # => 'inside'
245
- # 'C'.constantize # => 'outside', same as ::C
266
+ # C # => 'inside'
267
+ # constantize('C') # => 'outside', same as ::C
246
268
  # end
247
269
  #
248
270
  # NameError is raised when the name is not in CamelCase or the constant is
249
271
  # unknown.
250
272
  def constantize(camel_cased_word)
251
- names = camel_cased_word.split('::')
273
+ names = camel_cased_word.split("::".freeze)
252
274
 
253
275
  # Trigger a built-in NameError exception including the ill-formed constant in the message.
254
276
  Object.const_get(camel_cased_word) if names.empty?
@@ -266,7 +288,7 @@ module ActiveSupport
266
288
 
267
289
  # Go down the ancestors to check if it is owned directly. The check
268
290
  # stops when we reach Object or the end of ancestors tree.
269
- constant = constant.ancestors.inject do |const, ancestor|
291
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
270
292
  break const if ancestor == Object
271
293
  break ancestor if ancestor.const_defined?(name, false)
272
294
  const
@@ -280,8 +302,8 @@ module ActiveSupport
280
302
 
281
303
  # Tries to find a constant with the name specified in the argument string.
282
304
  #
283
- # 'Module'.safe_constantize # => Module
284
- # 'Test::Unit'.safe_constantize # => Test::Unit
305
+ # safe_constantize('Module') # => Module
306
+ # safe_constantize('Foo::Bar') # => Foo::Bar
285
307
  #
286
308
  # The name is assumed to be the one of a top-level constant, no matter
287
309
  # whether it starts with "::" or not. No lexical context is taken into
@@ -290,23 +312,25 @@ module ActiveSupport
290
312
  # C = 'outside'
291
313
  # module M
292
314
  # C = 'inside'
293
- # C # => 'inside'
294
- # 'C'.safe_constantize # => 'outside', same as ::C
315
+ # C # => 'inside'
316
+ # safe_constantize('C') # => 'outside', same as ::C
295
317
  # end
296
318
  #
297
319
  # +nil+ is returned when the name is not in CamelCase or the constant (or
298
320
  # part of it) is unknown.
299
321
  #
300
- # 'blargle'.safe_constantize # => nil
301
- # 'UnknownModule'.safe_constantize # => nil
302
- # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
322
+ # safe_constantize('blargle') # => nil
323
+ # safe_constantize('UnknownModule') # => nil
324
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
303
325
  def safe_constantize(camel_cased_word)
304
326
  constantize(camel_cased_word)
305
327
  rescue NameError => e
306
328
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
307
329
  e.name.to_s == camel_cased_word.to_s)
308
330
  rescue ArgumentError => e
309
- 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)
310
334
  end
311
335
 
312
336
  # Returns the suffix that should be added to a number to denote the position
@@ -325,10 +349,10 @@ module ActiveSupport
325
349
  "th"
326
350
  else
327
351
  case abs_number % 10
328
- when 1; "st"
329
- when 2; "nd"
330
- when 3; "rd"
331
- else "th"
352
+ when 1; "st"
353
+ when 2; "nd"
354
+ when 3; "rd"
355
+ else "th"
332
356
  end
333
357
  end
334
358
  end
@@ -348,36 +372,39 @@ module ActiveSupport
348
372
 
349
373
  private
350
374
 
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("::")
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)
358
382
 
359
- return Regexp.escape(camel_cased_word) if parts.blank?
383
+ return Regexp.escape(camel_cased_word) if parts.blank?
360
384
 
361
- last = parts.pop
385
+ last = parts.pop
362
386
 
363
- parts.reverse.inject(last) do |acc, part|
364
- part.empty? ? acc : "#{part}(::#{acc})?"
387
+ parts.reverse.inject(last) do |acc, part|
388
+ part.empty? ? acc : "#{part}(::#{acc})?"
389
+ end
365
390
  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
391
 
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
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
401
+
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
380
408
  end
381
- end
382
409
  end
383
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"