activesupport 4.0.13 → 5.2.5

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 (264) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +412 -444
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +8 -4
  5. data/lib/active_support/all.rb +5 -3
  6. data/lib/active_support/array_inquirer.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +14 -12
  8. data/lib/active_support/benchmarkable.rb +6 -14
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +67 -51
  11. data/lib/active_support/cache/mem_cache_store.rb +95 -97
  12. data/lib/active_support/cache/memory_store.rb +28 -30
  13. data/lib/active_support/cache/null_store.rb +7 -8
  14. data/lib/active_support/cache/redis_cache_store.rb +466 -0
  15. data/lib/active_support/cache/strategy/local_cache.rb +70 -56
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  17. data/lib/active_support/cache.rb +331 -206
  18. data/lib/active_support/callbacks.rb +697 -426
  19. data/lib/active_support/concern.rb +32 -10
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  21. data/lib/active_support/concurrency/share_lock.rb +227 -0
  22. data/lib/active_support/configurable.rb +8 -5
  23. data/lib/active_support/core_ext/array/access.rb +39 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +24 -35
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +23 -13
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -5
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -7
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -23
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  37. data/lib/active_support/core_ext/class.rb +4 -4
  38. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  39. data/lib/active_support/core_ext/date/blank.rb +14 -0
  40. data/lib/active_support/core_ext/date/calculations.rb +21 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +32 -22
  42. data/lib/active_support/core_ext/date/zones.rb +5 -34
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +199 -57
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  47. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  48. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  49. data/lib/active_support/core_ext/date_time/calculations.rb +78 -37
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  51. data/lib/active_support/core_ext/date_time/conversions.rb +19 -13
  52. data/lib/active_support/core_ext/date_time.rb +7 -4
  53. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +113 -29
  56. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/hash/compact.rb +29 -0
  59. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +12 -3
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +50 -38
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +12 -6
  66. data/lib/active_support/core_ext/hash/transform_values.rb +32 -0
  67. data/lib/active_support/core_ext/hash.rb +11 -8
  68. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  69. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  70. data/lib/active_support/core_ext/integer/time.rb +11 -33
  71. data/lib/active_support/core_ext/integer.rb +5 -3
  72. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +5 -74
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +6 -4
  77. data/lib/active_support/core_ext/load_error.rb +5 -21
  78. data/lib/active_support/core_ext/marshal.rb +13 -10
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +170 -21
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  85. data/lib/active_support/core_ext/module/delegation.rb +135 -45
  86. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -25
  88. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  89. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  90. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  91. data/lib/active_support/core_ext/module.rb +14 -10
  92. data/lib/active_support/core_ext/name_error.rb +22 -2
  93. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  94. data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  97. data/lib/active_support/core_ext/numeric.rb +6 -3
  98. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  99. data/lib/active_support/core_ext/object/blank.rb +70 -19
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  102. data/lib/active_support/core_ext/object/duplicable.rb +100 -34
  103. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +227 -0
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  108. data/lib/active_support/core_ext/object/try.rb +94 -24
  109. data/lib/active_support/core_ext/object/with_options.rb +45 -5
  110. data/lib/active_support/core_ext/object.rb +14 -12
  111. data/lib/active_support/core_ext/range/compare_range.rb +61 -0
  112. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  113. data/lib/active_support/core_ext/range/each.rb +19 -17
  114. data/lib/active_support/core_ext/range/include_range.rb +2 -22
  115. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  116. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  117. data/lib/active_support/core_ext/range.rb +7 -4
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string/access.rb +41 -39
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +17 -13
  123. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  124. data/lib/active_support/core_ext/string/filters.rb +55 -6
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +66 -24
  127. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  128. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  129. data/lib/active_support/core_ext/string/output_safety.rb +114 -54
  130. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  131. data/lib/active_support/core_ext/string/strip.rb +4 -5
  132. data/lib/active_support/core_ext/string/zones.rb +4 -1
  133. data/lib/active_support/core_ext/string.rb +15 -13
  134. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  135. data/lib/active_support/core_ext/time/calculations.rb +123 -110
  136. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  137. data/lib/active_support/core_ext/time/conversions.rb +23 -14
  138. data/lib/active_support/core_ext/time/zones.rb +42 -26
  139. data/lib/active_support/core_ext/time.rb +7 -5
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -2
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies/autoload.rb +3 -1
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies.rb +196 -166
  146. data/lib/active_support/deprecation/behaviors.rb +48 -15
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +66 -20
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/deprecation.rb +14 -11
  153. data/lib/active_support/descendants_tracker.rb +2 -0
  154. data/lib/active_support/digest.rb +20 -0
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/duration.rb +354 -34
  158. data/lib/active_support/encrypted_configuration.rb +49 -0
  159. data/lib/active_support/encrypted_file.rb +99 -0
  160. data/lib/active_support/evented_file_update_checker.rb +205 -0
  161. data/lib/active_support/execution_wrapper.rb +128 -0
  162. data/lib/active_support/executor.rb +8 -0
  163. data/lib/active_support/file_update_checker.rb +63 -37
  164. data/lib/active_support/gem_version.rb +17 -0
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +158 -35
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +38 -20
  169. data/lib/active_support/inflections.rb +19 -12
  170. data/lib/active_support/inflector/inflections.rb +79 -30
  171. data/lib/active_support/inflector/methods.rb +197 -129
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/inflector.rb +7 -5
  174. data/lib/active_support/json/decoding.rb +21 -25
  175. data/lib/active_support/json/encoding.rb +84 -292
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +26 -28
  178. data/lib/active_support/lazy_load_hooks.rb +51 -21
  179. data/lib/active_support/locale/en.yml +2 -0
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/log_subscriber.rb +13 -10
  182. data/lib/active_support/logger.rb +54 -3
  183. data/lib/active_support/logger_silence.rb +12 -7
  184. data/lib/active_support/logger_thread_safe_level.rb +34 -0
  185. data/lib/active_support/message_encryptor.rb +173 -50
  186. data/lib/active_support/message_verifier.rb +159 -22
  187. data/lib/active_support/messages/metadata.rb +71 -0
  188. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  189. data/lib/active_support/messages/rotator.rb +56 -0
  190. data/lib/active_support/multibyte/chars.rb +38 -26
  191. data/lib/active_support/multibyte/unicode.rb +138 -146
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +23 -16
  194. data/lib/active_support/notifications/instrumenter.rb +29 -8
  195. data/lib/active_support/notifications.rb +22 -13
  196. data/lib/active_support/number_helper/number_converter.rb +184 -0
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +29 -0
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +59 -0
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +14 -0
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +54 -0
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +125 -391
  206. data/lib/active_support/option_merger.rb +3 -1
  207. data/lib/active_support/ordered_hash.rb +6 -4
  208. data/lib/active_support/ordered_options.rb +31 -5
  209. data/lib/active_support/per_thread_registry.rb +19 -11
  210. data/lib/active_support/proxy_object.rb +2 -0
  211. data/lib/active_support/rails.rb +16 -8
  212. data/lib/active_support/railtie.rb +43 -9
  213. data/lib/active_support/reloader.rb +131 -0
  214. data/lib/active_support/rescuable.rb +108 -53
  215. data/lib/active_support/security_utils.rb +31 -0
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +54 -17
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +42 -37
  220. data/lib/active_support/testing/assertions.rb +126 -39
  221. data/lib/active_support/testing/autorun.rb +5 -3
  222. data/lib/active_support/testing/constant_lookup.rb +3 -6
  223. data/lib/active_support/testing/declarative.rb +10 -22
  224. data/lib/active_support/testing/deprecation.rb +14 -10
  225. data/lib/active_support/testing/file_fixtures.rb +36 -0
  226. data/lib/active_support/testing/isolation.rb +55 -86
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +5 -3
  231. data/lib/active_support/testing/time_helpers.rb +200 -0
  232. data/lib/active_support/time.rb +13 -13
  233. data/lib/active_support/time_with_zone.rb +223 -73
  234. data/lib/active_support/values/time_zone.rb +261 -126
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +6 -7
  237. data/lib/active_support/xml_mini/jdom.rb +116 -113
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +17 -16
  243. data/lib/active_support/xml_mini.rb +69 -51
  244. data/lib/active_support.rb +29 -3
  245. metadata +84 -54
  246. data/lib/active_support/basic_object.rb +0 -11
  247. data/lib/active_support/buffered_logger.rb +0 -21
  248. data/lib/active_support/concurrency/latch.rb +0 -27
  249. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  250. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  251. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  252. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  253. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  254. data/lib/active_support/core_ext/logger.rb +0 -67
  255. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  256. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  257. data/lib/active_support/core_ext/proc.rb +0 -17
  258. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  259. data/lib/active_support/core_ext/struct.rb +0 -6
  260. data/lib/active_support/core_ext/thread.rb +0 -79
  261. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  262. data/lib/active_support/file_watcher.rb +0 -36
  263. data/lib/active_support/json/variable.rb +0 -18
  264. data/lib/active_support/testing/pending.rb +0 -14
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/big_decimal/conversions"
4
+ require "active_support/core_ext/object/blank"
5
+ require "active_support/core_ext/hash/keys"
6
+ require "active_support/i18n"
7
+ require "active_support/core_ext/class/attribute"
8
+
9
+ module ActiveSupport
10
+ module NumberHelper
11
+ class NumberConverter # :nodoc:
12
+ # Default and i18n option namespace per class
13
+ class_attribute :namespace
14
+
15
+ # Does the object need a number that is a valid float?
16
+ class_attribute :validate_float
17
+
18
+ attr_reader :number, :opts
19
+
20
+ DEFAULTS = {
21
+ # Used in number_to_delimited
22
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
23
+ format: {
24
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
25
+ separator: ".",
26
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
27
+ delimiter: ",",
28
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
29
+ precision: 3,
30
+ # If set to true, precision will mean the number of significant digits instead
31
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
32
+ significant: false,
33
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
34
+ strip_insignificant_zeros: false
35
+ },
36
+
37
+ # Used in number_to_currency
38
+ currency: {
39
+ format: {
40
+ format: "%u%n",
41
+ negative_format: "-%u%n",
42
+ unit: "$",
43
+ # These five are to override number.format and are optional
44
+ separator: ".",
45
+ delimiter: ",",
46
+ precision: 2,
47
+ significant: false,
48
+ strip_insignificant_zeros: false
49
+ }
50
+ },
51
+
52
+ # Used in number_to_percentage
53
+ percentage: {
54
+ format: {
55
+ delimiter: "",
56
+ format: "%n%"
57
+ }
58
+ },
59
+
60
+ # Used in number_to_rounded
61
+ precision: {
62
+ format: {
63
+ delimiter: ""
64
+ }
65
+ },
66
+
67
+ # Used in number_to_human_size and number_to_human
68
+ human: {
69
+ format: {
70
+ # These five are to override number.format and are optional
71
+ delimiter: "",
72
+ precision: 3,
73
+ significant: true,
74
+ strip_insignificant_zeros: true
75
+ },
76
+ # Used in number_to_human_size
77
+ storage_units: {
78
+ # Storage units output formatting.
79
+ # %u is the storage unit, %n is the number (default: 2 MB)
80
+ format: "%n %u",
81
+ units: {
82
+ byte: "Bytes",
83
+ kb: "KB",
84
+ mb: "MB",
85
+ gb: "GB",
86
+ tb: "TB"
87
+ }
88
+ },
89
+ # Used in number_to_human
90
+ decimal_units: {
91
+ format: "%n %u",
92
+ # Decimal units output formatting
93
+ # By default we will only quantify some of the exponents
94
+ # but the commented ones might be defined or overridden
95
+ # by the user.
96
+ units: {
97
+ # femto: Quadrillionth
98
+ # pico: Trillionth
99
+ # nano: Billionth
100
+ # micro: Millionth
101
+ # mili: Thousandth
102
+ # centi: Hundredth
103
+ # deci: Tenth
104
+ unit: "",
105
+ # ten:
106
+ # one: Ten
107
+ # other: Tens
108
+ # hundred: Hundred
109
+ thousand: "Thousand",
110
+ million: "Million",
111
+ billion: "Billion",
112
+ trillion: "Trillion",
113
+ quadrillion: "Quadrillion"
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ def self.convert(number, options)
120
+ new(number, options).execute
121
+ end
122
+
123
+ def initialize(number, options)
124
+ @number = number
125
+ @opts = options.symbolize_keys
126
+ end
127
+
128
+ def execute
129
+ if !number
130
+ nil
131
+ elsif validate_float? && !valid_float?
132
+ number
133
+ else
134
+ convert
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def options
141
+ @options ||= format_options.merge(opts)
142
+ end
143
+
144
+ def format_options
145
+ default_format_options.merge!(i18n_format_options)
146
+ end
147
+
148
+ def default_format_options
149
+ options = DEFAULTS[:format].dup
150
+ options.merge!(DEFAULTS[namespace][:format]) if namespace
151
+ options
152
+ end
153
+
154
+ def i18n_format_options
155
+ locale = opts[:locale]
156
+ options = I18n.translate(:'number.format', locale: locale, default: {}).dup
157
+
158
+ if namespace
159
+ options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
160
+ end
161
+
162
+ options
163
+ end
164
+
165
+ def translate_number_value_with_default(key, i18n_options = {})
166
+ I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
167
+ end
168
+
169
+ def translate_in_locale(key, i18n_options = {})
170
+ translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
171
+ end
172
+
173
+ def default_value(key)
174
+ key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
175
+ end
176
+
177
+ def valid_float?
178
+ Float(number)
179
+ rescue ArgumentError, TypeError
180
+ false
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/numeric/inquiry"
4
+
5
+ module ActiveSupport
6
+ module NumberHelper
7
+ class NumberToCurrencyConverter < NumberConverter # :nodoc:
8
+ self.namespace = :currency
9
+
10
+ def convert
11
+ number = self.number.to_s.strip
12
+ format = options[:format]
13
+
14
+ if number.to_f.negative?
15
+ format = options[:negative_format]
16
+ number = absolute_value(number)
17
+ end
18
+
19
+ rounded_number = NumberToRoundedConverter.convert(number, options)
20
+ format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
21
+ end
22
+
23
+ private
24
+
25
+ def absolute_value(number)
26
+ number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "")
27
+ end
28
+
29
+ def options
30
+ @options ||= begin
31
+ defaults = default_format_options.merge(i18n_opts)
32
+ # Override negative format if format options are given
33
+ defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
34
+ defaults.merge!(opts)
35
+ end
36
+ end
37
+
38
+ def i18n_opts
39
+ # Set International negative format if it does not exist
40
+ i18n = i18n_format_options
41
+ i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
42
+ i18n
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class NumberToDelimitedConverter < NumberConverter #:nodoc:
6
+ self.validate_float = true
7
+
8
+ DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
9
+
10
+ def convert
11
+ parts.join(options[:separator])
12
+ end
13
+
14
+ private
15
+
16
+ def parts
17
+ left, right = number.to_s.split(".".freeze)
18
+ left.gsub!(delimiter_pattern) do |digit_to_delimit|
19
+ "#{digit_to_delimit}#{options[:delimiter]}"
20
+ end
21
+ [left, right].compact
22
+ end
23
+
24
+ def delimiter_pattern
25
+ options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class NumberToHumanConverter < NumberConverter # :nodoc:
6
+ DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
7
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
8
+ INVERTED_DECIMAL_UNITS = DECIMAL_UNITS.invert
9
+
10
+ self.namespace = :human
11
+ self.validate_float = true
12
+
13
+ def convert # :nodoc:
14
+ @number = RoundingHelper.new(options).round(number)
15
+ @number = Float(number)
16
+
17
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
18
+ unless options.key?(:strip_insignificant_zeros)
19
+ options[:strip_insignificant_zeros] = true
20
+ end
21
+
22
+ units = opts[:units]
23
+ exponent = calculate_exponent(units)
24
+ @number = number / (10**exponent)
25
+
26
+ rounded_number = NumberToRoundedConverter.convert(number, options)
27
+ unit = determine_unit(units, exponent)
28
+ format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, unit).strip
29
+ end
30
+
31
+ private
32
+
33
+ def format
34
+ options[:format] || translate_in_locale("human.decimal_units.format")
35
+ end
36
+
37
+ def determine_unit(units, exponent)
38
+ exp = DECIMAL_UNITS[exponent]
39
+ case units
40
+ when Hash
41
+ units[exp] || ""
42
+ when String, Symbol
43
+ I18n.translate("#{units}.#{exp}", locale: options[:locale], count: number.to_i)
44
+ else
45
+ translate_in_locale("human.decimal_units.units.#{exp}", count: number.to_i)
46
+ end
47
+ end
48
+
49
+ def calculate_exponent(units)
50
+ exponent = number != 0 ? Math.log10(number.abs).floor : 0
51
+ unit_exponents(units).find { |e| exponent >= e } || 0
52
+ end
53
+
54
+ def unit_exponents(units)
55
+ case units
56
+ when Hash
57
+ units
58
+ when String, Symbol
59
+ I18n.translate(units.to_s, locale: options[:locale], raise: true)
60
+ when nil
61
+ translate_in_locale("human.decimal_units.units", raise: true)
62
+ else
63
+ raise ArgumentError, ":units must be a Hash or String translation scope."
64
+ end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by(&:-@)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class NumberToHumanSizeConverter < NumberConverter #:nodoc:
6
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb]
7
+
8
+ self.namespace = :human
9
+ self.validate_float = true
10
+
11
+ def convert
12
+ @number = Float(number)
13
+
14
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
15
+ unless options.key?(:strip_insignificant_zeros)
16
+ options[:strip_insignificant_zeros] = true
17
+ end
18
+
19
+ if smaller_than_base?
20
+ number_to_format = number.to_i.to_s
21
+ else
22
+ human_size = number / (base**exponent)
23
+ number_to_format = NumberToRoundedConverter.convert(human_size, options)
24
+ end
25
+ conversion_format.gsub("%n".freeze, number_to_format).gsub("%u".freeze, unit)
26
+ end
27
+
28
+ private
29
+
30
+ def conversion_format
31
+ translate_number_value_with_default("human.storage_units.format", locale: options[:locale], raise: true)
32
+ end
33
+
34
+ def unit
35
+ translate_number_value_with_default(storage_unit_key, locale: options[:locale], count: number.to_i, raise: true)
36
+ end
37
+
38
+ def storage_unit_key
39
+ key_end = smaller_than_base? ? "byte" : STORAGE_UNITS[exponent]
40
+ "human.storage_units.units.#{key_end}"
41
+ end
42
+
43
+ def exponent
44
+ max = STORAGE_UNITS.size - 1
45
+ exp = (Math.log(number) / Math.log(base)).to_i
46
+ exp = max if exp > max # avoid overflow for the highest unit
47
+ exp
48
+ end
49
+
50
+ def smaller_than_base?
51
+ number.to_i < base
52
+ end
53
+
54
+ def base
55
+ 1024
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class NumberToPercentageConverter < NumberConverter # :nodoc:
6
+ self.namespace = :percentage
7
+
8
+ def convert
9
+ rounded_number = NumberToRoundedConverter.convert(number, options)
10
+ options[:format].gsub("%n".freeze, rounded_number)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class NumberToPhoneConverter < NumberConverter #:nodoc:
6
+ def convert
7
+ str = country_code(opts[:country_code]).dup
8
+ str << convert_to_phone_number(number.to_s.strip)
9
+ str << phone_ext(opts[:extension])
10
+ end
11
+
12
+ private
13
+
14
+ def convert_to_phone_number(number)
15
+ if opts[:area_code]
16
+ convert_with_area_code(number)
17
+ else
18
+ convert_without_area_code(number)
19
+ end
20
+ end
21
+
22
+ def convert_with_area_code(number)
23
+ default_pattern = /(\d{1,3})(\d{3})(\d{4}$)/
24
+ number.gsub!(regexp_pattern(default_pattern),
25
+ "(\\1) \\2#{delimiter}\\3")
26
+ number
27
+ end
28
+
29
+ def convert_without_area_code(number)
30
+ default_pattern = /(\d{0,3})(\d{3})(\d{4})$/
31
+ number.gsub!(regexp_pattern(default_pattern),
32
+ "\\1#{delimiter}\\2#{delimiter}\\3")
33
+ number.slice!(0, 1) if start_with_delimiter?(number)
34
+ number
35
+ end
36
+
37
+ def start_with_delimiter?(number)
38
+ delimiter.present? && number.start_with?(delimiter)
39
+ end
40
+
41
+ def delimiter
42
+ opts[:delimiter] || "-"
43
+ end
44
+
45
+ def country_code(code)
46
+ code.blank? ? "" : "+#{code}#{delimiter}"
47
+ end
48
+
49
+ def phone_ext(ext)
50
+ ext.blank? ? "" : " x #{ext}"
51
+ end
52
+
53
+ def regexp_pattern(default_pattern)
54
+ opts.fetch :pattern, default_pattern
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class NumberToRoundedConverter < NumberConverter # :nodoc:
6
+ self.namespace = :precision
7
+ self.validate_float = true
8
+
9
+ def convert
10
+ helper = RoundingHelper.new(options)
11
+ rounded_number = helper.round(number)
12
+
13
+ if precision = options[:precision]
14
+ if options[:significant] && precision > 0
15
+ digits = helper.digit_count(rounded_number)
16
+ precision -= digits
17
+ precision = 0 if precision < 0 # don't let it be negative
18
+ end
19
+
20
+ formatted_string =
21
+ if BigDecimal === rounded_number && rounded_number.finite?
22
+ s = rounded_number.to_s("F")
23
+ s << "0".freeze * precision
24
+ a, b = s.split(".".freeze, 2)
25
+ a << ".".freeze
26
+ a << b[0, precision]
27
+ else
28
+ "%00.#{precision}f" % rounded_number
29
+ end
30
+ else
31
+ formatted_string = rounded_number
32
+ end
33
+
34
+ delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
35
+ format_number(delimited_number)
36
+ end
37
+
38
+ private
39
+
40
+ def strip_insignificant_zeros
41
+ options[:strip_insignificant_zeros]
42
+ end
43
+
44
+ def format_number(number)
45
+ if strip_insignificant_zeros
46
+ escaped_separator = Regexp.escape(options[:separator])
47
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, "")
48
+ else
49
+ number
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class RoundingHelper # :nodoc:
6
+ attr_reader :options
7
+
8
+ def initialize(options)
9
+ @options = options
10
+ end
11
+
12
+ def round(number)
13
+ return number unless precision
14
+ number = convert_to_decimal(number)
15
+ if significant && precision > 0
16
+ round_significant(number)
17
+ else
18
+ round_without_significant(number)
19
+ end
20
+ end
21
+
22
+ def digit_count(number)
23
+ return 1 if number.zero?
24
+ (Math.log10(absolute_number(number)) + 1).floor
25
+ end
26
+
27
+ private
28
+ def round_without_significant(number)
29
+ number = number.round(precision)
30
+ number = number.to_i if precision == 0 && number.finite?
31
+ number = number.abs if number.zero? # prevent showing negative zeros
32
+ number
33
+ end
34
+
35
+ def round_significant(number)
36
+ return 0 if number.zero?
37
+ digits = digit_count(number)
38
+ multiplier = 10**(digits - precision)
39
+ (number / BigDecimal(multiplier.to_f.to_s)).round * multiplier
40
+ end
41
+
42
+ def convert_to_decimal(number)
43
+ case number
44
+ when Float, String
45
+ BigDecimal(number.to_s)
46
+ when Rational
47
+ BigDecimal(number, digit_count(number.to_i) + precision)
48
+ else
49
+ number.to_d
50
+ end
51
+ end
52
+
53
+ def precision
54
+ options[:precision]
55
+ end
56
+
57
+ def significant
58
+ options[:significant]
59
+ end
60
+
61
+ def absolute_number(number)
62
+ number.respond_to?(:abs) ? number.abs : number.to_d.abs
63
+ end
64
+ end
65
+ end
66
+ end