activesupport 6.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 (250) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +572 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support.rb +96 -0
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/all.rb +5 -0
  8. data/lib/active_support/array_inquirer.rb +48 -0
  9. data/lib/active_support/backtrace_cleaner.rb +132 -0
  10. data/lib/active_support/benchmarkable.rb +51 -0
  11. data/lib/active_support/builder.rb +8 -0
  12. data/lib/active_support/cache.rb +830 -0
  13. data/lib/active_support/cache/file_store.rb +196 -0
  14. data/lib/active_support/cache/mem_cache_store.rb +212 -0
  15. data/lib/active_support/cache/memory_store.rb +174 -0
  16. data/lib/active_support/cache/null_store.rb +48 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +488 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +194 -0
  19. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  20. data/lib/active_support/callbacks.rb +856 -0
  21. data/lib/active_support/concern.rb +171 -0
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  23. data/lib/active_support/concurrency/share_lock.rb +227 -0
  24. data/lib/active_support/configurable.rb +146 -0
  25. data/lib/active_support/core_ext.rb +5 -0
  26. data/lib/active_support/core_ext/array.rb +9 -0
  27. data/lib/active_support/core_ext/array/access.rb +104 -0
  28. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -0
  34. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  35. data/lib/active_support/core_ext/benchmark.rb +16 -0
  36. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  37. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  38. data/lib/active_support/core_ext/class.rb +4 -0
  39. data/lib/active_support/core_ext/class/attribute.rb +141 -0
  40. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/class/subclasses.rb +54 -0
  42. data/lib/active_support/core_ext/date.rb +7 -0
  43. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  44. data/lib/active_support/core_ext/date/blank.rb +14 -0
  45. data/lib/active_support/core_ext/date/calculations.rb +146 -0
  46. data/lib/active_support/core_ext/date/conversions.rb +96 -0
  47. data/lib/active_support/core_ext/date/zones.rb +8 -0
  48. data/lib/active_support/core_ext/date_and_time/calculations.rb +351 -0
  49. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  50. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  51. data/lib/active_support/core_ext/date_time.rb +7 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +211 -0
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +107 -0
  57. data/lib/active_support/core_ext/digest.rb +3 -0
  58. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  59. data/lib/active_support/core_ext/enumerable.rb +188 -0
  60. data/lib/active_support/core_ext/file.rb +3 -0
  61. data/lib/active_support/core_ext/file/atomic.rb +70 -0
  62. data/lib/active_support/core_ext/hash.rb +10 -0
  63. data/lib/active_support/core_ext/hash/compact.rb +5 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +263 -0
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +34 -0
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +24 -0
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  69. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  71. data/lib/active_support/core_ext/hash/slice.rb +26 -0
  72. data/lib/active_support/core_ext/hash/transform_values.rb +5 -0
  73. data/lib/active_support/core_ext/integer.rb +5 -0
  74. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  75. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  76. data/lib/active_support/core_ext/integer/time.rb +22 -0
  77. data/lib/active_support/core_ext/kernel.rb +5 -0
  78. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  79. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  80. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  81. data/lib/active_support/core_ext/load_error.rb +9 -0
  82. data/lib/active_support/core_ext/marshal.rb +24 -0
  83. data/lib/active_support/core_ext/module.rb +13 -0
  84. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  85. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  86. data/lib/active_support/core_ext/module/attr_internal.rb +38 -0
  87. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  88. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  89. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  90. data/lib/active_support/core_ext/module/delegation.rb +313 -0
  91. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  92. data/lib/active_support/core_ext/module/introspection.rb +86 -0
  93. data/lib/active_support/core_ext/module/reachable.rb +6 -0
  94. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  95. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  96. data/lib/active_support/core_ext/name_error.rb +38 -0
  97. data/lib/active_support/core_ext/numeric.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/bytes.rb +66 -0
  99. data/lib/active_support/core_ext/numeric/conversions.rb +136 -0
  100. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  101. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  102. data/lib/active_support/core_ext/object.rb +16 -0
  103. data/lib/active_support/core_ext/object/acts_like.rb +21 -0
  104. data/lib/active_support/core_ext/object/blank.rb +155 -0
  105. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  106. data/lib/active_support/core_ext/object/deep_dup.rb +55 -0
  107. data/lib/active_support/core_ext/object/duplicable.rb +49 -0
  108. data/lib/active_support/core_ext/object/inclusion.rb +29 -0
  109. data/lib/active_support/core_ext/object/instance_variables.rb +30 -0
  110. data/lib/active_support/core_ext/object/json.rb +228 -0
  111. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  112. data/lib/active_support/core_ext/object/to_query.rb +89 -0
  113. data/lib/active_support/core_ext/object/try.rb +156 -0
  114. data/lib/active_support/core_ext/object/with_options.rb +82 -0
  115. data/lib/active_support/core_ext/range.rb +7 -0
  116. data/lib/active_support/core_ext/range/compare_range.rb +70 -0
  117. data/lib/active_support/core_ext/range/conversions.rb +41 -0
  118. data/lib/active_support/core_ext/range/each.rb +25 -0
  119. data/lib/active_support/core_ext/range/include_range.rb +9 -0
  120. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  121. data/lib/active_support/core_ext/range/overlaps.rb +10 -0
  122. data/lib/active_support/core_ext/regexp.rb +7 -0
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string.rb +15 -0
  125. data/lib/active_support/core_ext/string/access.rb +114 -0
  126. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  127. data/lib/active_support/core_ext/string/conversions.rb +59 -0
  128. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  129. data/lib/active_support/core_ext/string/filters.rb +145 -0
  130. data/lib/active_support/core_ext/string/indent.rb +45 -0
  131. data/lib/active_support/core_ext/string/inflections.rb +259 -0
  132. data/lib/active_support/core_ext/string/inquiry.rb +15 -0
  133. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  134. data/lib/active_support/core_ext/string/output_safety.rb +314 -0
  135. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  136. data/lib/active_support/core_ext/string/strip.rb +27 -0
  137. data/lib/active_support/core_ext/string/zones.rb +16 -0
  138. data/lib/active_support/core_ext/time.rb +7 -0
  139. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  140. data/lib/active_support/core_ext/time/calculations.rb +344 -0
  141. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  142. data/lib/active_support/core_ext/time/conversions.rb +72 -0
  143. data/lib/active_support/core_ext/time/zones.rb +113 -0
  144. data/lib/active_support/core_ext/uri.rb +25 -0
  145. data/lib/active_support/current_attributes.rb +203 -0
  146. data/lib/active_support/dependencies.rb +806 -0
  147. data/lib/active_support/dependencies/autoload.rb +79 -0
  148. data/lib/active_support/dependencies/interlock.rb +57 -0
  149. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  150. data/lib/active_support/deprecation.rb +46 -0
  151. data/lib/active_support/deprecation/behaviors.rb +109 -0
  152. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  153. data/lib/active_support/deprecation/instance_delegator.rb +39 -0
  154. data/lib/active_support/deprecation/method_wrappers.rb +78 -0
  155. data/lib/active_support/deprecation/proxy_wrappers.rb +173 -0
  156. data/lib/active_support/deprecation/reporting.rb +114 -0
  157. data/lib/active_support/descendants_tracker.rb +109 -0
  158. data/lib/active_support/digest.rb +20 -0
  159. data/lib/active_support/duration.rb +433 -0
  160. data/lib/active_support/duration/iso8601_parser.rb +124 -0
  161. data/lib/active_support/duration/iso8601_serializer.rb +54 -0
  162. data/lib/active_support/encrypted_configuration.rb +45 -0
  163. data/lib/active_support/encrypted_file.rb +100 -0
  164. data/lib/active_support/evented_file_update_checker.rb +235 -0
  165. data/lib/active_support/execution_wrapper.rb +129 -0
  166. data/lib/active_support/executor.rb +8 -0
  167. data/lib/active_support/file_update_checker.rb +163 -0
  168. data/lib/active_support/gem_version.rb +17 -0
  169. data/lib/active_support/gzip.rb +38 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +399 -0
  171. data/lib/active_support/i18n.rb +16 -0
  172. data/lib/active_support/i18n_railtie.rb +126 -0
  173. data/lib/active_support/inflections.rb +72 -0
  174. data/lib/active_support/inflector.rb +9 -0
  175. data/lib/active_support/inflector/inflections.rb +257 -0
  176. data/lib/active_support/inflector/methods.rb +398 -0
  177. data/lib/active_support/inflector/transliterate.rb +147 -0
  178. data/lib/active_support/json.rb +4 -0
  179. data/lib/active_support/json/decoding.rb +76 -0
  180. data/lib/active_support/json/encoding.rb +134 -0
  181. data/lib/active_support/key_generator.rb +41 -0
  182. data/lib/active_support/lazy_load_hooks.rb +82 -0
  183. data/lib/active_support/locale/en.rb +31 -0
  184. data/lib/active_support/locale/en.yml +135 -0
  185. data/lib/active_support/log_subscriber.rb +135 -0
  186. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  187. data/lib/active_support/logger.rb +93 -0
  188. data/lib/active_support/logger_silence.rb +45 -0
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -0
  190. data/lib/active_support/message_encryptor.rb +227 -0
  191. data/lib/active_support/message_verifier.rb +205 -0
  192. data/lib/active_support/messages/metadata.rb +71 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  194. data/lib/active_support/messages/rotator.rb +56 -0
  195. data/lib/active_support/multibyte.rb +23 -0
  196. data/lib/active_support/multibyte/chars.rb +216 -0
  197. data/lib/active_support/multibyte/unicode.rb +157 -0
  198. data/lib/active_support/notifications.rb +253 -0
  199. data/lib/active_support/notifications/fanout.rb +244 -0
  200. data/lib/active_support/notifications/instrumenter.rb +164 -0
  201. data/lib/active_support/number_helper.rb +378 -0
  202. data/lib/active_support/number_helper/number_converter.rb +184 -0
  203. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  204. data/lib/active_support/number_helper/number_to_delimited_converter.rb +31 -0
  205. data/lib/active_support/number_helper/number_to_human_converter.rb +70 -0
  206. data/lib/active_support/number_helper/number_to_human_size_converter.rb +61 -0
  207. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  208. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  209. data/lib/active_support/number_helper/number_to_rounded_converter.rb +56 -0
  210. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  211. data/lib/active_support/option_merger.rb +27 -0
  212. data/lib/active_support/ordered_hash.rb +50 -0
  213. data/lib/active_support/ordered_options.rb +85 -0
  214. data/lib/active_support/parameter_filter.rb +129 -0
  215. data/lib/active_support/per_thread_registry.rb +60 -0
  216. data/lib/active_support/proxy_object.rb +15 -0
  217. data/lib/active_support/rails.rb +29 -0
  218. data/lib/active_support/railtie.rb +80 -0
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +174 -0
  221. data/lib/active_support/security_utils.rb +31 -0
  222. data/lib/active_support/string_inquirer.rb +34 -0
  223. data/lib/active_support/subscriber.rb +169 -0
  224. data/lib/active_support/tagged_logging.rb +88 -0
  225. data/lib/active_support/test_case.rb +163 -0
  226. data/lib/active_support/testing/assertions.rb +228 -0
  227. data/lib/active_support/testing/autorun.rb +7 -0
  228. data/lib/active_support/testing/constant_lookup.rb +51 -0
  229. data/lib/active_support/testing/declarative.rb +28 -0
  230. data/lib/active_support/testing/deprecation.rb +38 -0
  231. data/lib/active_support/testing/file_fixtures.rb +38 -0
  232. data/lib/active_support/testing/isolation.rb +110 -0
  233. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  234. data/lib/active_support/testing/parallelization.rb +128 -0
  235. data/lib/active_support/testing/setup_and_teardown.rb +55 -0
  236. data/lib/active_support/testing/stream.rb +44 -0
  237. data/lib/active_support/testing/tagged_logging.rb +27 -0
  238. data/lib/active_support/testing/time_helpers.rb +200 -0
  239. data/lib/active_support/time.rb +20 -0
  240. data/lib/active_support/time_with_zone.rb +561 -0
  241. data/lib/active_support/values/time_zone.rb +570 -0
  242. data/lib/active_support/version.rb +10 -0
  243. data/lib/active_support/xml_mini.rb +202 -0
  244. data/lib/active_support/xml_mini/jdom.rb +183 -0
  245. data/lib/active_support/xml_mini/libxml.rb +80 -0
  246. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  247. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  248. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  249. data/lib/active_support/xml_mini/rexml.rb +130 -0
  250. metadata +385 -0
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/deep_merge"
4
+ require "active_support/core_ext/hash/except"
5
+ require "active_support/core_ext/hash/slice"
6
+ begin
7
+ require "i18n"
8
+ rescue LoadError => e
9
+ $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
10
+ raise e
11
+ end
12
+ require "active_support/lazy_load_hooks"
13
+
14
+ ActiveSupport.run_load_hooks(:i18n)
15
+ I18n.load_path << File.expand_path("locale/en.yml", __dir__)
16
+ I18n.load_path << File.expand_path("locale/en.rb", __dir__)
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/array/wrap"
5
+
6
+ # :enddoc:
7
+
8
+ module I18n
9
+ class Railtie < Rails::Railtie
10
+ config.i18n = ActiveSupport::OrderedOptions.new
11
+ config.i18n.railties_load_path = []
12
+ config.i18n.load_path = []
13
+ config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
14
+
15
+ # Set the i18n configuration after initialization since a lot of
16
+ # configuration is still usually done in application initializers.
17
+ config.after_initialize do |app|
18
+ I18n::Railtie.initialize_i18n(app)
19
+ end
20
+
21
+ # Trigger i18n config before any eager loading has happened
22
+ # so it's ready if any classes require it when eager loaded.
23
+ config.before_eager_load do |app|
24
+ I18n::Railtie.initialize_i18n(app)
25
+ end
26
+
27
+ @i18n_inited = false
28
+
29
+ # Setup i18n configuration.
30
+ def self.initialize_i18n(app)
31
+ return if @i18n_inited
32
+
33
+ fallbacks = app.config.i18n.delete(:fallbacks)
34
+
35
+ # Avoid issues with setting the default_locale by disabling available locales
36
+ # check while configuring.
37
+ enforce_available_locales = app.config.i18n.delete(:enforce_available_locales)
38
+ enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
39
+ I18n.enforce_available_locales = false
40
+
41
+ reloadable_paths = []
42
+ app.config.i18n.each do |setting, value|
43
+ case setting
44
+ when :railties_load_path
45
+ reloadable_paths = value
46
+ app.config.i18n.load_path.unshift(*value.flat_map(&:existent))
47
+ when :load_path
48
+ I18n.load_path += value
49
+ else
50
+ I18n.send("#{setting}=", value)
51
+ end
52
+ end
53
+
54
+ init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
55
+
56
+ # Restore available locales check so it will take place from now on.
57
+ I18n.enforce_available_locales = enforce_available_locales
58
+
59
+ directories = watched_dirs_with_extensions(reloadable_paths)
60
+ reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
61
+ I18n.load_path.keep_if { |p| File.exist?(p) }
62
+ I18n.load_path |= reloadable_paths.flat_map(&:existent)
63
+
64
+ I18n.reload!
65
+ end
66
+
67
+ app.reloaders << reloader
68
+ app.reloader.to_run do
69
+ reloader.execute_if_updated { require_unload_lock! }
70
+ end
71
+ reloader.execute
72
+
73
+ @i18n_inited = true
74
+ end
75
+
76
+ def self.include_fallbacks_module
77
+ I18n.backend.class.include(I18n::Backend::Fallbacks)
78
+ end
79
+
80
+ def self.init_fallbacks(fallbacks)
81
+ include_fallbacks_module
82
+
83
+ args = \
84
+ case fallbacks
85
+ when ActiveSupport::OrderedOptions
86
+ [*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
87
+ when Hash, Array
88
+ Array.wrap(fallbacks)
89
+ else # TrueClass
90
+ [I18n.default_locale]
91
+ end
92
+
93
+ if args.empty? || args.first.is_a?(Hash)
94
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
95
+ Using I18n fallbacks with an empty `defaults` sets the defaults to
96
+ include the `default_locale`. This behavior will change in Rails 6.1.
97
+ If you desire the default locale to be included in the defaults, please
98
+ explicitly configure it with `config.i18n.fallbacks.defaults =
99
+ [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
100
+ {...}]`. If you want to opt-in to the new behavior, use
101
+ `config.i18n.fallbacks.defaults = [nil, {...}]`.
102
+ MSG
103
+ args.unshift I18n.default_locale
104
+ end
105
+
106
+ I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
107
+ end
108
+
109
+ def self.validate_fallbacks(fallbacks)
110
+ case fallbacks
111
+ when ActiveSupport::OrderedOptions
112
+ !fallbacks.empty?
113
+ when TrueClass, Array, Hash
114
+ true
115
+ else
116
+ raise "Unexpected fallback type #{fallbacks.inspect}"
117
+ end
118
+ end
119
+
120
+ def self.watched_dirs_with_extensions(paths)
121
+ paths.each_with_object({}) do |path, result|
122
+ result[path.absolute_current] = path.extensions
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/inflector/inflections"
4
+
5
+ #--
6
+ # Defines the standard inflection rules. These are the starting point for
7
+ # new projects and are not considered complete. The current set of inflection
8
+ # rules is frozen. This means, we do not change them to become more complete.
9
+ # This is a safety measure to keep existing applications from breaking.
10
+ #++
11
+ module ActiveSupport
12
+ Inflector.inflections(:en) do |inflect|
13
+ inflect.plural(/$/, "s")
14
+ inflect.plural(/s$/i, "s")
15
+ inflect.plural(/^(ax|test)is$/i, '\1es')
16
+ inflect.plural(/(octop|vir)us$/i, '\1i')
17
+ inflect.plural(/(octop|vir)i$/i, '\1i')
18
+ inflect.plural(/(alias|status)$/i, '\1es')
19
+ inflect.plural(/(bu)s$/i, '\1ses')
20
+ inflect.plural(/(buffal|tomat)o$/i, '\1oes')
21
+ inflect.plural(/([ti])um$/i, '\1a')
22
+ inflect.plural(/([ti])a$/i, '\1a')
23
+ inflect.plural(/sis$/i, "ses")
24
+ inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
25
+ inflect.plural(/(hive)$/i, '\1s')
26
+ inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
27
+ inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
28
+ inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
29
+ inflect.plural(/^(m|l)ouse$/i, '\1ice')
30
+ inflect.plural(/^(m|l)ice$/i, '\1ice')
31
+ inflect.plural(/^(ox)$/i, '\1en')
32
+ inflect.plural(/^(oxen)$/i, '\1')
33
+ inflect.plural(/(quiz)$/i, '\1zes')
34
+
35
+ inflect.singular(/s$/i, "")
36
+ inflect.singular(/(ss)$/i, '\1')
37
+ inflect.singular(/(n)ews$/i, '\1ews')
38
+ inflect.singular(/([ti])a$/i, '\1um')
39
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
40
+ inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
41
+ inflect.singular(/([^f])ves$/i, '\1fe')
42
+ inflect.singular(/(hive)s$/i, '\1')
43
+ inflect.singular(/(tive)s$/i, '\1')
44
+ inflect.singular(/([lr])ves$/i, '\1f')
45
+ inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
46
+ inflect.singular(/(s)eries$/i, '\1eries')
47
+ inflect.singular(/(m)ovies$/i, '\1ovie')
48
+ inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
49
+ inflect.singular(/^(m|l)ice$/i, '\1ouse')
50
+ inflect.singular(/(bus)(es)?$/i, '\1')
51
+ inflect.singular(/(o)es$/i, '\1')
52
+ inflect.singular(/(shoe)s$/i, '\1')
53
+ inflect.singular(/(cris|test)(is|es)$/i, '\1is')
54
+ inflect.singular(/^(a)x[ie]s$/i, '\1xis')
55
+ inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
56
+ inflect.singular(/(alias|status)(es)?$/i, '\1')
57
+ inflect.singular(/^(ox)en/i, '\1')
58
+ inflect.singular(/(vert|ind)ices$/i, '\1ex')
59
+ inflect.singular(/(matr)ices$/i, '\1ix')
60
+ inflect.singular(/(quiz)zes$/i, '\1')
61
+ inflect.singular(/(database)s$/i, '\1')
62
+
63
+ inflect.irregular("person", "people")
64
+ inflect.irregular("man", "men")
65
+ inflect.irregular("child", "children")
66
+ inflect.irregular("sex", "sexes")
67
+ inflect.irregular("move", "moves")
68
+ inflect.irregular("zombie", "zombies")
69
+
70
+ inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
71
+ end
72
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # in case active_support/inflector is required without the rest of active_support
4
+ require "active_support/inflector/inflections"
5
+ require "active_support/inflector/transliterate"
6
+ require "active_support/inflector/methods"
7
+
8
+ require "active_support/inflections"
9
+ require "active_support/core_ext/string/inflections"
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "active_support/i18n"
5
+ require "active_support/deprecation"
6
+
7
+ module ActiveSupport
8
+ module Inflector
9
+ extend self
10
+
11
+ # A singleton instance of this class is yielded by Inflector.inflections,
12
+ # which can then be used to specify additional inflection rules. If passed
13
+ # an optional locale, rules for other languages can be specified. The
14
+ # default locale is <tt>:en</tt>. Only rules for English are provided.
15
+ #
16
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
17
+ # inflect.plural /^(ox)$/i, '\1\2en'
18
+ # inflect.singular /^(ox)en/i, '\1'
19
+ #
20
+ # inflect.irregular 'octopus', 'octopi'
21
+ #
22
+ # inflect.uncountable 'equipment'
23
+ # end
24
+ #
25
+ # New rules are added at the top. So in the example above, the irregular
26
+ # rule for octopus will now be the first of the pluralization and
27
+ # singularization rules that is runs. This guarantees that your rules run
28
+ # before any of the rules that may already have been loaded.
29
+ class Inflections
30
+ @__instance__ = Concurrent::Map.new
31
+
32
+ class Uncountables < Array
33
+ def initialize
34
+ @regex_array = []
35
+ super
36
+ end
37
+
38
+ def delete(entry)
39
+ super entry
40
+ @regex_array.delete(to_regex(entry))
41
+ end
42
+
43
+ def <<(*word)
44
+ add(word)
45
+ end
46
+
47
+ def add(words)
48
+ words = words.flatten.map(&:downcase)
49
+ concat(words)
50
+ @regex_array += words.map { |word| to_regex(word) }
51
+ self
52
+ end
53
+
54
+ def uncountable?(str)
55
+ @regex_array.any? { |regex| regex.match? str }
56
+ end
57
+
58
+ private
59
+ def to_regex(string)
60
+ /\b#{::Regexp.escape(string)}\Z/i
61
+ end
62
+ end
63
+
64
+ def self.instance(locale = :en)
65
+ @__instance__[locale] ||= new
66
+ end
67
+
68
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
69
+
70
+ attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
71
+
72
+ def initialize
73
+ @plurals, @singulars, @uncountables, @humans, @acronyms = [], [], Uncountables.new, [], {}
74
+ define_acronym_regex_patterns
75
+ end
76
+
77
+ # Private, for the test suite.
78
+ def initialize_dup(orig) # :nodoc:
79
+ %w(plurals singulars uncountables humans acronyms).each do |scope|
80
+ instance_variable_set("@#{scope}", orig.send(scope).dup)
81
+ end
82
+ define_acronym_regex_patterns
83
+ end
84
+
85
+ # Specifies a new acronym. An acronym must be specified as it will appear
86
+ # in a camelized string. An underscore string that contains the acronym
87
+ # will retain the acronym when passed to +camelize+, +humanize+, or
88
+ # +titleize+. A camelized string that contains the acronym will maintain
89
+ # the acronym when titleized or humanized, and will convert the acronym
90
+ # into a non-delimited single lowercase word when passed to +underscore+.
91
+ #
92
+ # acronym 'HTML'
93
+ # titleize 'html' # => 'HTML'
94
+ # camelize 'html' # => 'HTML'
95
+ # underscore 'MyHTML' # => 'my_html'
96
+ #
97
+ # The acronym, however, must occur as a delimited unit and not be part of
98
+ # another word for conversions to recognize it:
99
+ #
100
+ # acronym 'HTTP'
101
+ # camelize 'my_http_delimited' # => 'MyHTTPDelimited'
102
+ # camelize 'https' # => 'Https', not 'HTTPs'
103
+ # underscore 'HTTPS' # => 'http_s', not 'https'
104
+ #
105
+ # acronym 'HTTPS'
106
+ # camelize 'https' # => 'HTTPS'
107
+ # underscore 'HTTPS' # => 'https'
108
+ #
109
+ # Note: Acronyms that are passed to +pluralize+ will no longer be
110
+ # recognized, since the acronym will not occur as a delimited unit in the
111
+ # pluralized result. To work around this, you must specify the pluralized
112
+ # form as an acronym as well:
113
+ #
114
+ # acronym 'API'
115
+ # camelize(pluralize('api')) # => 'Apis'
116
+ #
117
+ # acronym 'APIs'
118
+ # camelize(pluralize('api')) # => 'APIs'
119
+ #
120
+ # +acronym+ may be used to specify any word that contains an acronym or
121
+ # otherwise needs to maintain a non-standard capitalization. The only
122
+ # restriction is that the word must begin with a capital letter.
123
+ #
124
+ # acronym 'RESTful'
125
+ # underscore 'RESTful' # => 'restful'
126
+ # underscore 'RESTfulController' # => 'restful_controller'
127
+ # titleize 'RESTfulController' # => 'RESTful Controller'
128
+ # camelize 'restful' # => 'RESTful'
129
+ # camelize 'restful_controller' # => 'RESTfulController'
130
+ #
131
+ # acronym 'McDonald'
132
+ # underscore 'McDonald' # => 'mcdonald'
133
+ # camelize 'mcdonald' # => 'McDonald'
134
+ def acronym(word)
135
+ @acronyms[word.downcase] = word
136
+ define_acronym_regex_patterns
137
+ end
138
+
139
+ # Specifies a new pluralization rule and its replacement. The rule can
140
+ # either be a string or a regular expression. The replacement should
141
+ # always be a string that may include references to the matched data from
142
+ # the rule.
143
+ def plural(rule, replacement)
144
+ @uncountables.delete(rule) if rule.is_a?(String)
145
+ @uncountables.delete(replacement)
146
+ @plurals.prepend([rule, replacement])
147
+ end
148
+
149
+ # Specifies a new singularization rule and its replacement. The rule can
150
+ # either be a string or a regular expression. The replacement should
151
+ # always be a string that may include references to the matched data from
152
+ # the rule.
153
+ def singular(rule, replacement)
154
+ @uncountables.delete(rule) if rule.is_a?(String)
155
+ @uncountables.delete(replacement)
156
+ @singulars.prepend([rule, replacement])
157
+ end
158
+
159
+ # Specifies a new irregular that applies to both pluralization and
160
+ # singularization at the same time. This can only be used for strings, not
161
+ # regular expressions. You simply pass the irregular in singular and
162
+ # plural form.
163
+ #
164
+ # irregular 'octopus', 'octopi'
165
+ # irregular 'person', 'people'
166
+ def irregular(singular, plural)
167
+ @uncountables.delete(singular)
168
+ @uncountables.delete(plural)
169
+
170
+ s0 = singular[0]
171
+ srest = singular[1..-1]
172
+
173
+ p0 = plural[0]
174
+ prest = plural[1..-1]
175
+
176
+ if s0.upcase == p0.upcase
177
+ plural(/(#{s0})#{srest}$/i, '\1' + prest)
178
+ plural(/(#{p0})#{prest}$/i, '\1' + prest)
179
+
180
+ singular(/(#{s0})#{srest}$/i, '\1' + srest)
181
+ singular(/(#{p0})#{prest}$/i, '\1' + srest)
182
+ else
183
+ plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
184
+ plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
185
+ plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
186
+ plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
187
+
188
+ singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
189
+ singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
190
+ singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
191
+ singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
192
+ end
193
+ end
194
+
195
+ # Specifies words that are uncountable and should not be inflected.
196
+ #
197
+ # uncountable 'money'
198
+ # uncountable 'money', 'information'
199
+ # uncountable %w( money information rice )
200
+ def uncountable(*words)
201
+ @uncountables.add(words)
202
+ end
203
+
204
+ # Specifies a humanized form of a string by a regular expression rule or
205
+ # by a string mapping. When using a regular expression based replacement,
206
+ # the normal humanize formatting is called after the replacement. When a
207
+ # string is used, the human form should be specified as desired (example:
208
+ # 'The name', not 'the_name').
209
+ #
210
+ # human /_cnt$/i, '\1_count'
211
+ # human 'legacy_col_person_name', 'Name'
212
+ def human(rule, replacement)
213
+ @humans.prepend([rule, replacement])
214
+ end
215
+
216
+ # Clears the loaded inflections within a given scope (default is
217
+ # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
218
+ # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
219
+ # <tt>:humans</tt>.
220
+ #
221
+ # clear :all
222
+ # clear :plurals
223
+ def clear(scope = :all)
224
+ case scope
225
+ when :all
226
+ @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
227
+ else
228
+ instance_variable_set "@#{scope}", []
229
+ end
230
+ end
231
+
232
+ private
233
+
234
+ def define_acronym_regex_patterns
235
+ @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
236
+ @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
237
+ @acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/
238
+ end
239
+ end
240
+
241
+ # Yields a singleton instance of Inflector::Inflections so you can specify
242
+ # additional inflector rules. If passed an optional locale, rules for other
243
+ # languages can be specified. If not specified, defaults to <tt>:en</tt>.
244
+ # Only rules for English are provided.
245
+ #
246
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
247
+ # inflect.uncountable 'rails'
248
+ # end
249
+ def inflections(locale = :en)
250
+ if block_given?
251
+ yield Inflections.instance(locale)
252
+ else
253
+ Inflections.instance(locale)
254
+ end
255
+ end
256
+ end
257
+ end