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,5 +1,4 @@
1
1
  require "active_support"
2
- require "rails"
3
2
  require "active_support/file_update_checker"
4
3
  require "active_support/core_ext/array/wrap"
5
4
 
@@ -10,20 +9,6 @@ module I18n
10
9
  config.i18n.load_path = []
11
10
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
12
11
 
13
- def self.reloader
14
- @reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! }
15
- end
16
-
17
- # Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
18
- # point, no path was added to the reloader, I18n.reload! is not triggered
19
- # on to_prepare callbacks. This will only happen on the config.after_initialize
20
- # callback below.
21
- initializer "i18n.callbacks" do
22
- ActionDispatch::Reloader.to_prepare do
23
- I18n::Railtie.reloader.execute_if_updated
24
- end
25
- end
26
-
27
12
  # Set the i18n configuration after initialization since a lot of
28
13
  # configuration is still usually done in application initializers.
29
14
  config.after_initialize do |app|
@@ -31,23 +16,33 @@ module I18n
31
16
  end
32
17
 
33
18
  # Trigger i18n config before any eager loading has happened
34
- # so it's ready if any classes require it when eager loaded
19
+ # so it's ready if any classes require it when eager loaded.
35
20
  config.before_eager_load do |app|
36
21
  I18n::Railtie.initialize_i18n(app)
37
22
  end
38
23
 
39
24
  protected
40
25
 
41
- # Setup i18n configuration
26
+ @i18n_inited = false
27
+
28
+ # Setup i18n configuration.
42
29
  def self.initialize_i18n(app)
43
30
  return if @i18n_inited
44
31
 
45
32
  fallbacks = app.config.i18n.delete(:fallbacks)
46
33
 
34
+ # Avoid issues with setting the default_locale by disabling available locales
35
+ # check while configuring.
36
+ enforce_available_locales = app.config.i18n.delete(:enforce_available_locales)
37
+ enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
38
+ I18n.enforce_available_locales = false
39
+
40
+ reloadable_paths = []
47
41
  app.config.i18n.each do |setting, value|
48
42
  case setting
49
43
  when :railties_load_path
50
- app.config.i18n.load_path.unshift(*value)
44
+ reloadable_paths = value
45
+ app.config.i18n.load_path.unshift(*value.map(&:existent).flatten)
51
46
  when :load_path
52
47
  I18n.load_path += value
53
48
  else
@@ -57,14 +52,32 @@ module I18n
57
52
 
58
53
  init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
59
54
 
60
- reloader.paths.concat I18n.load_path
61
- reloader.execute_if_updated
55
+ # Restore available locales check so it will take place from now on.
56
+ I18n.enforce_available_locales = enforce_available_locales
57
+
58
+ directories = watched_dirs_with_extensions(reloadable_paths)
59
+ reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
60
+ I18n.load_path.keep_if { |p| File.exist?(p) }
61
+ I18n.load_path |= reloadable_paths.map(&:existent).flatten
62
+
63
+ I18n.reload!
64
+ end
65
+
66
+ app.reloaders << reloader
67
+ app.reloader.to_run do
68
+ reloader.execute_if_updated { require_unload_lock! }
69
+ # TODO: remove the following line as soon as the return value of
70
+ # callbacks is ignored, that is, returning `false` does not
71
+ # display a deprecation warning or halts the callback chain.
72
+ true
73
+ end
74
+ reloader.execute
62
75
 
63
76
  @i18n_inited = true
64
77
  end
65
78
 
66
79
  def self.include_fallbacks_module
67
- I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
80
+ I18n.backend.class.include(I18n::Backend::Fallbacks)
68
81
  end
69
82
 
70
83
  def self.init_fallbacks(fallbacks)
@@ -92,5 +105,11 @@ module I18n
92
105
  raise "Unexpected fallback type #{fallbacks.inspect}"
93
106
  end
94
107
  end
108
+
109
+ def self.watched_dirs_with_extensions(paths)
110
+ paths.each_with_object({}) do |path, result|
111
+ result[path.absolute_current] = path.extensions
112
+ end
113
+ end
95
114
  end
96
115
  end
@@ -1,8 +1,16 @@
1
+ require 'active_support/inflector/inflections'
2
+
3
+ #--
4
+ # Defines the standard inflection rules. These are the starting point for
5
+ # new projects and are not considered complete. The current set of inflection
6
+ # rules is frozen. This means, we do not change them to become more complete.
7
+ # This is a safety measure to keep existing applications from breaking.
8
+ #++
1
9
  module ActiveSupport
2
- Inflector.inflections do |inflect|
10
+ Inflector.inflections(:en) do |inflect|
3
11
  inflect.plural(/$/, 's')
4
12
  inflect.plural(/s$/i, 's')
5
- inflect.plural(/(ax|test)is$/i, '\1es')
13
+ inflect.plural(/^(ax|test)is$/i, '\1es')
6
14
  inflect.plural(/(octop|vir)us$/i, '\1i')
7
15
  inflect.plural(/(octop|vir)i$/i, '\1i')
8
16
  inflect.plural(/(alias|status)$/i, '\1es')
@@ -16,17 +24,18 @@ module ActiveSupport
16
24
  inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
17
25
  inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
18
26
  inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
19
- inflect.plural(/([m|l])ouse$/i, '\1ice')
20
- inflect.plural(/([m|l])ice$/i, '\1ice')
27
+ inflect.plural(/^(m|l)ouse$/i, '\1ice')
28
+ inflect.plural(/^(m|l)ice$/i, '\1ice')
21
29
  inflect.plural(/^(ox)$/i, '\1en')
22
30
  inflect.plural(/^(oxen)$/i, '\1')
23
31
  inflect.plural(/(quiz)$/i, '\1zes')
24
32
 
25
33
  inflect.singular(/s$/i, '')
34
+ inflect.singular(/(ss)$/i, '\1')
26
35
  inflect.singular(/(n)ews$/i, '\1ews')
27
36
  inflect.singular(/([ti])a$/i, '\1um')
28
- inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
29
- inflect.singular(/(^analy)ses$/i, '\1sis')
37
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
38
+ inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
30
39
  inflect.singular(/([^f])ves$/i, '\1fe')
31
40
  inflect.singular(/(hive)s$/i, '\1')
32
41
  inflect.singular(/(tive)s$/i, '\1')
@@ -35,13 +44,14 @@ module ActiveSupport
35
44
  inflect.singular(/(s)eries$/i, '\1eries')
36
45
  inflect.singular(/(m)ovies$/i, '\1ovie')
37
46
  inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
38
- inflect.singular(/([m|l])ice$/i, '\1ouse')
39
- inflect.singular(/(bus)es$/i, '\1')
47
+ inflect.singular(/^(m|l)ice$/i, '\1ouse')
48
+ inflect.singular(/(bus)(es)?$/i, '\1')
40
49
  inflect.singular(/(o)es$/i, '\1')
41
50
  inflect.singular(/(shoe)s$/i, '\1')
42
- inflect.singular(/(cris|ax|test)es$/i, '\1is')
43
- inflect.singular(/(octop|vir)i$/i, '\1us')
44
- inflect.singular(/(alias|status)es$/i, '\1')
51
+ inflect.singular(/(cris|test)(is|es)$/i, '\1is')
52
+ inflect.singular(/^(a)x[ie]s$/i, '\1xis')
53
+ inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
54
+ inflect.singular(/(alias|status)(es)?$/i, '\1')
45
55
  inflect.singular(/^(ox)en/i, '\1')
46
56
  inflect.singular(/(vert|ind)ices$/i, '\1ex')
47
57
  inflect.singular(/(matr)ices$/i, '\1ix')
@@ -53,9 +63,8 @@ module ActiveSupport
53
63
  inflect.irregular('child', 'children')
54
64
  inflect.irregular('sex', 'sexes')
55
65
  inflect.irregular('move', 'moves')
56
- inflect.irregular('cow', 'kine')
57
66
  inflect.irregular('zombie', 'zombies')
58
67
 
59
- inflect.uncountable(%w(equipment information rice money species series fish sheep jeans))
68
+ inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
60
69
  end
61
70
  end
@@ -1,211 +1,242 @@
1
+ require 'concurrent/map'
2
+ require 'active_support/core_ext/array/prepend_and_append'
3
+ require 'active_support/i18n'
4
+
1
5
  module ActiveSupport
2
6
  module Inflector
3
- # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
4
- # inflection rules. Examples:
7
+ extend self
8
+
9
+ # A singleton instance of this class is yielded by Inflector.inflections,
10
+ # which can then be used to specify additional inflection rules. If passed
11
+ # an optional locale, rules for other languages can be specified. The
12
+ # default locale is <tt>:en</tt>. Only rules for English are provided.
5
13
  #
6
- # ActiveSupport::Inflector.inflections do |inflect|
14
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
7
15
  # inflect.plural /^(ox)$/i, '\1\2en'
8
16
  # inflect.singular /^(ox)en/i, '\1'
9
17
  #
10
18
  # inflect.irregular 'octopus', 'octopi'
11
19
  #
12
- # inflect.uncountable "equipment"
20
+ # inflect.uncountable 'equipment'
13
21
  # end
14
22
  #
15
- # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
16
- # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
17
- # already have been loaded.
23
+ # New rules are added at the top. So in the example above, the irregular
24
+ # rule for octopus will now be the first of the pluralization and
25
+ # singularization rules that is runs. This guarantees that your rules run
26
+ # before any of the rules that may already have been loaded.
18
27
  class Inflections
19
- def self.instance
20
- @__instance__ ||= new
28
+ @__instance__ = Concurrent::Map.new
29
+
30
+ class Uncountables < Array
31
+ def initialize
32
+ @regex_array = []
33
+ super
34
+ end
35
+
36
+ def delete(entry)
37
+ super entry
38
+ @regex_array.delete(to_regex(entry))
39
+ end
40
+
41
+ def <<(*word)
42
+ add(word)
43
+ end
44
+
45
+ def add(words)
46
+ self.concat(words.flatten.map(&:downcase))
47
+ @regex_array += self.map {|word| to_regex(word) }
48
+ self
49
+ end
50
+
51
+ def uncountable?(str)
52
+ @regex_array.any? { |regex| regex === str }
53
+ end
54
+
55
+ private
56
+ def to_regex(string)
57
+ /\b#{::Regexp.escape(string)}\Z/i
58
+ end
59
+ end
60
+
61
+ def self.instance(locale = :en)
62
+ @__instance__[locale] ||= new
21
63
  end
22
64
 
23
- attr_reader :plurals, :singulars, :uncountables, :humans
65
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
24
66
 
25
67
  def initialize
26
- @plurals, @singulars, @uncountables, @humans = [], [], [], []
68
+ @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], Uncountables.new, [], {}, /(?=a)b/
69
+ end
70
+
71
+ # Private, for the test suite.
72
+ def initialize_dup(orig) # :nodoc:
73
+ %w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
74
+ instance_variable_set("@#{scope}", orig.send(scope).dup)
75
+ end
76
+ end
77
+
78
+ # Specifies a new acronym. An acronym must be specified as it will appear
79
+ # in a camelized string. An underscore string that contains the acronym
80
+ # will retain the acronym when passed to +camelize+, +humanize+, or
81
+ # +titleize+. A camelized string that contains the acronym will maintain
82
+ # the acronym when titleized or humanized, and will convert the acronym
83
+ # into a non-delimited single lowercase word when passed to +underscore+.
84
+ #
85
+ # acronym 'HTML'
86
+ # titleize 'html' # => 'HTML'
87
+ # camelize 'html' # => 'HTML'
88
+ # underscore 'MyHTML' # => 'my_html'
89
+ #
90
+ # The acronym, however, must occur as a delimited unit and not be part of
91
+ # another word for conversions to recognize it:
92
+ #
93
+ # acronym 'HTTP'
94
+ # camelize 'my_http_delimited' # => 'MyHTTPDelimited'
95
+ # camelize 'https' # => 'Https', not 'HTTPs'
96
+ # underscore 'HTTPS' # => 'http_s', not 'https'
97
+ #
98
+ # acronym 'HTTPS'
99
+ # camelize 'https' # => 'HTTPS'
100
+ # underscore 'HTTPS' # => 'https'
101
+ #
102
+ # Note: Acronyms that are passed to +pluralize+ will no longer be
103
+ # recognized, since the acronym will not occur as a delimited unit in the
104
+ # pluralized result. To work around this, you must specify the pluralized
105
+ # form as an acronym as well:
106
+ #
107
+ # acronym 'API'
108
+ # camelize(pluralize('api')) # => 'Apis'
109
+ #
110
+ # acronym 'APIs'
111
+ # camelize(pluralize('api')) # => 'APIs'
112
+ #
113
+ # +acronym+ may be used to specify any word that contains an acronym or
114
+ # otherwise needs to maintain a non-standard capitalization. The only
115
+ # restriction is that the word must begin with a capital letter.
116
+ #
117
+ # acronym 'RESTful'
118
+ # underscore 'RESTful' # => 'restful'
119
+ # underscore 'RESTfulController' # => 'restful_controller'
120
+ # titleize 'RESTfulController' # => 'RESTful Controller'
121
+ # camelize 'restful' # => 'RESTful'
122
+ # camelize 'restful_controller' # => 'RESTfulController'
123
+ #
124
+ # acronym 'McDonald'
125
+ # underscore 'McDonald' # => 'mcdonald'
126
+ # camelize 'mcdonald' # => 'McDonald'
127
+ def acronym(word)
128
+ @acronyms[word.downcase] = word
129
+ @acronym_regex = /#{@acronyms.values.join("|")}/
27
130
  end
28
131
 
29
- # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
30
- # The replacement should always be a string that may include references to the matched data from the rule.
132
+ # Specifies a new pluralization rule and its replacement. The rule can
133
+ # either be a string or a regular expression. The replacement should
134
+ # always be a string that may include references to the matched data from
135
+ # the rule.
31
136
  def plural(rule, replacement)
32
137
  @uncountables.delete(rule) if rule.is_a?(String)
33
138
  @uncountables.delete(replacement)
34
- @plurals.insert(0, [rule, replacement])
139
+ @plurals.prepend([rule, replacement])
35
140
  end
36
141
 
37
- # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
38
- # The replacement should always be a string that may include references to the matched data from the rule.
142
+ # Specifies a new singularization rule and its replacement. The rule can
143
+ # either be a string or a regular expression. The replacement should
144
+ # always be a string that may include references to the matched data from
145
+ # the rule.
39
146
  def singular(rule, replacement)
40
147
  @uncountables.delete(rule) if rule.is_a?(String)
41
148
  @uncountables.delete(replacement)
42
- @singulars.insert(0, [rule, replacement])
149
+ @singulars.prepend([rule, replacement])
43
150
  end
44
151
 
45
- # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
46
- # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
152
+ # Specifies a new irregular that applies to both pluralization and
153
+ # singularization at the same time. This can only be used for strings, not
154
+ # regular expressions. You simply pass the irregular in singular and
155
+ # plural form.
47
156
  #
48
- # Examples:
49
157
  # irregular 'octopus', 'octopi'
50
158
  # irregular 'person', 'people'
51
159
  def irregular(singular, plural)
52
160
  @uncountables.delete(singular)
53
161
  @uncountables.delete(plural)
54
- if singular[0,1].upcase == plural[0,1].upcase
55
- plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
56
- plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
57
- singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
162
+
163
+ s0 = singular[0]
164
+ srest = singular[1..-1]
165
+
166
+ p0 = plural[0]
167
+ prest = plural[1..-1]
168
+
169
+ if s0.upcase == p0.upcase
170
+ plural(/(#{s0})#{srest}$/i, '\1' + prest)
171
+ plural(/(#{p0})#{prest}$/i, '\1' + prest)
172
+
173
+ singular(/(#{s0})#{srest}$/i, '\1' + srest)
174
+ singular(/(#{p0})#{prest}$/i, '\1' + srest)
58
175
  else
59
- plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
60
- plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
61
- plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
62
- plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
63
- singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
64
- singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
176
+ plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
177
+ plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
178
+ plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
179
+ plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
180
+
181
+ singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
182
+ singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
183
+ singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
184
+ singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
65
185
  end
66
186
  end
67
187
 
68
- # Add uncountable words that shouldn't be attempted inflected.
188
+ # Specifies words that are uncountable and should not be inflected.
69
189
  #
70
- # Examples:
71
- # uncountable "money"
72
- # uncountable "money", "information"
190
+ # uncountable 'money'
191
+ # uncountable 'money', 'information'
73
192
  # uncountable %w( money information rice )
74
193
  def uncountable(*words)
75
- (@uncountables << words).flatten!
194
+ @uncountables.add(words)
76
195
  end
77
196
 
78
- # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
79
- # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
80
- # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
197
+ # Specifies a humanized form of a string by a regular expression rule or
198
+ # by a string mapping. When using a regular expression based replacement,
199
+ # the normal humanize formatting is called after the replacement. When a
200
+ # string is used, the human form should be specified as desired (example:
201
+ # 'The name', not 'the_name').
81
202
  #
82
- # Examples:
83
203
  # human /_cnt$/i, '\1_count'
84
- # human "legacy_col_person_name", "Name"
204
+ # human 'legacy_col_person_name', 'Name'
85
205
  def human(rule, replacement)
86
- @humans.insert(0, [rule, replacement])
206
+ @humans.prepend([rule, replacement])
87
207
  end
88
208
 
89
- # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
90
- # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
91
- # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
209
+ # Clears the loaded inflections within a given scope (default is
210
+ # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
211
+ # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
212
+ # <tt>:humans</tt>.
92
213
  #
93
- # Examples:
94
214
  # clear :all
95
215
  # clear :plurals
96
216
  def clear(scope = :all)
97
217
  case scope
98
218
  when :all
99
- @plurals, @singulars, @uncountables, @humans = [], [], [], []
219
+ @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
100
220
  else
101
221
  instance_variable_set "@#{scope}", []
102
222
  end
103
223
  end
104
224
  end
105
225
 
106
- # Yields a singleton instance of Inflector::Inflections so you can specify additional
107
- # inflector rules.
226
+ # Yields a singleton instance of Inflector::Inflections so you can specify
227
+ # additional inflector rules. If passed an optional locale, rules for other
228
+ # languages can be specified. If not specified, defaults to <tt>:en</tt>.
229
+ # Only rules for English are provided.
108
230
  #
109
- # Example:
110
- # ActiveSupport::Inflector.inflections do |inflect|
111
- # inflect.uncountable "rails"
231
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
232
+ # inflect.uncountable 'rails'
112
233
  # end
113
- def inflections
234
+ def inflections(locale = :en)
114
235
  if block_given?
115
- yield Inflections.instance
116
- else
117
- Inflections.instance
118
- end
119
- end
120
-
121
- # Returns the plural form of the word in the string.
122
- #
123
- # Examples:
124
- # "post".pluralize # => "posts"
125
- # "octopus".pluralize # => "octopi"
126
- # "sheep".pluralize # => "sheep"
127
- # "words".pluralize # => "words"
128
- # "CamelOctopus".pluralize # => "CamelOctopi"
129
- def pluralize(word)
130
- result = word.to_s.dup
131
-
132
- if word.empty? || inflections.uncountables.include?(result.downcase)
133
- result
134
- else
135
- inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
136
- result
137
- end
138
- end
139
-
140
- # The reverse of +pluralize+, returns the singular form of a word in a string.
141
- #
142
- # Examples:
143
- # "posts".singularize # => "post"
144
- # "octopi".singularize # => "octopus"
145
- # "sheep".singularize # => "sheep"
146
- # "word".singularize # => "word"
147
- # "CamelOctopi".singularize # => "CamelOctopus"
148
- def singularize(word)
149
- result = word.to_s.dup
150
-
151
- if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
152
- result
236
+ yield Inflections.instance(locale)
153
237
  else
154
- inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
155
- result
238
+ Inflections.instance(locale)
156
239
  end
157
240
  end
158
-
159
- # Capitalizes the first word and turns underscores into spaces and strips a
160
- # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
161
- #
162
- # Examples:
163
- # "employee_salary" # => "Employee salary"
164
- # "author_id" # => "Author"
165
- def humanize(lower_case_and_underscored_word)
166
- result = lower_case_and_underscored_word.to_s.dup
167
-
168
- inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
169
- result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
170
- end
171
-
172
- # Capitalizes all the words and replaces some characters in the string to create
173
- # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
174
- # used in the Rails internals.
175
- #
176
- # +titleize+ is also aliased as as +titlecase+.
177
- #
178
- # Examples:
179
- # "man from the boondocks".titleize # => "Man From The Boondocks"
180
- # "x-men: the last stand".titleize # => "X Men: The Last Stand"
181
- def titleize(word)
182
- humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
183
- end
184
-
185
- # Create the name of a table like Rails does for models to table names. This method
186
- # uses the +pluralize+ method on the last word in the string.
187
- #
188
- # Examples
189
- # "RawScaledScorer".tableize # => "raw_scaled_scorers"
190
- # "egg_and_ham".tableize # => "egg_and_hams"
191
- # "fancyCategory".tableize # => "fancy_categories"
192
- def tableize(class_name)
193
- pluralize(underscore(class_name))
194
- end
195
-
196
- # Create a class name from a plural table name like Rails does for table names to models.
197
- # Note that this returns a string and not a Class. (To convert to an actual class
198
- # follow +classify+ with +constantize+.)
199
- #
200
- # Examples:
201
- # "egg_and_hams".classify # => "EggAndHam"
202
- # "posts".classify # => "Post"
203
- #
204
- # Singular names are not handled correctly:
205
- # "business".classify # => "Busines"
206
- def classify(table_name)
207
- # strip out any leading schema name
208
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
209
- end
210
241
  end
211
242
  end