activesupport 1.2.4 → 8.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +505 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support/actionable_error.rb +50 -0
  6. data/lib/active_support/all.rb +5 -0
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +234 -0
  9. data/lib/active_support/benchmark.rb +21 -0
  10. data/lib/active_support/benchmarkable.rb +53 -0
  11. data/lib/active_support/broadcast_logger.rb +238 -0
  12. data/lib/active_support/builder.rb +8 -0
  13. data/lib/active_support/cache/coder.rb +153 -0
  14. data/lib/active_support/cache/entry.rb +134 -0
  15. data/lib/active_support/cache/file_store.rb +244 -0
  16. data/lib/active_support/cache/mem_cache_store.rb +288 -0
  17. data/lib/active_support/cache/memory_store.rb +264 -0
  18. data/lib/active_support/cache/null_store.rb +62 -0
  19. data/lib/active_support/cache/redis_cache_store.rb +498 -0
  20. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  21. data/lib/active_support/cache/strategy/local_cache.rb +246 -0
  22. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  23. data/lib/active_support/cache.rb +1170 -0
  24. data/lib/active_support/callbacks.rb +960 -0
  25. data/lib/active_support/class_attribute.rb +33 -0
  26. data/lib/active_support/code_generator.rb +79 -0
  27. data/lib/active_support/concern.rb +217 -0
  28. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  29. data/lib/active_support/concurrency/null_lock.rb +13 -0
  30. data/lib/active_support/concurrency/share_lock.rb +225 -0
  31. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  32. data/lib/active_support/configurable.rb +193 -0
  33. data/lib/active_support/configuration_file.rb +60 -0
  34. data/lib/active_support/continuous_integration.rb +145 -0
  35. data/lib/active_support/core_ext/array/access.rb +100 -0
  36. data/lib/active_support/core_ext/array/conversions.rb +209 -26
  37. data/lib/active_support/core_ext/array/extract.rb +21 -0
  38. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  39. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  40. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  41. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  42. data/lib/active_support/core_ext/array.rb +8 -4
  43. data/lib/active_support/core_ext/benchmark.rb +6 -0
  44. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  45. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  46. data/lib/active_support/core_ext/class/attribute.rb +137 -0
  47. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  48. data/lib/active_support/core_ext/class/subclasses.rb +24 -0
  49. data/lib/active_support/core_ext/class.rb +4 -0
  50. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  51. data/lib/active_support/core_ext/date/blank.rb +18 -0
  52. data/lib/active_support/core_ext/date/calculations.rb +161 -0
  53. data/lib/active_support/core_ext/date/conversions.rb +95 -28
  54. data/lib/active_support/core_ext/date/zones.rb +8 -0
  55. data/lib/active_support/core_ext/date.rb +6 -5
  56. data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
  57. data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -0
  58. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  59. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  60. data/lib/active_support/core_ext/date_time/blank.rb +18 -0
  61. data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
  62. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  63. data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
  64. data/lib/active_support/core_ext/date_time.rb +7 -0
  65. data/lib/active_support/core_ext/digest/uuid.rb +76 -0
  66. data/lib/active_support/core_ext/digest.rb +3 -0
  67. data/lib/active_support/core_ext/enumerable.rb +277 -7
  68. data/lib/active_support/core_ext/erb/util.rb +201 -0
  69. data/lib/active_support/core_ext/file/atomic.rb +72 -0
  70. data/lib/active_support/core_ext/file.rb +3 -0
  71. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  72. data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
  73. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  74. data/lib/active_support/core_ext/hash/except.rb +12 -0
  75. data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
  76. data/lib/active_support/core_ext/hash/keys.rb +134 -44
  77. data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
  78. data/lib/active_support/core_ext/hash/slice.rb +27 -0
  79. data/lib/active_support/core_ext/hash.rb +9 -8
  80. data/lib/active_support/core_ext/integer/inflections.rb +29 -13
  81. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  82. data/lib/active_support/core_ext/integer/time.rb +22 -0
  83. data/lib/active_support/core_ext/integer.rb +4 -6
  84. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  85. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  86. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  87. data/lib/active_support/core_ext/kernel.rb +4 -78
  88. data/lib/active_support/core_ext/load_error.rb +6 -35
  89. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  90. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  91. data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
  92. data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
  93. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
  94. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  95. data/lib/active_support/core_ext/module/delegation.rb +225 -0
  96. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  97. data/lib/active_support/core_ext/module/introspection.rb +65 -0
  98. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  99. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  100. data/lib/active_support/core_ext/module.rb +13 -0
  101. data/lib/active_support/core_ext/name_error.rb +59 -0
  102. data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
  103. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  104. data/lib/active_support/core_ext/numeric/time.rb +64 -57
  105. data/lib/active_support/core_ext/numeric.rb +4 -6
  106. data/lib/active_support/core_ext/object/acts_like.rb +45 -0
  107. data/lib/active_support/core_ext/object/blank.rb +199 -0
  108. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  109. data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
  110. data/lib/active_support/core_ext/object/duplicable.rb +69 -0
  111. data/lib/active_support/core_ext/object/inclusion.rb +37 -0
  112. data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
  113. data/lib/active_support/core_ext/object/json.rb +267 -0
  114. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  115. data/lib/active_support/core_ext/object/to_query.rb +93 -0
  116. data/lib/active_support/core_ext/object/try.rb +158 -0
  117. data/lib/active_support/core_ext/object/with.rb +46 -0
  118. data/lib/active_support/core_ext/object/with_options.rb +101 -0
  119. data/lib/active_support/core_ext/object.rb +17 -0
  120. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  121. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  122. data/lib/active_support/core_ext/pathname.rb +4 -0
  123. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  124. data/lib/active_support/core_ext/range/conversions.rb +58 -17
  125. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  126. data/lib/active_support/core_ext/range/sole.rb +17 -0
  127. data/lib/active_support/core_ext/range.rb +5 -4
  128. data/lib/active_support/core_ext/regexp.rb +14 -0
  129. data/lib/active_support/core_ext/securerandom.rb +57 -0
  130. data/lib/active_support/core_ext/string/access.rb +93 -56
  131. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  132. data/lib/active_support/core_ext/string/conversions.rb +57 -16
  133. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  134. data/lib/active_support/core_ext/string/filters.rb +151 -0
  135. data/lib/active_support/core_ext/string/indent.rb +45 -0
  136. data/lib/active_support/core_ext/string/inflections.rb +297 -54
  137. data/lib/active_support/core_ext/string/inquiry.rb +16 -0
  138. data/lib/active_support/core_ext/string/multibyte.rb +67 -0
  139. data/lib/active_support/core_ext/string/output_safety.rb +235 -0
  140. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
  141. data/lib/active_support/core_ext/string/strip.rb +27 -0
  142. data/lib/active_support/core_ext/string/zones.rb +16 -0
  143. data/lib/active_support/core_ext/string.rb +14 -10
  144. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  145. data/lib/active_support/core_ext/symbol.rb +3 -0
  146. data/lib/active_support/core_ext/thread/backtrace/location.rb +7 -0
  147. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  148. data/lib/active_support/core_ext/time/calculations.rb +358 -153
  149. data/lib/active_support/core_ext/time/compatibility.rb +15 -0
  150. data/lib/active_support/core_ext/time/conversions.rb +69 -30
  151. data/lib/active_support/core_ext/time/zones.rb +97 -0
  152. data/lib/active_support/core_ext/time.rb +6 -6
  153. data/lib/active_support/core_ext.rb +5 -1
  154. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  155. data/lib/active_support/current_attributes.rb +243 -0
  156. data/lib/active_support/deep_mergeable.rb +53 -0
  157. data/lib/active_support/delegation.rb +183 -0
  158. data/lib/active_support/dependencies/autoload.rb +72 -0
  159. data/lib/active_support/dependencies/interlock.rb +55 -0
  160. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  161. data/lib/active_support/dependencies.rb +84 -222
  162. data/lib/active_support/deprecation/behaviors.rb +148 -0
  163. data/lib/active_support/deprecation/constant_accessor.rb +74 -0
  164. data/lib/active_support/deprecation/deprecators.rb +104 -0
  165. data/lib/active_support/deprecation/disallowed.rb +54 -0
  166. data/lib/active_support/deprecation/method_wrappers.rb +68 -0
  167. data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
  168. data/lib/active_support/deprecation/reporting.rb +162 -0
  169. data/lib/active_support/deprecation.rb +81 -0
  170. data/lib/active_support/deprecator.rb +7 -0
  171. data/lib/active_support/descendants_tracker.rb +112 -0
  172. data/lib/active_support/digest.rb +22 -0
  173. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  174. data/lib/active_support/duration/iso8601_serializer.rb +64 -0
  175. data/lib/active_support/duration.rb +524 -0
  176. data/lib/active_support/editor.rb +70 -0
  177. data/lib/active_support/encrypted_configuration.rb +126 -0
  178. data/lib/active_support/encrypted_file.rb +133 -0
  179. data/lib/active_support/environment_inquirer.rb +40 -0
  180. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  181. data/lib/active_support/error_reporter.rb +318 -0
  182. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  183. data/lib/active_support/event_reporter.rb +592 -0
  184. data/lib/active_support/evented_file_update_checker.rb +185 -0
  185. data/lib/active_support/execution_context/test_helper.rb +13 -0
  186. data/lib/active_support/execution_context.rb +110 -0
  187. data/lib/active_support/execution_wrapper.rb +150 -0
  188. data/lib/active_support/executor/test_helper.rb +7 -0
  189. data/lib/active_support/executor.rb +8 -0
  190. data/lib/active_support/file_update_checker.rb +166 -0
  191. data/lib/active_support/fork_tracker.rb +43 -0
  192. data/lib/active_support/gem_version.rb +17 -0
  193. data/lib/active_support/gzip.rb +41 -0
  194. data/lib/active_support/hash_with_indifferent_access.rb +464 -0
  195. data/lib/active_support/html_safe_translation.rb +56 -0
  196. data/lib/active_support/i18n.rb +17 -0
  197. data/lib/active_support/i18n_railtie.rb +140 -0
  198. data/lib/active_support/inflections.rb +68 -49
  199. data/lib/active_support/inflector/inflections.rb +290 -0
  200. data/lib/active_support/inflector/methods.rb +387 -0
  201. data/lib/active_support/inflector/transliterate.rb +147 -0
  202. data/lib/active_support/inflector.rb +7 -164
  203. data/lib/active_support/isolated_execution_state.rb +76 -0
  204. data/lib/active_support/json/decoding.rb +78 -0
  205. data/lib/active_support/json/encoding.rb +256 -0
  206. data/lib/active_support/json.rb +4 -0
  207. data/lib/active_support/key_generator.rb +66 -0
  208. data/lib/active_support/lazy_load_hooks.rb +107 -0
  209. data/lib/active_support/locale/en.rb +33 -0
  210. data/lib/active_support/locale/en.yml +141 -0
  211. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  212. data/lib/active_support/log_subscriber.rb +188 -0
  213. data/lib/active_support/logger.rb +55 -0
  214. data/lib/active_support/logger_silence.rb +21 -0
  215. data/lib/active_support/logger_thread_safe_level.rb +50 -0
  216. data/lib/active_support/message_encryptor.rb +374 -0
  217. data/lib/active_support/message_encryptors.rb +193 -0
  218. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  219. data/lib/active_support/message_pack/extensions.rb +310 -0
  220. data/lib/active_support/message_pack/serializer.rb +63 -0
  221. data/lib/active_support/message_pack.rb +50 -0
  222. data/lib/active_support/message_verifier.rb +377 -0
  223. data/lib/active_support/message_verifiers.rb +189 -0
  224. data/lib/active_support/messages/codec.rb +65 -0
  225. data/lib/active_support/messages/metadata.rb +146 -0
  226. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  227. data/lib/active_support/messages/rotation_coordinator.rb +102 -0
  228. data/lib/active_support/messages/rotator.rb +69 -0
  229. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  230. data/lib/active_support/multibyte/chars.rb +188 -0
  231. data/lib/active_support/multibyte/unicode.rb +42 -0
  232. data/lib/active_support/multibyte.rb +27 -0
  233. data/lib/active_support/notifications/fanout.rb +467 -0
  234. data/lib/active_support/notifications/instrumenter.rb +240 -0
  235. data/lib/active_support/notifications.rb +281 -0
  236. data/lib/active_support/number_helper/number_converter.rb +190 -0
  237. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  238. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  239. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  240. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  241. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  242. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  243. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  244. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  245. data/lib/active_support/number_helper.rb +479 -0
  246. data/lib/active_support/option_merger.rb +38 -0
  247. data/lib/active_support/ordered_hash.rb +50 -0
  248. data/lib/active_support/ordered_options.rb +141 -25
  249. data/lib/active_support/parameter_filter.rb +157 -0
  250. data/lib/active_support/rails.rb +26 -0
  251. data/lib/active_support/railtie.rb +180 -0
  252. data/lib/active_support/reloader.rb +138 -0
  253. data/lib/active_support/rescuable.rb +176 -0
  254. data/lib/active_support/secure_compare_rotator.rb +58 -0
  255. data/lib/active_support/security_utils.rb +38 -0
  256. data/lib/active_support/string_inquirer.rb +35 -0
  257. data/lib/active_support/structured_event_subscriber.rb +99 -0
  258. data/lib/active_support/subscriber.rb +141 -0
  259. data/lib/active_support/syntax_error_proxy.rb +67 -0
  260. data/lib/active_support/tagged_logging.rb +157 -0
  261. data/lib/active_support/test_case.rb +365 -0
  262. data/lib/active_support/testing/assertions.rb +369 -0
  263. data/lib/active_support/testing/autorun.rb +10 -0
  264. data/lib/active_support/testing/constant_lookup.rb +51 -0
  265. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  266. data/lib/active_support/testing/declarative.rb +28 -0
  267. data/lib/active_support/testing/deprecation.rb +82 -0
  268. data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
  269. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  270. data/lib/active_support/testing/file_fixtures.rb +38 -0
  271. data/lib/active_support/testing/isolation.rb +121 -0
  272. data/lib/active_support/testing/method_call_assertions.rb +69 -0
  273. data/lib/active_support/testing/notification_assertions.rb +92 -0
  274. data/lib/active_support/testing/parallelization/server.rb +98 -0
  275. data/lib/active_support/testing/parallelization/worker.rb +107 -0
  276. data/lib/active_support/testing/parallelization.rb +79 -0
  277. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  278. data/lib/active_support/testing/setup_and_teardown.rb +57 -0
  279. data/lib/active_support/testing/stream.rb +41 -0
  280. data/lib/active_support/testing/tagged_logging.rb +27 -0
  281. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  282. data/lib/active_support/testing/time_helpers.rb +273 -0
  283. data/lib/active_support/time.rb +20 -0
  284. data/lib/active_support/time_with_zone.rb +613 -0
  285. data/lib/active_support/values/time_zone.rb +599 -158
  286. data/lib/active_support/version.rb +7 -6
  287. data/lib/active_support/xml_mini/jdom.rb +175 -0
  288. data/lib/active_support/xml_mini/libxml.rb +80 -0
  289. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  290. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  291. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  292. data/lib/active_support/xml_mini/rexml.rb +137 -0
  293. data/lib/active_support/xml_mini.rb +212 -0
  294. data/lib/active_support.rb +122 -10
  295. metadata +524 -93
  296. data/CHANGELOG +0 -283
  297. data/lib/active_support/binding_of_caller.rb +0 -84
  298. data/lib/active_support/breakpoint.rb +0 -523
  299. data/lib/active_support/class_attribute_accessors.rb +0 -57
  300. data/lib/active_support/class_inheritable_attributes.rb +0 -117
  301. data/lib/active_support/clean_logger.rb +0 -36
  302. data/lib/active_support/core_ext/blank.rb +0 -38
  303. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
  304. data/lib/active_support/core_ext/cgi.rb +0 -5
  305. data/lib/active_support/core_ext/exception.rb +0 -29
  306. data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
  307. data/lib/active_support/core_ext/object_and_class.rb +0 -44
  308. data/lib/active_support/module_attribute_accessors.rb +0 -57
  309. data/lib/active_support/whiny_nil.rb +0 -38
@@ -0,0 +1,387 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/inflections"
4
+
5
+ module ActiveSupport
6
+ # = Active Support \Inflector
7
+ #
8
+ # The Inflector transforms words from singular to plural, class names to table
9
+ # names, modularized class names to ones without, and class names to foreign
10
+ # keys. The default inflections for pluralization, singularization, and
11
+ # uncountable words are kept in inflections.rb.
12
+ #
13
+ # The \Rails core team has stated patches for the inflections library will not
14
+ # be accepted in order to avoid breaking legacy applications which may be
15
+ # relying on errant inflections. If you discover an incorrect inflection and
16
+ # require it for your application or wish to define rules for languages other
17
+ # than English, please correct or add them yourself (explained below).
18
+ module Inflector
19
+ extend self
20
+
21
+ # Returns the plural form of the word in the string.
22
+ #
23
+ # If passed an optional +locale+ parameter, the word will be
24
+ # pluralized using rules defined for that language. By default,
25
+ # this parameter is set to <tt>:en</tt>.
26
+ #
27
+ # pluralize('post') # => "posts"
28
+ # pluralize('octopus') # => "octopi"
29
+ # pluralize('sheep') # => "sheep"
30
+ # pluralize('words') # => "words"
31
+ # pluralize('CamelOctopus') # => "CamelOctopi"
32
+ # pluralize('ley', :es) # => "leyes"
33
+ def pluralize(word, locale = :en)
34
+ apply_inflections(word, inflections(locale).plurals, locale)
35
+ end
36
+
37
+ # The reverse of #pluralize, returns the singular form of a word in a
38
+ # string.
39
+ #
40
+ # If passed an optional +locale+ parameter, the word will be
41
+ # singularized using rules defined for that language. By default,
42
+ # this parameter is set to <tt>:en</tt>.
43
+ #
44
+ # singularize('posts') # => "post"
45
+ # singularize('octopi') # => "octopus"
46
+ # singularize('sheep') # => "sheep"
47
+ # singularize('word') # => "word"
48
+ # singularize('CamelOctopi') # => "CamelOctopus"
49
+ # singularize('leyes', :es) # => "ley"
50
+ def singularize(word, locale = :en)
51
+ apply_inflections(word, inflections(locale).singulars, locale)
52
+ end
53
+
54
+ # Converts strings to UpperCamelCase.
55
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
56
+ # lowerCamelCase.
57
+ #
58
+ # Also converts '/' to '::' which is useful for converting
59
+ # paths to namespaces.
60
+ #
61
+ # camelize('active_model') # => "ActiveModel"
62
+ # camelize('active_model', false) # => "activeModel"
63
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
64
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
65
+ #
66
+ # As a rule of thumb you can think of +camelize+ as the inverse of
67
+ # #underscore, though there are cases where that does not hold:
68
+ #
69
+ # camelize(underscore('SSLError')) # => "SslError"
70
+ def camelize(term, uppercase_first_letter = true)
71
+ string = term.to_s
72
+ # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
73
+ if !uppercase_first_letter || uppercase_first_letter == :lower
74
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
75
+ elsif string.match?(/\A[a-z\d]*\z/)
76
+ return inflections.acronyms[string]&.dup || string.capitalize
77
+ else
78
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
79
+ end
80
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
81
+ word = $2
82
+ substituted = inflections.acronyms[word] || word.capitalize! || word
83
+ $1 ? "::#{substituted}" : substituted
84
+ end
85
+ string
86
+ end
87
+
88
+ # Makes an underscored, lowercase form from the expression in the string.
89
+ #
90
+ # Changes '::' to '/' to convert namespaces to paths.
91
+ #
92
+ # underscore('ActiveModel') # => "active_model"
93
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
94
+ #
95
+ # As a rule of thumb you can think of +underscore+ as the inverse of
96
+ # #camelize, though there are cases where that does not hold:
97
+ #
98
+ # camelize(underscore('SSLError')) # => "SslError"
99
+ def underscore(camel_cased_word)
100
+ return camel_cased_word.to_s.dup unless /[A-Z-]|::/.match?(camel_cased_word)
101
+ word = camel_cased_word.to_s.gsub("::", "/")
102
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
103
+ word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
104
+ word.tr!("-", "_")
105
+ word.downcase!
106
+ word
107
+ end
108
+
109
+ # Tweaks an attribute name for display to end users.
110
+ #
111
+ # Specifically, performs these transformations:
112
+ #
113
+ # * Applies human inflection rules to the argument.
114
+ # * Deletes leading underscores, if any.
115
+ # * Removes an "_id" suffix if present.
116
+ # * Replaces underscores with spaces, if any.
117
+ # * Downcases all words except acronyms.
118
+ # * Capitalizes the first word.
119
+ # The capitalization of the first word can be turned off by setting the
120
+ # +:capitalize+ option to false (default is true).
121
+ #
122
+ # The trailing '_id' can be kept and capitalized by setting the
123
+ # optional parameter +keep_id_suffix+ to true (default is false).
124
+ #
125
+ # humanize('employee_salary') # => "Employee salary"
126
+ # humanize('author_id') # => "Author"
127
+ # humanize('author_id', capitalize: false) # => "author"
128
+ # humanize('_id') # => "Id"
129
+ # humanize('author_id', keep_id_suffix: true) # => "Author id"
130
+ #
131
+ # If "SSL" was defined to be an acronym:
132
+ #
133
+ # humanize('ssl_error') # => "SSL error"
134
+ #
135
+ def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
136
+ result = lower_case_and_underscored_word.to_s.dup
137
+
138
+ inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
139
+
140
+ result.tr!("_", " ")
141
+ result.lstrip!
142
+ if !keep_id_suffix && lower_case_and_underscored_word&.end_with?("_id")
143
+ result.delete_suffix!(" id")
144
+ end
145
+
146
+ result.gsub!(/([[[:alpha:]]\d]+)/i) do |match|
147
+ match.downcase!
148
+ inflections.acronyms[match] || match
149
+ end
150
+
151
+ if capitalize
152
+ result.sub!(/\A[[:alpha:]]/) do |match|
153
+ match.upcase!
154
+ match
155
+ end
156
+ end
157
+
158
+ result
159
+ end
160
+
161
+ # Converts the first character in the string to uppercase.
162
+ #
163
+ # upcase_first('what a Lovely Day') # => "What a Lovely Day"
164
+ # upcase_first('w') # => "W"
165
+ # upcase_first('') # => ""
166
+ def upcase_first(string)
167
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
168
+ end
169
+
170
+ # Converts the first character in the string to lowercase.
171
+ #
172
+ # downcase_first('If they enjoyed The Matrix') # => "if they enjoyed The Matrix"
173
+ # downcase_first('I') # => "i"
174
+ # downcase_first('') # => ""
175
+ def downcase_first(string)
176
+ string.length > 0 ? string[0].downcase.concat(string[1..-1]) : +""
177
+ end
178
+
179
+ # Capitalizes all the words and replaces some characters in the string to
180
+ # create a nicer looking title. +titleize+ is meant for creating pretty
181
+ # output. It is not used in the \Rails internals.
182
+ #
183
+ # The trailing '_id','Id'.. can be kept and capitalized by setting the
184
+ # optional parameter +keep_id_suffix+ to true.
185
+ # By default, this parameter is false.
186
+ #
187
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
188
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
189
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
190
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
191
+ # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
192
+ def titleize(word, keep_id_suffix: false)
193
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])[a-z]/) do |match|
194
+ match.capitalize
195
+ end
196
+ end
197
+
198
+ # Creates the name of a table like \Rails does for models to table names.
199
+ # This method uses the #pluralize method on the last word in the string.
200
+ #
201
+ # tableize('RawScaledScorer') # => "raw_scaled_scorers"
202
+ # tableize('ham_and_egg') # => "ham_and_eggs"
203
+ # tableize('fancyCategory') # => "fancy_categories"
204
+ def tableize(class_name)
205
+ pluralize(underscore(class_name))
206
+ end
207
+
208
+ # Creates a class name from a plural table name like \Rails does for table
209
+ # names to models. Note that this returns a string and not a Class. (To
210
+ # convert to an actual class follow +classify+ with #constantize.)
211
+ #
212
+ # classify('ham_and_eggs') # => "HamAndEgg"
213
+ # classify('posts') # => "Post"
214
+ #
215
+ # Singular names are not handled correctly:
216
+ #
217
+ # classify('calculus') # => "Calculu"
218
+ def classify(table_name)
219
+ # strip out any leading schema name
220
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
221
+ end
222
+
223
+ # Replaces underscores with dashes in the string.
224
+ #
225
+ # dasherize('puni_puni') # => "puni-puni"
226
+ def dasherize(underscored_word)
227
+ underscored_word.tr("_", "-")
228
+ end
229
+
230
+ # Removes the module part from the expression in the string.
231
+ #
232
+ # demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
233
+ # demodulize('Inflections') # => "Inflections"
234
+ # demodulize('::Inflections') # => "Inflections"
235
+ # demodulize('') # => ""
236
+ #
237
+ # See also #deconstantize.
238
+ def demodulize(path)
239
+ path = path.to_s
240
+ if i = path.rindex("::")
241
+ path[(i + 2), path.length]
242
+ else
243
+ path
244
+ end
245
+ end
246
+
247
+ # Removes the rightmost segment from the constant expression in the string.
248
+ #
249
+ # deconstantize('Net::HTTP') # => "Net"
250
+ # deconstantize('::Net::HTTP') # => "::Net"
251
+ # deconstantize('String') # => ""
252
+ # deconstantize('::String') # => ""
253
+ # deconstantize('') # => ""
254
+ #
255
+ # See also #demodulize.
256
+ def deconstantize(path)
257
+ path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
258
+ end
259
+
260
+ # Creates a foreign key name from a class name.
261
+ # +separate_class_name_and_id_with_underscore+ sets whether
262
+ # the method should put '_' between the name and 'id'.
263
+ #
264
+ # foreign_key('Message') # => "message_id"
265
+ # foreign_key('Message', false) # => "messageid"
266
+ # foreign_key('Admin::Post') # => "post_id"
267
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
268
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
269
+ end
270
+
271
+ # Tries to find a constant with the name specified in the argument string.
272
+ #
273
+ # constantize('Module') # => Module
274
+ # constantize('Foo::Bar') # => Foo::Bar
275
+ #
276
+ # The name is assumed to be the one of a top-level constant, no matter
277
+ # whether it starts with "::" or not. No lexical context is taken into
278
+ # account:
279
+ #
280
+ # C = 'outside'
281
+ # module M
282
+ # C = 'inside'
283
+ # C # => 'inside'
284
+ # constantize('C') # => 'outside', same as ::C
285
+ # end
286
+ #
287
+ # NameError is raised when the name is not in CamelCase or the constant is
288
+ # unknown.
289
+ def constantize(camel_cased_word)
290
+ Object.const_get(camel_cased_word)
291
+ end
292
+
293
+ # Tries to find a constant with the name specified in the argument string.
294
+ #
295
+ # safe_constantize('Module') # => Module
296
+ # safe_constantize('Foo::Bar') # => Foo::Bar
297
+ #
298
+ # The name is assumed to be the one of a top-level constant, no matter
299
+ # whether it starts with "::" or not. No lexical context is taken into
300
+ # account:
301
+ #
302
+ # C = 'outside'
303
+ # module M
304
+ # C = 'inside'
305
+ # C # => 'inside'
306
+ # safe_constantize('C') # => 'outside', same as ::C
307
+ # end
308
+ #
309
+ # +nil+ is returned when the name is not in CamelCase or the constant (or
310
+ # part of it) is unknown.
311
+ #
312
+ # safe_constantize('blargle') # => nil
313
+ # safe_constantize('UnknownModule') # => nil
314
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
315
+ def safe_constantize(camel_cased_word)
316
+ constantize(camel_cased_word)
317
+ rescue NameError => e
318
+ raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
319
+ e.name.to_s == camel_cased_word.to_s)
320
+ rescue LoadError => e
321
+ message = e.respond_to?(:original_message) ? e.original_message : e.message
322
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message)
323
+ end
324
+
325
+ # Returns the suffix that should be added to a number to denote the position
326
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
327
+ #
328
+ # ordinal(1) # => "st"
329
+ # ordinal(2) # => "nd"
330
+ # ordinal(1002) # => "nd"
331
+ # ordinal(1003) # => "rd"
332
+ # ordinal(-11) # => "th"
333
+ # ordinal(-1021) # => "st"
334
+ def ordinal(number)
335
+ I18n.translate("number.nth.ordinals", number: number)
336
+ end
337
+
338
+ # Turns a number into an ordinal string used to denote the position in an
339
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
340
+ #
341
+ # ordinalize(1) # => "1st"
342
+ # ordinalize(2) # => "2nd"
343
+ # ordinalize(1002) # => "1002nd"
344
+ # ordinalize(1003) # => "1003rd"
345
+ # ordinalize(-11) # => "-11th"
346
+ # ordinalize(-1021) # => "-1021st"
347
+ def ordinalize(number)
348
+ I18n.translate("number.nth.ordinalized", number: number)
349
+ end
350
+
351
+ private
352
+ # Mounts a regular expression, returned as a string to ease interpolation,
353
+ # that will match part by part the given constant.
354
+ #
355
+ # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
356
+ # const_regexp("::") # => "::"
357
+ def const_regexp(camel_cased_word)
358
+ parts = camel_cased_word.split("::")
359
+
360
+ return Regexp.escape(camel_cased_word) if parts.empty?
361
+
362
+ last = parts.pop
363
+
364
+ parts.reverse!.inject(last) do |acc, part|
365
+ part.empty? ? acc : "#{part}(::#{acc})?"
366
+ end
367
+ end
368
+
369
+ # Applies inflection rules for +singularize+ and +pluralize+.
370
+ #
371
+ # If passed an optional +locale+ parameter, the uncountables will be
372
+ # found for that locale.
373
+ #
374
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
375
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
376
+ def apply_inflections(word, rules, locale = :en)
377
+ result = word.to_s.dup
378
+
379
+ if word.empty? || inflections(locale).uncountables.uncountable?(result)
380
+ result
381
+ else
382
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
383
+ result
384
+ end
385
+ end
386
+ end
387
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/multibyte"
4
+ require "active_support/i18n"
5
+
6
+ module ActiveSupport
7
+ module Inflector
8
+ ALLOWED_ENCODINGS_FOR_TRANSLITERATE = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030].freeze
9
+
10
+ # Replaces non-ASCII characters with an ASCII approximation, or if none
11
+ # exists, a replacement character which defaults to "?".
12
+ #
13
+ # transliterate('Ærøskøbing')
14
+ # # => "AEroskobing"
15
+ #
16
+ # Default approximations are provided for Western/Latin characters,
17
+ # e.g, "ø", "ñ", "é", "ß", etc.
18
+ #
19
+ # This method is I18n aware, so you can set up custom approximations for a
20
+ # locale. This can be useful, for example, to transliterate German's "ü"
21
+ # and "ö" to "ue" and "oe", or to add support for transliterating Russian
22
+ # to ASCII.
23
+ #
24
+ # In order to make your custom transliterations available, you must set
25
+ # them as the <tt>i18n.transliterate.rule</tt> i18n key:
26
+ #
27
+ # # Store the transliterations in locales/de.yml
28
+ # i18n:
29
+ # transliterate:
30
+ # rule:
31
+ # ü: "ue"
32
+ # ö: "oe"
33
+ #
34
+ # # Or set them using Ruby
35
+ # I18n.backend.store_translations(:de, i18n: {
36
+ # transliterate: {
37
+ # rule: {
38
+ # 'ü' => 'ue',
39
+ # 'ö' => 'oe'
40
+ # }
41
+ # }
42
+ # })
43
+ #
44
+ # The value for <tt>i18n.transliterate.rule</tt> can be a simple Hash that
45
+ # maps characters to ASCII approximations as shown above, or, for more
46
+ # complex requirements, a Proc:
47
+ #
48
+ # I18n.backend.store_translations(:de, i18n: {
49
+ # transliterate: {
50
+ # rule: ->(string) { MyTransliterator.transliterate(string) }
51
+ # }
52
+ # })
53
+ #
54
+ # Now you can have different transliterations for each locale:
55
+ #
56
+ # transliterate('Jürgen', locale: :en)
57
+ # # => "Jurgen"
58
+ #
59
+ # transliterate('Jürgen', locale: :de)
60
+ # # => "Juergen"
61
+ #
62
+ # Transliteration is restricted to UTF-8, US-ASCII, and GB18030 strings.
63
+ # Other encodings will raise an ArgumentError.
64
+ def transliterate(string, replacement = "?", locale: nil)
65
+ raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
66
+ raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
67
+
68
+ return string.dup if string.ascii_only?
69
+ string = string.dup if string.frozen?
70
+
71
+ input_encoding = string.encoding
72
+
73
+ # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
74
+ # US-ASCII is given. This way we can let tidy_bytes handle the string
75
+ # in the same way as we do for UTF-8
76
+ string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII
77
+
78
+ # GB18030 is Unicode compatible but is not a direct mapping so needs to be
79
+ # transcoded. Using invalid/undef :replace will result in loss of data in
80
+ # the event of invalid characters, but since tidy_bytes will replace
81
+ # invalid/undef with a "?" we're safe to do the same beforehand
82
+ string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030
83
+
84
+ transliterated = I18n.transliterate(
85
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
86
+ replacement: replacement,
87
+ locale: locale
88
+ )
89
+
90
+ # Restore the string encoding of the input if it was not UTF-8.
91
+ # Apply invalid/undef :replace as tidy_bytes does
92
+ transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding
93
+
94
+ transliterated
95
+ end
96
+
97
+ # Replaces special characters in a string so that it may be used as part of
98
+ # a 'pretty' URL.
99
+ #
100
+ # parameterize("Donald E. Knuth") # => "donald-e-knuth"
101
+ # parameterize("^très|Jolie-- ") # => "tres-jolie"
102
+ #
103
+ # To use a custom separator, override the +separator+ argument.
104
+ #
105
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
106
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
107
+ #
108
+ # To preserve the case of the characters in a string, use the +preserve_case+ argument.
109
+ #
110
+ # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
111
+ # parameterize("^très|Jolie-- ", preserve_case: true) # => "tres-Jolie"
112
+ #
113
+ # It preserves dashes and underscores unless they are used as separators:
114
+ #
115
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
116
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
117
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
118
+ #
119
+ # If the optional parameter +locale+ is specified,
120
+ # the word will be parameterized as a word of that language.
121
+ # By default, this parameter is set to <tt>nil</tt> and it will use
122
+ # the configured <tt>I18n.locale</tt>.
123
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
124
+ # Replace accented chars with their ASCII equivalents.
125
+ parameterized_string = transliterate(string, locale: locale)
126
+
127
+ # Turn unwanted chars into the separator.
128
+ parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
129
+
130
+ unless separator.nil? || separator.empty?
131
+ # No more than one of the separator in a row.
132
+ if separator.length == 1
133
+ parameterized_string.squeeze!(separator)
134
+ else
135
+ re_sep = Regexp.escape(separator)
136
+ parameterized_string.gsub!(/#{re_sep}{2,}/, separator)
137
+ end
138
+ # Remove leading/trailing separator.
139
+ parameterized_string.delete_prefix!(separator)
140
+ parameterized_string.delete_suffix!(separator)
141
+ end
142
+
143
+ parameterized_string.downcase! unless preserve_case
144
+ parameterized_string
145
+ end
146
+ end
147
+ end