activesupport 4.2.11.1 → 6.0.3.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 (263) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -411
  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 +48 -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 +58 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +95 -91
  13. data/lib/active_support/cache/memory_store.rb +39 -36
  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 +75 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +331 -217
  19. data/lib/active_support/callbacks.rb +650 -592
  20. data/lib/active_support/concern.rb +35 -6
  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 +13 -14
  24. data/lib/active_support/core_ext/array/access.rb +41 -1
  25. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  26. data/lib/active_support/core_ext/array/extract.rb +21 -0
  27. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  28. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  29. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  30. data/lib/active_support/core_ext/array/prepend_and_append.rb +4 -6
  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 +3 -1
  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 +45 -31
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +20 -6
  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 +25 -23
  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 +154 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -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 +7 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +114 -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/compact.rb +4 -23
  61. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  63. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  64. data/lib/active_support/core_ext/hash/except.rb +12 -9
  65. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  66. data/lib/active_support/core_ext/hash/keys.rb +19 -42
  67. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  68. data/lib/active_support/core_ext/hash/slice.rb +5 -27
  69. data/lib/active_support/core_ext/hash/transform_values.rb +4 -22
  70. data/lib/active_support/core_ext/hash.rb +10 -9
  71. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  72. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  73. data/lib/active_support/core_ext/integer/time.rb +11 -18
  74. data/lib/active_support/core_ext/integer.rb +5 -3
  75. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  76. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  78. data/lib/active_support/core_ext/kernel.rb +5 -5
  79. data/lib/active_support/core_ext/load_error.rb +3 -22
  80. data/lib/active_support/core_ext/marshal.rb +8 -8
  81. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  82. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  83. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +46 -46
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  87. data/lib/active_support/core_ext/module/delegation.rb +133 -30
  88. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  89. data/lib/active_support/core_ext/module/introspection.rb +44 -19
  90. data/lib/active_support/core_ext/module/reachable.rb +5 -7
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -11
  94. data/lib/active_support/core_ext/name_error.rb +22 -2
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +129 -136
  97. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  99. data/lib/active_support/core_ext/numeric.rb +5 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  101. data/lib/active_support/core_ext/object/blank.rb +27 -3
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  104. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  105. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +51 -20
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  110. data/lib/active_support/core_ext/object/try.rb +81 -23
  111. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  112. data/lib/active_support/core_ext/object.rb +14 -13
  113. data/lib/active_support/core_ext/range/compare_range.rb +76 -0
  114. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  115. data/lib/active_support/core_ext/range/each.rb +18 -17
  116. data/lib/active_support/core_ext/range/include_range.rb +7 -21
  117. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  118. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  119. data/lib/active_support/core_ext/range.rb +7 -4
  120. data/lib/active_support/core_ext/regexp.rb +2 -0
  121. data/lib/active_support/core_ext/securerandom.rb +45 -0
  122. data/lib/active_support/core_ext/string/access.rb +16 -6
  123. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  124. data/lib/active_support/core_ext/string/conversions.rb +7 -4
  125. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  126. data/lib/active_support/core_ext/string/filters.rb +48 -6
  127. data/lib/active_support/core_ext/string/indent.rb +6 -4
  128. data/lib/active_support/core_ext/string/inflections.rb +66 -24
  129. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  130. data/lib/active_support/core_ext/string/multibyte.rb +16 -7
  131. data/lib/active_support/core_ext/string/output_safety.rb +93 -40
  132. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  133. data/lib/active_support/core_ext/string/strip.rb +6 -5
  134. data/lib/active_support/core_ext/string/zones.rb +4 -2
  135. data/lib/active_support/core_ext/string.rb +15 -13
  136. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  137. data/lib/active_support/core_ext/time/calculations.rb +115 -52
  138. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  139. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  140. data/lib/active_support/core_ext/time/zones.rb +41 -7
  141. data/lib/active_support/core_ext/time.rb +7 -6
  142. data/lib/active_support/core_ext/uri.rb +6 -7
  143. data/lib/active_support/core_ext.rb +3 -1
  144. data/lib/active_support/current_attributes.rb +203 -0
  145. data/lib/active_support/dependencies/autoload.rb +2 -0
  146. data/lib/active_support/dependencies/interlock.rb +57 -0
  147. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  148. data/lib/active_support/dependencies.rb +208 -166
  149. data/lib/active_support/deprecation/behaviors.rb +44 -11
  150. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  151. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  152. data/lib/active_support/deprecation/method_wrappers.rb +61 -21
  153. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  154. data/lib/active_support/deprecation/reporting.rb +32 -12
  155. data/lib/active_support/deprecation.rb +12 -9
  156. data/lib/active_support/descendants_tracker.rb +57 -9
  157. data/lib/active_support/digest.rb +20 -0
  158. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  159. data/lib/active_support/duration/iso8601_serializer.rb +53 -0
  160. data/lib/active_support/duration.rb +315 -40
  161. data/lib/active_support/encrypted_configuration.rb +45 -0
  162. data/lib/active_support/encrypted_file.rb +100 -0
  163. data/lib/active_support/evented_file_update_checker.rb +234 -0
  164. data/lib/active_support/execution_wrapper.rb +129 -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/gem_version.rb +6 -4
  168. data/lib/active_support/gzip.rb +7 -5
  169. data/lib/active_support/hash_with_indifferent_access.rb +129 -30
  170. data/lib/active_support/i18n.rb +9 -6
  171. data/lib/active_support/i18n_railtie.rb +50 -14
  172. data/lib/active_support/inflections.rb +13 -11
  173. data/lib/active_support/inflector/inflections.rb +58 -13
  174. data/lib/active_support/inflector/methods.rb +159 -145
  175. data/lib/active_support/inflector/transliterate.rb +84 -34
  176. data/lib/active_support/inflector.rb +7 -5
  177. data/lib/active_support/json/decoding.rb +32 -30
  178. data/lib/active_support/json/encoding.rb +17 -60
  179. data/lib/active_support/json.rb +4 -2
  180. data/lib/active_support/key_generator.rb +11 -43
  181. data/lib/active_support/lazy_load_hooks.rb +53 -20
  182. data/lib/active_support/locale/en.rb +33 -0
  183. data/lib/active_support/locale/en.yml +2 -0
  184. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  185. data/lib/active_support/log_subscriber.rb +44 -19
  186. data/lib/active_support/logger.rb +9 -23
  187. data/lib/active_support/logger_silence.rb +32 -14
  188. data/lib/active_support/logger_thread_safe_level.rb +32 -8
  189. data/lib/active_support/message_encryptor.rb +166 -53
  190. data/lib/active_support/message_verifier.rb +149 -16
  191. data/lib/active_support/messages/metadata.rb +72 -0
  192. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  193. data/lib/active_support/messages/rotator.rb +56 -0
  194. data/lib/active_support/multibyte/chars.rb +56 -63
  195. data/lib/active_support/multibyte/unicode.rb +56 -290
  196. data/lib/active_support/multibyte.rb +4 -2
  197. data/lib/active_support/notifications/fanout.rb +109 -22
  198. data/lib/active_support/notifications/instrumenter.rb +107 -16
  199. data/lib/active_support/notifications.rb +51 -10
  200. data/lib/active_support/number_helper/number_converter.rb +16 -15
  201. data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -15
  202. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  203. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -10
  204. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -9
  205. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  206. data/lib/active_support/number_helper/number_to_phone_converter.rb +15 -5
  207. data/lib/active_support/number_helper/number_to_rounded_converter.rb +25 -57
  208. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  209. data/lib/active_support/number_helper.rb +105 -68
  210. data/lib/active_support/option_merger.rb +24 -4
  211. data/lib/active_support/ordered_hash.rb +7 -5
  212. data/lib/active_support/ordered_options.rb +27 -5
  213. data/lib/active_support/parameter_filter.rb +128 -0
  214. data/lib/active_support/per_thread_registry.rb +9 -4
  215. data/lib/active_support/proxy_object.rb +2 -0
  216. data/lib/active_support/rails.rb +10 -8
  217. data/lib/active_support/railtie.rb +43 -9
  218. data/lib/active_support/reloader.rb +130 -0
  219. data/lib/active_support/rescuable.rb +108 -53
  220. data/lib/active_support/security_utils.rb +15 -11
  221. data/lib/active_support/string_inquirer.rb +11 -4
  222. data/lib/active_support/subscriber.rb +74 -30
  223. data/lib/active_support/tagged_logging.rb +25 -13
  224. data/lib/active_support/test_case.rb +107 -44
  225. data/lib/active_support/testing/assertions.rb +151 -20
  226. data/lib/active_support/testing/autorun.rb +4 -2
  227. data/lib/active_support/testing/constant_lookup.rb +2 -1
  228. data/lib/active_support/testing/declarative.rb +3 -1
  229. data/lib/active_support/testing/deprecation.rb +13 -10
  230. data/lib/active_support/testing/file_fixtures.rb +38 -0
  231. data/lib/active_support/testing/isolation.rb +35 -26
  232. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  233. data/lib/active_support/testing/parallelization.rb +134 -0
  234. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  235. data/lib/active_support/testing/stream.rb +43 -0
  236. data/lib/active_support/testing/tagged_logging.rb +3 -1
  237. data/lib/active_support/testing/time_helpers.rb +84 -20
  238. data/lib/active_support/time.rb +14 -12
  239. data/lib/active_support/time_with_zone.rb +179 -39
  240. data/lib/active_support/values/time_zone.rb +203 -63
  241. data/lib/active_support/version.rb +3 -1
  242. data/lib/active_support/xml_mini/jdom.rb +116 -115
  243. data/lib/active_support/xml_mini/libxml.rb +16 -13
  244. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  245. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  246. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  247. data/lib/active_support/xml_mini/rexml.rb +11 -9
  248. data/lib/active_support/xml_mini.rb +38 -46
  249. data/lib/active_support.rb +13 -11
  250. metadata +84 -26
  251. data/lib/active_support/concurrency/latch.rb +0 -27
  252. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  253. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  254. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  255. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  256. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  257. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  258. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  259. data/lib/active_support/core_ext/object/itself.rb +0 -15
  260. data/lib/active_support/core_ext/struct.rb +0 -6
  261. data/lib/active_support/core_ext/thread.rb +0 -86
  262. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  263. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,6 +1,6 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'active_support/inflections'
3
+ require "active_support/inflections"
4
4
 
5
5
  module ActiveSupport
6
6
  # The Inflector transforms words from singular to plural, class names to table
@@ -22,58 +22,58 @@ module ActiveSupport
22
22
  # pluralized using rules defined for that language. By default,
23
23
  # this parameter is set to <tt>:en</tt>.
24
24
  #
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"
25
+ # pluralize('post') # => "posts"
26
+ # pluralize('octopus') # => "octopi"
27
+ # pluralize('sheep') # => "sheep"
28
+ # pluralize('words') # => "words"
29
+ # pluralize('CamelOctopus') # => "CamelOctopi"
30
+ # pluralize('ley', :es) # => "leyes"
31
31
  def pluralize(word, locale = :en)
32
- apply_inflections(word, inflections(locale).plurals)
32
+ apply_inflections(word, inflections(locale).plurals, locale)
33
33
  end
34
34
 
35
- # The reverse of +pluralize+, returns the singular form of a word in a
35
+ # The reverse of #pluralize, returns the singular form of a word in a
36
36
  # string.
37
37
  #
38
38
  # If passed an optional +locale+ parameter, the word will be
39
39
  # singularized using rules defined for that language. By default,
40
40
  # this parameter is set to <tt>:en</tt>.
41
41
  #
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"
42
+ # singularize('posts') # => "post"
43
+ # singularize('octopi') # => "octopus"
44
+ # singularize('sheep') # => "sheep"
45
+ # singularize('word') # => "word"
46
+ # singularize('CamelOctopi') # => "CamelOctopus"
47
+ # singularize('leyes', :es) # => "ley"
48
48
  def singularize(word, locale = :en)
49
- apply_inflections(word, inflections(locale).singulars)
49
+ apply_inflections(word, inflections(locale).singulars, locale)
50
50
  end
51
51
 
52
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument
53
- # to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
52
+ # Converts strings to UpperCamelCase.
53
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
54
54
  # lowerCamelCase.
55
55
  #
56
- # +camelize+ will also convert '/' to '::' which is useful for converting
56
+ # Also converts '/' to '::' which is useful for converting
57
57
  # paths to namespaces.
58
58
  #
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"
59
+ # camelize('active_model') # => "ActiveModel"
60
+ # camelize('active_model', false) # => "activeModel"
61
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
62
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
63
63
  #
64
64
  # 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:
65
+ # #underscore, though there are cases where that does not hold:
66
66
  #
67
- # 'SSLError'.underscore.camelize # => "SslError"
67
+ # camelize(underscore('SSLError')) # => "SslError"
68
68
  def camelize(term, uppercase_first_letter = true)
69
69
  string = term.to_s
70
70
  if uppercase_first_letter
71
- string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
71
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
72
72
  else
73
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
73
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
74
74
  end
75
75
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
76
- string.gsub!(/\//, '::')
76
+ string.gsub!("/", "::")
77
77
  string
78
78
  end
79
79
 
@@ -81,19 +81,19 @@ module ActiveSupport
81
81
  #
82
82
  # Changes '::' to '/' to convert namespaces to paths.
83
83
  #
84
- # 'ActiveModel'.underscore # => "active_model"
85
- # 'ActiveModel::Errors'.underscore # => "active_model/errors"
84
+ # underscore('ActiveModel') # => "active_model"
85
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
86
86
  #
87
87
  # 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:
88
+ # #camelize, though there are cases where that does not hold:
89
89
  #
90
- # 'SSLError'.underscore.camelize # => "SslError"
90
+ # camelize(underscore('SSLError')) # => "SslError"
91
91
  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')
92
+ return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
93
+ word = camel_cased_word.to_s.gsub("::", "/")
94
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$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
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.sub!(/_id\z/, "")
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,14 +262,14 @@ 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('::')
272
+ names = camel_cased_word.split("::")
252
273
 
253
274
  # Trigger a built-in NameError exception including the ill-formed constant in the message.
254
275
  Object.const_get(camel_cased_word) if names.empty?
@@ -266,7 +287,7 @@ module ActiveSupport
266
287
 
267
288
  # Go down the ancestors to check if it is owned directly. The check
268
289
  # stops when we reach Object or the end of ancestors tree.
269
- constant = constant.ancestors.inject do |const, ancestor|
290
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
270
291
  break const if ancestor == Object
271
292
  break ancestor if ancestor.const_defined?(name, false)
272
293
  const
@@ -280,8 +301,8 @@ module ActiveSupport
280
301
 
281
302
  # Tries to find a constant with the name specified in the argument string.
282
303
  #
283
- # 'Module'.safe_constantize # => Module
284
- # 'Test::Unit'.safe_constantize # => Test::Unit
304
+ # safe_constantize('Module') # => Module
305
+ # safe_constantize('Foo::Bar') # => Foo::Bar
285
306
  #
286
307
  # The name is assumed to be the one of a top-level constant, no matter
287
308
  # whether it starts with "::" or not. No lexical context is taken into
@@ -290,23 +311,25 @@ module ActiveSupport
290
311
  # C = 'outside'
291
312
  # module M
292
313
  # C = 'inside'
293
- # C # => 'inside'
294
- # 'C'.safe_constantize # => 'outside', same as ::C
314
+ # C # => 'inside'
315
+ # safe_constantize('C') # => 'outside', same as ::C
295
316
  # end
296
317
  #
297
318
  # +nil+ is returned when the name is not in CamelCase or the constant (or
298
319
  # part of it) is unknown.
299
320
  #
300
- # 'blargle'.safe_constantize # => nil
301
- # 'UnknownModule'.safe_constantize # => nil
302
- # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
321
+ # safe_constantize('blargle') # => nil
322
+ # safe_constantize('UnknownModule') # => nil
323
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
303
324
  def safe_constantize(camel_cased_word)
304
325
  constantize(camel_cased_word)
305
326
  rescue NameError => e
306
327
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
307
328
  e.name.to_s == camel_cased_word.to_s)
308
329
  rescue ArgumentError => e
309
- raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
330
+ raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
331
+ rescue LoadError => e
332
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
310
333
  end
311
334
 
312
335
  # Returns the suffix that should be added to a number to denote the position
@@ -319,18 +342,7 @@ module ActiveSupport
319
342
  # ordinal(-11) # => "th"
320
343
  # ordinal(-1021) # => "st"
321
344
  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
345
+ I18n.translate("number.nth.ordinals", number: number)
334
346
  end
335
347
 
336
348
  # Turns a number into an ordinal string used to denote the position in an
@@ -343,41 +355,43 @@ module ActiveSupport
343
355
  # ordinalize(-11) # => "-11th"
344
356
  # ordinalize(-1021) # => "-1021st"
345
357
  def ordinalize(number)
346
- "#{number}#{ordinal(number)}"
358
+ I18n.translate("number.nth.ordinalized", number: number)
347
359
  end
348
360
 
349
361
  private
362
+ # Mounts a regular expression, returned as a string to ease interpolation,
363
+ # that will match part by part the given constant.
364
+ #
365
+ # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
366
+ # const_regexp("::") # => "::"
367
+ def const_regexp(camel_cased_word)
368
+ parts = camel_cased_word.split("::")
350
369
 
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?
370
+ return Regexp.escape(camel_cased_word) if parts.blank?
360
371
 
361
- last = parts.pop
372
+ last = parts.pop
362
373
 
363
- parts.reverse.inject(last) do |acc, part|
364
- part.empty? ? acc : "#{part}(::#{acc})?"
374
+ parts.reverse.inject(last) do |acc, part|
375
+ part.empty? ? acc : "#{part}(::#{acc})?"
376
+ end
365
377
  end
366
- end
367
378
 
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
-
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
379
+ # Applies inflection rules for +singularize+ and +pluralize+.
380
+ #
381
+ # If passed an optional +locale+ parameter, the uncountables will be
382
+ # found for that locale.
383
+ #
384
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
385
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
386
+ def apply_inflections(word, rules, locale = :en)
387
+ result = word.to_s.dup
388
+
389
+ if word.empty? || inflections(locale).uncountables.uncountable?(result)
390
+ result
391
+ else
392
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
393
+ result
394
+ end
380
395
  end
381
- end
382
396
  end
383
397
  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
  #
@@ -51,47 +51,97 @@ module ActiveSupport
51
51
  #
52
52
  # Now you can have different transliterations for each locale:
53
53
  #
54
- # I18n.locale = :en
55
- # transliterate('Jürgen')
54
+ # transliterate('Jürgen', locale: :en)
56
55
  # # => "Jurgen"
57
56
  #
58
- # I18n.locale = :de
59
- # transliterate('Jürgen')
57
+ # transliterate('Jürgen', locale: :de)
60
58
  # # => "Juergen"
61
- def transliterate(string, replacement = "?")
62
- I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
63
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
64
- :replacement => replacement)
59
+ #
60
+ # Transliteration is restricted to UTF-8, US-ASCII and GB18030 strings
61
+ # Other encodings will raise an ArgumentError.
62
+ def transliterate(string, replacement = "?", locale: nil)
63
+ string = string.dup if string.frozen?
64
+ raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
65
+
66
+ allowed_encodings = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030]
67
+ raise ArgumentError, "Can not transliterate strings with #{string.encoding} encoding" unless allowed_encodings.include?(string.encoding)
68
+
69
+ input_encoding = string.encoding
70
+
71
+ # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
72
+ # US-ASCII is given. This way we can let tidy_bytes handle the string
73
+ # in the same way as we do for UTF-8
74
+ string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII
75
+
76
+ # GB18030 is Unicode compatible but is not a direct mapping so needs to be
77
+ # transcoded. Using invalid/undef :replace will result in loss of data in
78
+ # the event of invalid characters, but since tidy_bytes will replace
79
+ # invalid/undef with a "?" we're safe to do the same beforehand
80
+ string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030
81
+
82
+ transliterated = I18n.transliterate(
83
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
84
+ replacement: replacement,
85
+ locale: locale
86
+ )
87
+
88
+ # Restore the string encoding of the input if it was not UTF-8.
89
+ # Apply invalid/undef :replace as tidy_bytes does
90
+ transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding
91
+
92
+ transliterated
65
93
  end
66
94
 
67
95
  # Replaces special characters in a string so that it may be used as part of
68
96
  # a 'pretty' URL.
69
97
  #
70
- # class Person
71
- # def to_param
72
- # "#{id}-#{name.parameterize}"
73
- # end
74
- # end
75
- #
76
- # @person = Person.find(1)
77
- # # => #<Person id: 1, name: "Donald E. Knuth">
78
- #
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
83
- 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)
98
+ # parameterize("Donald E. Knuth") # => "donald-e-knuth"
99
+ # parameterize("^très|Jolie-- ") # => "tres-jolie"
100
+ #
101
+ # To use a custom separator, override the +separator+ argument.
102
+ #
103
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
104
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
105
+ #
106
+ # To preserve the case of the characters in a string, use the +preserve_case+ argument.
107
+ #
108
+ # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
109
+ # parameterize("^très|Jolie-- ", preserve_case: true) # => "tres-Jolie"
110
+ #
111
+ # It preserves dashes and underscores unless they are used as separators:
112
+ #
113
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
114
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
115
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
116
+ #
117
+ # If the optional parameter +locale+ is specified,
118
+ # the word will be parameterized as a word of that language.
119
+ # By default, this parameter is set to <tt>nil</tt> and it will use
120
+ # the configured <tt>I18n.locale<tt>.
121
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
122
+ # Replace accented chars with their ASCII equivalents.
123
+ parameterized_string = transliterate(string, locale: locale)
124
+
125
+ # Turn unwanted chars into the separator.
126
+ parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
127
+
128
+ unless separator.nil? || separator.empty?
129
+ if separator == "-"
130
+ re_duplicate_separator = /-{2,}/
131
+ re_leading_trailing_separator = /^-|-$/i
132
+ else
133
+ re_sep = Regexp.escape(separator)
134
+ re_duplicate_separator = /#{re_sep}{2,}/
135
+ re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
136
+ end
88
137
  # No more than one of the separator in a row.
89
- parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
138
+ parameterized_string.gsub!(re_duplicate_separator, separator)
90
139
  # Remove leading/trailing separator.
91
- parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
140
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
92
141
  end
93
- parameterized_string.downcase
94
- end
95
142
 
143
+ parameterized_string.downcase! unless preserve_case
144
+ parameterized_string
145
+ end
96
146
  end
97
147
  end