activesupport 3.1.0 → 5.0.0

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