activesupport 4.0.12 → 7.0.2.4

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.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (295) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +249 -501
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +41 -13
  9. data/lib/active_support/benchmarkable.rb +7 -15
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +96 -74
  12. data/lib/active_support/cache/mem_cache_store.rb +211 -103
  13. data/lib/active_support/cache/memory_store.rb +90 -58
  14. data/lib/active_support/cache/null_store.rb +19 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +468 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +86 -83
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  18. data/lib/active_support/cache.rb +580 -241
  19. data/lib/active_support/callbacks.rb +812 -425
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +103 -14
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
  23. data/lib/active_support/concurrency/share_lock.rb +226 -0
  24. data/lib/active_support/configurable.rb +21 -19
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +47 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +35 -44
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +26 -16
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  34. data/lib/active_support/core_ext/array.rb +10 -7
  35. data/lib/active_support/core_ext/benchmark.rb +5 -3
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  37. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  38. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  40. data/lib/active_support/core_ext/class/subclasses.rb +25 -26
  41. data/lib/active_support/core_ext/class.rb +4 -4
  42. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  43. data/lib/active_support/core_ext/date/blank.rb +14 -0
  44. data/lib/active_support/core_ext/date/calculations.rb +31 -18
  45. data/lib/active_support/core_ext/date/conversions.rb +43 -32
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +5 -34
  48. data/lib/active_support/core_ext/date.rb +7 -4
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +8 -4
  59. data/lib/active_support/core_ext/digest/uuid.rb +79 -0
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +249 -17
  62. data/lib/active_support/core_ext/file/atomic.rb +41 -32
  63. data/lib/active_support/core_ext/file.rb +3 -1
  64. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +14 -5
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +39 -56
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -23
  72. data/lib/active_support/core_ext/hash.rb +10 -8
  73. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +11 -33
  76. data/lib/active_support/core_ext/integer.rb +5 -3
  77. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  80. data/lib/active_support/core_ext/kernel.rb +5 -4
  81. data/lib/active_support/core_ext/load_error.rb +5 -21
  82. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  83. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  84. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
  87. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  88. data/lib/active_support/core_ext/module/delegation.rb +172 -45
  89. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  90. data/lib/active_support/core_ext/module/introspection.rb +23 -38
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -10
  94. data/lib/active_support/core_ext/name_error.rb +45 -4
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  99. data/lib/active_support/core_ext/numeric.rb +6 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +70 -20
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  104. data/lib/active_support/core_ext/object/duplicable.rb +17 -47
  105. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +244 -0
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  110. data/lib/active_support/core_ext/object/try.rb +106 -26
  111. data/lib/active_support/core_ext/object/with_options.rb +64 -5
  112. data/lib/active_support/core_ext/object.rb +14 -12
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +18 -17
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  121. data/lib/active_support/core_ext/range.rb +7 -4
  122. data/lib/active_support/core_ext/regexp.rb +10 -1
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string/access.rb +42 -51
  125. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  126. data/lib/active_support/core_ext/string/conversions.rb +18 -13
  127. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  128. data/lib/active_support/core_ext/string/filters.rb +97 -7
  129. data/lib/active_support/core_ext/string/indent.rb +6 -4
  130. data/lib/active_support/core_ext/string/inflections.rb +106 -25
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  133. data/lib/active_support/core_ext/string/output_safety.rb +227 -54
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +6 -5
  136. data/lib/active_support/core_ext/string/zones.rb +4 -1
  137. data/lib/active_support/core_ext/string.rb +15 -13
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  141. data/lib/active_support/core_ext/time/calculations.rb +178 -116
  142. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  143. data/lib/active_support/core_ext/time/conversions.rb +37 -25
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +44 -42
  146. data/lib/active_support/core_ext/time.rb +8 -5
  147. data/lib/active_support/core_ext/uri.rb +4 -25
  148. data/lib/active_support/core_ext.rb +4 -2
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +3 -1
  152. data/lib/active_support/dependencies/interlock.rb +49 -0
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +71 -696
  155. data/lib/active_support/deprecation/behaviors.rb +65 -16
  156. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  159. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
  161. data/lib/active_support/deprecation/reporting.rb +81 -18
  162. data/lib/active_support/deprecation.rb +19 -11
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  166. data/lib/active_support/duration/iso8601_serializer.rb +67 -0
  167. data/lib/active_support/duration.rb +437 -39
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +117 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +170 -0
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +151 -0
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +8 -0
  178. data/lib/active_support/file_update_checker.rb +62 -37
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +17 -0
  181. data/lib/active_support/gzip.rb +7 -5
  182. data/lib/active_support/hash_with_indifferent_access.rb +207 -54
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +10 -6
  185. data/lib/active_support/i18n_railtie.rb +48 -19
  186. data/lib/active_support/inflections.rb +19 -12
  187. data/lib/active_support/inflector/inflections.rb +97 -37
  188. data/lib/active_support/inflector/methods.rb +192 -157
  189. data/lib/active_support/inflector/transliterate.rb +83 -33
  190. data/lib/active_support/inflector.rb +7 -5
  191. data/lib/active_support/isolated_execution_state.rb +64 -0
  192. data/lib/active_support/json/decoding.rb +37 -42
  193. data/lib/active_support/json/encoding.rb +93 -293
  194. data/lib/active_support/json.rb +4 -2
  195. data/lib/active_support/key_generator.rb +30 -47
  196. data/lib/active_support/lazy_load_hooks.rb +54 -21
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +10 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  200. data/lib/active_support/log_subscriber.rb +61 -18
  201. data/lib/active_support/logger.rb +40 -4
  202. data/lib/active_support/logger_silence.rb +17 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +69 -0
  204. data/lib/active_support/message_encryptor.rb +178 -55
  205. data/lib/active_support/message_verifier.rb +195 -26
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +45 -92
  210. data/lib/active_support/multibyte/unicode.rb +44 -377
  211. data/lib/active_support/multibyte.rb +5 -3
  212. data/lib/active_support/notifications/fanout.rb +177 -44
  213. data/lib/active_support/notifications/instrumenter.rb +117 -17
  214. data/lib/active_support/notifications.rb +106 -39
  215. data/lib/active_support/number_helper/number_converter.rb +181 -0
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  223. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  224. data/lib/active_support/number_helper.rb +152 -394
  225. data/lib/active_support/option_merger.rb +18 -5
  226. data/lib/active_support/ordered_hash.rb +8 -6
  227. data/lib/active_support/ordered_options.rb +43 -7
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +24 -11
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +10 -11
  232. data/lib/active_support/railtie.rb +118 -12
  233. data/lib/active_support/reloader.rb +130 -0
  234. data/lib/active_support/rescuable.rb +112 -57
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +38 -0
  238. data/lib/active_support/string_inquirer.rb +11 -4
  239. data/lib/active_support/subscriber.rb +109 -39
  240. data/lib/active_support/tagged_logging.rb +54 -17
  241. data/lib/active_support/test_case.rb +121 -37
  242. data/lib/active_support/testing/assertions.rb +177 -39
  243. data/lib/active_support/testing/autorun.rb +5 -3
  244. data/lib/active_support/testing/constant_lookup.rb +3 -6
  245. data/lib/active_support/testing/declarative.rb +10 -22
  246. data/lib/active_support/testing/deprecation.rb +65 -11
  247. data/lib/active_support/testing/file_fixtures.rb +38 -0
  248. data/lib/active_support/testing/isolation.rb +56 -87
  249. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  255. data/lib/active_support/testing/stream.rb +41 -0
  256. data/lib/active_support/testing/tagged_logging.rb +6 -4
  257. data/lib/active_support/testing/time_helpers.rb +246 -0
  258. data/lib/active_support/time.rb +13 -13
  259. data/lib/active_support/time_with_zone.rb +315 -90
  260. data/lib/active_support/values/time_zone.rb +306 -135
  261. data/lib/active_support/version.rb +6 -7
  262. data/lib/active_support/xml_mini/jdom.rb +117 -115
  263. data/lib/active_support/xml_mini/libxml.rb +22 -21
  264. data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
  265. data/lib/active_support/xml_mini/nokogiri.rb +19 -19
  266. data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
  267. data/lib/active_support/xml_mini/rexml.rb +25 -17
  268. data/lib/active_support/xml_mini.rb +67 -56
  269. data/lib/active_support.rb +58 -3
  270. metadata +125 -66
  271. data/lib/active_support/basic_object.rb +0 -11
  272. data/lib/active_support/buffered_logger.rb +0 -21
  273. data/lib/active_support/concurrency/latch.rb +0 -27
  274. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  275. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  276. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  277. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  278. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  279. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  280. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  281. data/lib/active_support/core_ext/logger.rb +0 -67
  282. data/lib/active_support/core_ext/marshal.rb +0 -21
  283. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  284. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  285. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  286. data/lib/active_support/core_ext/proc.rb +0 -17
  287. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  288. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  289. data/lib/active_support/core_ext/struct.rb +0 -6
  290. data/lib/active_support/core_ext/thread.rb +0 -79
  291. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  292. data/lib/active_support/file_watcher.rb +0 -36
  293. data/lib/active_support/json/variable.rb +0 -18
  294. data/lib/active_support/testing/pending.rb +0 -14
  295. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,117 +1,24 @@
1
- require 'active_support/core_ext/big_decimal/conversions'
2
- require 'active_support/core_ext/object/blank'
3
- require 'active_support/core_ext/hash/keys'
4
- require 'active_support/i18n'
1
+ # frozen_string_literal: true
5
2
 
6
3
  module ActiveSupport
7
4
  module NumberHelper
8
- extend self
9
-
10
- DEFAULTS = {
11
- # Used in number_to_delimited
12
- # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
13
- format: {
14
- # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
15
- separator: ".",
16
- # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
17
- delimiter: ",",
18
- # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
19
- precision: 3,
20
- # If set to true, precision will mean the number of significant digits instead
21
- # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
22
- significant: false,
23
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
24
- strip_insignificant_zeros: false
25
- },
26
-
27
- # Used in number_to_currency
28
- currency: {
29
- format: {
30
- format: "%u%n",
31
- negative_format: "-%u%n",
32
- unit: "$",
33
- # These five are to override number.format and are optional
34
- separator: ".",
35
- delimiter: ",",
36
- precision: 2,
37
- significant: false,
38
- strip_insignificant_zeros: false
39
- }
40
- },
41
-
42
- # Used in number_to_percentage
43
- percentage: {
44
- format: {
45
- delimiter: "",
46
- format: "%n%"
47
- }
48
- },
49
-
50
- # Used in number_to_rounded
51
- precision: {
52
- format: {
53
- delimiter: ""
54
- }
55
- },
56
-
57
- # Used in number_to_human_size and number_to_human
58
- human: {
59
- format: {
60
- # These five are to override number.format and are optional
61
- delimiter: "",
62
- precision: 3,
63
- significant: true,
64
- strip_insignificant_zeros: true
65
- },
66
- # Used in number_to_human_size
67
- storage_units: {
68
- # Storage units output formatting.
69
- # %u is the storage unit, %n is the number (default: 2 MB)
70
- format: "%n %u",
71
- units: {
72
- byte: "Bytes",
73
- kb: "KB",
74
- mb: "MB",
75
- gb: "GB",
76
- tb: "TB"
77
- }
78
- },
79
- # Used in number_to_human
80
- decimal_units: {
81
- format: "%n %u",
82
- # Decimal units output formatting
83
- # By default we will only quantify some of the exponents
84
- # but the commented ones might be defined or overridden
85
- # by the user.
86
- units: {
87
- # femto: Quadrillionth
88
- # pico: Trillionth
89
- # nano: Billionth
90
- # micro: Millionth
91
- # mili: Thousandth
92
- # centi: Hundredth
93
- # deci: Tenth
94
- unit: "",
95
- # ten:
96
- # one: Ten
97
- # other: Tens
98
- # hundred: Hundred
99
- thousand: "Thousand",
100
- million: "Million",
101
- billion: "Billion",
102
- trillion: "Trillion",
103
- quadrillion: "Quadrillion"
104
- }
105
- }
106
- }
107
- }
108
-
109
- DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
110
- -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
5
+ extend ActiveSupport::Autoload
6
+
7
+ eager_autoload do
8
+ autoload :NumberConverter
9
+ autoload :RoundingHelper
10
+ autoload :NumberToRoundedConverter
11
+ autoload :NumberToDelimitedConverter
12
+ autoload :NumberToHumanConverter
13
+ autoload :NumberToHumanSizeConverter
14
+ autoload :NumberToPhoneConverter
15
+ autoload :NumberToCurrencyConverter
16
+ autoload :NumberToPercentageConverter
17
+ end
111
18
 
112
- STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
19
+ extend self
113
20
 
114
- # Formats a +number+ into a US phone number (e.g., (555)
21
+ # Formats a +number+ into a phone number (US by default e.g., (555)
115
22
  # 123-9876). You can customize the format in the +options+ hash.
116
23
  #
117
24
  # ==== Options
@@ -123,52 +30,49 @@ module ActiveSupport
123
30
  # end of the generated number.
124
31
  # * <tt>:country_code</tt> - Sets the country code for the phone
125
32
  # number.
33
+ # * <tt>:pattern</tt> - Specifies how the number is divided into three
34
+ # groups with the custom regexp to override the default format.
126
35
  # ==== Examples
127
36
  #
128
- # number_to_phone(5551234) # => 555-1234
129
- # number_to_phone('5551234') # => 555-1234
130
- # number_to_phone(1235551234) # => 123-555-1234
131
- # number_to_phone(1235551234, area_code: true) # => (123) 555-1234
132
- # number_to_phone(1235551234, delimiter: ' ') # => 123 555 1234
133
- # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
134
- # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
135
- # number_to_phone('123a456') # => 123a456
37
+ # number_to_phone(5551234) # => "555-1234"
38
+ # number_to_phone('5551234') # => "555-1234"
39
+ # number_to_phone(1235551234) # => "123-555-1234"
40
+ # number_to_phone(1235551234, area_code: true) # => "(123) 555-1234"
41
+ # number_to_phone(1235551234, delimiter: ' ') # => "123 555 1234"
42
+ # number_to_phone(1235551234, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
43
+ # number_to_phone(1235551234, country_code: 1) # => "+1-123-555-1234"
44
+ # number_to_phone('123a456') # => "123a456"
136
45
  #
137
46
  # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
138
- # # => +1.123.555.1234 x 1343
47
+ # # => "+1.123.555.1234 x 1343"
48
+ #
49
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
50
+ # # => "(755) 6123-4567"
51
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
52
+ # # => "133-1234-5678"
139
53
  def number_to_phone(number, options = {})
140
- return unless number
141
- options = options.symbolize_keys
142
-
143
- number = number.to_s.strip
144
- area_code = options[:area_code]
145
- delimiter = options[:delimiter] || "-"
146
- extension = options[:extension]
147
- country_code = options[:country_code]
148
-
149
- if area_code
150
- number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
151
- else
152
- number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
153
- number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
154
- end
155
-
156
- str = ''
157
- str << "+#{country_code}#{delimiter}" unless country_code.blank?
158
- str << number
159
- str << " x #{extension}" unless extension.blank?
160
- str
54
+ NumberToPhoneConverter.convert(number, options)
161
55
  end
162
56
 
163
57
  # Formats a +number+ into a currency string (e.g., $13.65). You
164
58
  # can customize the format in the +options+ hash.
165
59
  #
60
+ # The currency unit and number formatting of the current locale will be used
61
+ # unless otherwise specified in the provided options. No currency conversion
62
+ # is performed. If the user is given a way to change their locale, they will
63
+ # also be able to change the relative value of the currency displayed with
64
+ # this helper. If your application will ever support multiple locales, you
65
+ # may want to specify a constant <tt>:locale</tt> option or consider
66
+ # using a library capable of currency conversion.
67
+ #
166
68
  # ==== Options
167
69
  #
168
70
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
169
71
  # (defaults to current locale).
170
72
  # * <tt>:precision</tt> - Sets the level of precision (defaults
171
73
  # to 2).
74
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
75
+ # (defaults to :default. See BigDecimal::mode)
172
76
  # * <tt>:unit</tt> - Sets the denomination of the currency
173
77
  # (defaults to "$").
174
78
  # * <tt>:separator</tt> - Sets the separator between the units
@@ -179,45 +83,36 @@ module ActiveSupport
179
83
  # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
180
84
  # currency, and <tt>%n</tt> for the number.
181
85
  # * <tt>:negative_format</tt> - Sets the format for negative
182
- # numbers (defaults to prepending an hyphen to the formatted
86
+ # numbers (defaults to prepending a hyphen to the formatted
183
87
  # number given by <tt>:format</tt>). Accepts the same fields
184
88
  # than <tt>:format</tt>, except <tt>%n</tt> is here the
185
89
  # absolute value of the number.
90
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
91
+ # insignificant zeros after the decimal separator (defaults to
92
+ # +false+).
186
93
  #
187
94
  # ==== Examples
188
95
  #
189
- # number_to_currency(1234567890.50) # => $1,234,567,890.50
190
- # number_to_currency(1234567890.506) # => $1,234,567,890.51
191
- # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
192
- # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
193
- # number_to_currency('123a456') # => $123a456
96
+ # number_to_currency(1234567890.50) # => "$1,234,567,890.50"
97
+ # number_to_currency(1234567890.506) # => "$1,234,567,890.51"
98
+ # number_to_currency(1234567890.506, precision: 3) # => "$1,234,567,890.506"
99
+ # number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €"
100
+ # number_to_currency('123a456') # => "$123a456"
194
101
  #
102
+ # number_to_currency(-0.456789, precision: 0)
103
+ # # => "$0"
195
104
  # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
196
- # # => ($1,234,567,890.50)
105
+ # # => "($1,234,567,890.50)"
197
106
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '')
198
- # # => &pound;1234567890,50
107
+ # # => "&pound;1234567890,50"
199
108
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
200
- # # => 1234567890,50 &pound;
109
+ # # => "1234567890,50 &pound;"
110
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
111
+ # # => "$1,234,567,890.5"
112
+ # number_to_currency(1234567890.50, precision: 0, round_mode: :up)
113
+ # # => "$1,234,567,891"
201
114
  def number_to_currency(number, options = {})
202
- return unless number
203
- options = options.symbolize_keys
204
-
205
- currency = i18n_format_options(options[:locale], :currency)
206
- currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
207
-
208
- defaults = default_format_options(:currency).merge!(currency)
209
- defaults[:negative_format] = "-" + options[:format] if options[:format]
210
- options = defaults.merge!(options)
211
-
212
- unit = options.delete(:unit)
213
- format = options.delete(:format)
214
-
215
- if number.to_f.phase != 0
216
- format = options.delete(:negative_format)
217
- number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
218
- end
219
-
220
- format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
115
+ NumberToCurrencyConverter.convert(number, options)
221
116
  end
222
117
 
223
118
  # Formats a +number+ as a percentage string (e.g., 65%). You can
@@ -228,9 +123,11 @@ module ActiveSupport
228
123
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
229
124
  # (defaults to current locale).
230
125
  # * <tt>:precision</tt> - Sets the precision of the number
231
- # (defaults to 3).
232
- # * <tt>:significant</tt> - If +true+, precision will be the #
233
- # of significant_digits. If +false+, the # of fractional
126
+ # (defaults to 3). Keeps the number's precision if +nil+.
127
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
128
+ # (defaults to :default. See BigDecimal::mode)
129
+ # * <tt>:significant</tt> - If +true+, precision will be the number
130
+ # of significant_digits. If +false+, the number of fractional
234
131
  # digits (defaults to +false+).
235
132
  # * <tt>:separator</tt> - Sets the separator between the
236
133
  # fractional and integer digits (defaults to ".").
@@ -244,23 +141,18 @@ module ActiveSupport
244
141
  #
245
142
  # ==== Examples
246
143
  #
247
- # number_to_percentage(100) # => 100.000%
248
- # number_to_percentage('98') # => 98.000%
249
- # number_to_percentage(100, precision: 0) # => 100%
250
- # number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
251
- # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
252
- # number_to_percentage(1000, locale: :fr) # => 1 000,000%
253
- # number_to_percentage('98a') # => 98a%
254
- # number_to_percentage(100, format: '%n %') # => 100 %
144
+ # number_to_percentage(100) # => "100.000%"
145
+ # number_to_percentage('98') # => "98.000%"
146
+ # number_to_percentage(100, precision: 0) # => "100%"
147
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
148
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
149
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
150
+ # number_to_percentage(1000, precision: nil) # => "1000%"
151
+ # number_to_percentage('98a') # => "98a%"
152
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
153
+ # number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%"
255
154
  def number_to_percentage(number, options = {})
256
- return unless number
257
- options = options.symbolize_keys
258
-
259
- defaults = format_options(options[:locale], :percentage)
260
- options = defaults.merge!(options)
261
-
262
- format = options[:format] || "%n%"
263
- format.gsub('%n', self.number_to_rounded(number, options))
155
+ NumberToPercentageConverter.convert(number, options)
264
156
  end
265
157
 
266
158
  # Formats a +number+ with grouped thousands using +delimiter+
@@ -275,29 +167,27 @@ module ActiveSupport
275
167
  # to ",").
276
168
  # * <tt>:separator</tt> - Sets the separator between the
277
169
  # fractional and integer digits (defaults to ".").
170
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
171
+ # deriving the placement of delimiter. Helpful when using currency formats
172
+ # like INR.
278
173
  #
279
174
  # ==== Examples
280
175
  #
281
- # number_to_delimited(12345678) # => 12,345,678
282
- # number_to_delimited('123456') # => 123,456
283
- # number_to_delimited(12345678.05) # => 12,345,678.05
284
- # number_to_delimited(12345678, delimiter: '.') # => 12.345.678
285
- # number_to_delimited(12345678, delimiter: ',') # => 12,345,678
286
- # number_to_delimited(12345678.05, separator: ' ') # => 12,345,678 05
287
- # number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
288
- # number_to_delimited('112a') # => 112a
176
+ # number_to_delimited(12345678) # => "12,345,678"
177
+ # number_to_delimited('123456') # => "123,456"
178
+ # number_to_delimited(12345678.05) # => "12,345,678.05"
179
+ # number_to_delimited(12345678, delimiter: '.') # => "12.345.678"
180
+ # number_to_delimited(12345678, delimiter: ',') # => "12,345,678"
181
+ # number_to_delimited(12345678.05, separator: ' ') # => "12,345,678 05"
182
+ # number_to_delimited(12345678.05, locale: :fr) # => "12 345 678,05"
183
+ # number_to_delimited('112a') # => "112a"
289
184
  # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
290
- # # => 98 765 432,98
185
+ # # => "98 765 432,98"
186
+ # number_to_delimited("123456.78",
187
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
188
+ # # => "1,23,456.78"
291
189
  def number_to_delimited(number, options = {})
292
- options = options.symbolize_keys
293
-
294
- return number unless valid_float?(number)
295
-
296
- options = format_options(options[:locale]).merge!(options)
297
-
298
- parts = number.to_s.split('.')
299
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
300
- parts.join(options[:separator])
190
+ NumberToDelimitedConverter.convert(number, options)
301
191
  end
302
192
 
303
193
  # Formats a +number+ with the specified level of
@@ -310,9 +200,11 @@ module ActiveSupport
310
200
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
311
201
  # (defaults to current locale).
312
202
  # * <tt>:precision</tt> - Sets the precision of the number
313
- # (defaults to 3).
314
- # * <tt>:significant</tt> - If +true+, precision will be the #
315
- # of significant_digits. If +false+, the # of fractional
203
+ # (defaults to 3). Keeps the number's precision if +nil+.
204
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
205
+ # (defaults to :default. See BigDecimal::mode)
206
+ # * <tt>:significant</tt> - If +true+, precision will be the number
207
+ # of significant_digits. If +false+, the number of fractional
316
208
  # digits (defaults to +false+).
317
209
  # * <tt>:separator</tt> - Sets the separator between the
318
210
  # fractional and integer digits (defaults to ".").
@@ -324,59 +216,29 @@ module ActiveSupport
324
216
  #
325
217
  # ==== Examples
326
218
  #
327
- # number_to_rounded(111.2345) # => 111.235
328
- # number_to_rounded(111.2345, precision: 2) # => 111.23
329
- # number_to_rounded(13, precision: 5) # => 13.00000
330
- # number_to_rounded(389.32314, precision: 0) # => 389
331
- # number_to_rounded(111.2345, significant: true) # => 111
332
- # number_to_rounded(111.2345, precision: 1, significant: true) # => 100
333
- # number_to_rounded(13, precision: 5, significant: true) # => 13.000
334
- # number_to_rounded(111.234, locale: :fr) # => 111,234
219
+ # number_to_rounded(111.2345) # => "111.235"
220
+ # number_to_rounded(111.2345, precision: 2) # => "111.23"
221
+ # number_to_rounded(13, precision: 5) # => "13.00000"
222
+ # number_to_rounded(389.32314, precision: 0) # => "389"
223
+ # number_to_rounded(111.2345, significant: true) # => "111"
224
+ # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
225
+ # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
226
+ # number_to_rounded(13, precision: nil) # => "13"
227
+ # number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
228
+ # number_to_rounded(111.234, locale: :fr) # => "111,234"
335
229
  #
336
230
  # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
337
- # # => 13
231
+ # # => "13"
338
232
  #
339
- # number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
233
+ # number_to_rounded(389.32314, precision: 4, significant: true) # => "389.3"
340
234
  # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
341
- # # => 1.111,23
235
+ # # => "1.111,23"
342
236
  def number_to_rounded(number, options = {})
343
- return number unless valid_float?(number)
344
- number = Float(number)
345
- options = options.symbolize_keys
346
-
347
- defaults = format_options(options[:locale], :precision)
348
- options = defaults.merge!(options)
349
-
350
- precision = options.delete :precision
351
- significant = options.delete :significant
352
- strip_insignificant_zeros = options.delete :strip_insignificant_zeros
353
-
354
- if significant && precision > 0
355
- if number == 0
356
- digits, rounded_number = 1, 0
357
- else
358
- digits = (Math.log10(number.abs) + 1).floor
359
- multiplier = 10 ** (digits - precision)
360
- rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
361
- digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
362
- end
363
- precision -= digits
364
- precision = 0 if precision < 0 # don't let it be negative
365
- else
366
- rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
367
- rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
368
- end
369
- formatted_number = self.number_to_delimited("%01.#{precision}f" % rounded_number, options)
370
- if strip_insignificant_zeros
371
- escaped_separator = Regexp.escape(options[:separator])
372
- formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
373
- else
374
- formatted_number
375
- end
237
+ NumberToRoundedConverter.convert(number, options)
376
238
  end
377
239
 
378
240
  # Formats the bytes in +number+ into a more understandable
379
- # representation (e.g., giving it 1500 yields 1.5 KB). This
241
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
380
242
  # method is useful for reporting file sizes to users. You can
381
243
  # customize the format in the +options+ hash.
382
244
  #
@@ -389,8 +251,10 @@ module ActiveSupport
389
251
  # (defaults to current locale).
390
252
  # * <tt>:precision</tt> - Sets the precision of the number
391
253
  # (defaults to 3).
392
- # * <tt>:significant</tt> - If +true+, precision will be the #
393
- # of significant_digits. If +false+, the # of fractional
254
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
255
+ # (defaults to :default. See BigDecimal::mode)
256
+ # * <tt>:significant</tt> - If +true+, precision will be the number
257
+ # of significant_digits. If +false+, the number of fractional
394
258
  # digits (defaults to +true+)
395
259
  # * <tt>:separator</tt> - Sets the separator between the
396
260
  # fractional and integer digits (defaults to ".").
@@ -399,61 +263,29 @@ module ActiveSupport
399
263
  # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
400
264
  # insignificant zeros after the decimal separator (defaults to
401
265
  # +true+)
402
- # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
403
- # prefix (defaults to :binary)
404
266
  #
405
267
  # ==== Examples
406
268
  #
407
- # number_to_human_size(123) # => 123 Bytes
408
- # number_to_human_size(1234) # => 1.21 KB
409
- # number_to_human_size(12345) # => 12.1 KB
410
- # number_to_human_size(1234567) # => 1.18 MB
411
- # number_to_human_size(1234567890) # => 1.15 GB
412
- # number_to_human_size(1234567890123) # => 1.12 TB
413
- # number_to_human_size(1234567, precision: 2) # => 1.2 MB
414
- # number_to_human_size(483989, precision: 2) # => 470 KB
415
- # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
416
- #
417
- # Non-significant zeros after the fractional separator are stripped out by
418
- # default (set <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
419
- #
420
- # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
421
- # number_to_human_size(524288000, precision: 5) # => "500 MB"
269
+ # number_to_human_size(123) # => "123 Bytes"
270
+ # number_to_human_size(1234) # => "1.21 KB"
271
+ # number_to_human_size(12345) # => "12.1 KB"
272
+ # number_to_human_size(1234567) # => "1.18 MB"
273
+ # number_to_human_size(1234567890) # => "1.15 GB"
274
+ # number_to_human_size(1234567890123) # => "1.12 TB"
275
+ # number_to_human_size(1234567890123456) # => "1.1 PB"
276
+ # number_to_human_size(1234567890123456789) # => "1.07 EB"
277
+ # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
278
+ # number_to_human_size(483989, precision: 2) # => "470 KB"
279
+ # number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
280
+ # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
281
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
282
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
422
283
  def number_to_human_size(number, options = {})
423
- options = options.symbolize_keys
424
-
425
- return number unless valid_float?(number)
426
- number = Float(number)
427
-
428
- defaults = format_options(options[:locale], :human)
429
- options = defaults.merge!(options)
430
-
431
- #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
432
- options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
433
-
434
- storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
435
-
436
- base = options[:prefix] == :si ? 1000 : 1024
437
-
438
- if number.to_i < base
439
- unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
440
- storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
441
- else
442
- max_exp = STORAGE_UNITS.size - 1
443
- exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
444
- exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
445
- number /= base ** exponent
446
-
447
- unit_key = STORAGE_UNITS[exponent]
448
- unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
449
-
450
- formatted_number = self.number_to_rounded(number, options)
451
- storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
452
- end
284
+ NumberToHumanSizeConverter.convert(number, options)
453
285
  end
454
286
 
455
287
  # Pretty prints (formats and approximates) a number in a way it
456
- # is more readable by humans (eg.: 1200000000 becomes "1.2
288
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
457
289
  # Billion"). This is useful for numbers that can get very large
458
290
  # (and too hard to read).
459
291
  #
@@ -461,7 +293,7 @@ module ActiveSupport
461
293
  # size.
462
294
  #
463
295
  # You can also define your own unit-quantifier names if you want
464
- # to use other decimal units (eg.: 1500 becomes "1.5
296
+ # to use other decimal units (e.g.: 1500 becomes "1.5
465
297
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
466
298
  # define a wide range of unit quantifiers, even fractional ones
467
299
  # (centi, deci, mili, etc).
@@ -472,8 +304,10 @@ module ActiveSupport
472
304
  # (defaults to current locale).
473
305
  # * <tt>:precision</tt> - Sets the precision of the number
474
306
  # (defaults to 3).
475
- # * <tt>:significant</tt> - If +true+, precision will be the #
476
- # of significant_digits. If +false+, the # of fractional
307
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
308
+ # (defaults to :default. See BigDecimal::mode)
309
+ # * <tt>:significant</tt> - If +true+, precision will be the number
310
+ # of significant_digits. If +false+, the number of fractional
477
311
  # digits (defaults to +true+)
478
312
  # * <tt>:separator</tt> - Sets the separator between the
479
313
  # fractional and integer digits (defaults to ".").
@@ -486,12 +320,12 @@ module ActiveSupport
486
320
  # string containing an i18n scope where to find this hash. It
487
321
  # might have the following keys:
488
322
  # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
489
- # *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
490
- # *<tt>:billion</tt>, <tt>:trillion</tt>,
491
- # *<tt>:quadrillion</tt>
323
+ # <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
324
+ # <tt>:billion</tt>, <tt>:trillion</tt>,
325
+ # <tt>:quadrillion</tt>
492
326
  # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
493
- # *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
494
- # *<tt>:pico</tt>, <tt>:femto</tt>
327
+ # <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
328
+ # <tt>:pico</tt>, <tt>:femto</tt>
495
329
  # * <tt>:format</tt> - Sets the format of the output string
496
330
  # (defaults to "%n %u"). The field types are:
497
331
  # * %u - The quantifier (ex.: 'thousand')
@@ -509,18 +343,23 @@ module ActiveSupport
509
343
  # number_to_human(1234567890123456789) # => "1230 Quadrillion"
510
344
  # number_to_human(489939, precision: 2) # => "490 Thousand"
511
345
  # number_to_human(489939, precision: 4) # => "489.9 Thousand"
346
+ # number_to_human(489939, precision: 2
347
+ # , round_mode: :down) # => "480 Thousand"
512
348
  # number_to_human(1234567, precision: 4,
513
349
  # significant: false) # => "1.2346 Million"
514
350
  # number_to_human(1234567, precision: 1,
515
351
  # separator: ',',
516
352
  # significant: false) # => "1,2 Million"
517
353
  #
354
+ # number_to_human(500000000, precision: 5) # => "500 Million"
355
+ # number_to_human(12345012345, significant: false) # => "12.345 Billion"
356
+ #
518
357
  # Non-significant zeros after the decimal separator are stripped
519
358
  # out by default (set <tt>:strip_insignificant_zeros</tt> to
520
359
  # +false+ to change that):
521
360
  #
522
- # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
523
- # number_to_human(500000000, precision: 5) # => "500 Million"
361
+ # number_to_human(12.00001) # => "12"
362
+ # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
524
363
  #
525
364
  # ==== Custom Unit Quantifiers
526
365
  #
@@ -550,88 +389,7 @@ module ActiveSupport
550
389
  # number_to_human(1, units: :distance) # => "1 meter"
551
390
  # number_to_human(0.34, units: :distance) # => "34 centimeters"
552
391
  def number_to_human(number, options = {})
553
- options = options.symbolize_keys
554
-
555
- return number unless valid_float?(number)
556
- number = Float(number)
557
-
558
- defaults = format_options(options[:locale], :human)
559
- options = defaults.merge!(options)
560
-
561
- #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
562
- options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
563
-
564
- inverted_du = DECIMAL_UNITS.invert
565
-
566
- units = options.delete :units
567
- unit_exponents = case units
568
- when Hash
569
- units
570
- when String, Symbol
571
- I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
572
- when nil
573
- translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
574
- else
575
- raise ArgumentError, ":units must be a Hash or String translation scope."
576
- end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
577
-
578
- number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
579
- display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
580
- number /= 10 ** display_exponent
581
-
582
- unit = case units
583
- when Hash
584
- units[DECIMAL_UNITS[display_exponent]] || ''
585
- when String, Symbol
586
- I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
587
- else
588
- translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
589
- end
590
-
591
- decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
592
- formatted_number = self.number_to_rounded(number, options)
593
- decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
594
- end
595
-
596
- def self.private_module_and_instance_method(method_name) #:nodoc:
597
- private method_name
598
- private_class_method method_name
599
- end
600
- private_class_method :private_module_and_instance_method
601
-
602
- def format_options(locale, namespace = nil) #:nodoc:
603
- default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
604
- end
605
- private_module_and_instance_method :format_options
606
-
607
- def default_format_options(namespace = nil) #:nodoc:
608
- options = DEFAULTS[:format].dup
609
- options.merge!(DEFAULTS[namespace][:format]) if namespace
610
- options
611
- end
612
- private_module_and_instance_method :default_format_options
613
-
614
- def i18n_format_options(locale, namespace = nil) #:nodoc:
615
- options = I18n.translate(:'number.format', locale: locale, default: {}).dup
616
- if namespace
617
- options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
618
- end
619
- options
620
- end
621
- private_module_and_instance_method :i18n_format_options
622
-
623
- def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
624
- default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
625
-
626
- I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
627
- end
628
- private_module_and_instance_method :translate_number_value_with_default
629
-
630
- def valid_float?(number) #:nodoc:
631
- Float(number)
632
- rescue ArgumentError, TypeError
633
- false
392
+ NumberToHumanConverter.convert(number, options)
634
393
  end
635
- private_module_and_instance_method :valid_float?
636
394
  end
637
395
  end