omg-activesupport 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (289) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +86 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support/actionable_error.rb +50 -0
  6. data/lib/active_support/all.rb +5 -0
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +163 -0
  9. data/lib/active_support/benchmark.rb +21 -0
  10. data/lib/active_support/benchmarkable.rb +53 -0
  11. data/lib/active_support/broadcast_logger.rb +251 -0
  12. data/lib/active_support/builder.rb +8 -0
  13. data/lib/active_support/cache/coder.rb +153 -0
  14. data/lib/active_support/cache/entry.rb +134 -0
  15. data/lib/active_support/cache/file_store.rb +244 -0
  16. data/lib/active_support/cache/mem_cache_store.rb +290 -0
  17. data/lib/active_support/cache/memory_store.rb +262 -0
  18. data/lib/active_support/cache/null_store.rb +62 -0
  19. data/lib/active_support/cache/redis_cache_store.rb +492 -0
  20. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  21. data/lib/active_support/cache/strategy/local_cache.rb +201 -0
  22. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  23. data/lib/active_support/cache.rb +1104 -0
  24. data/lib/active_support/callbacks.rb +944 -0
  25. data/lib/active_support/class_attribute.rb +26 -0
  26. data/lib/active_support/code_generator.rb +79 -0
  27. data/lib/active_support/concern.rb +217 -0
  28. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +72 -0
  29. data/lib/active_support/concurrency/null_lock.rb +13 -0
  30. data/lib/active_support/concurrency/share_lock.rb +225 -0
  31. data/lib/active_support/configurable.rb +159 -0
  32. data/lib/active_support/configuration_file.rb +60 -0
  33. data/lib/active_support/core_ext/array/access.rb +100 -0
  34. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  35. data/lib/active_support/core_ext/array/extract.rb +21 -0
  36. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  37. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  38. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  39. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  40. data/lib/active_support/core_ext/array.rb +9 -0
  41. data/lib/active_support/core_ext/benchmark.rb +13 -0
  42. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  43. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  44. data/lib/active_support/core_ext/class/attribute.rb +122 -0
  45. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  46. data/lib/active_support/core_ext/class/subclasses.rb +24 -0
  47. data/lib/active_support/core_ext/class.rb +4 -0
  48. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  49. data/lib/active_support/core_ext/date/blank.rb +18 -0
  50. data/lib/active_support/core_ext/date/calculations.rb +161 -0
  51. data/lib/active_support/core_ext/date/conversions.rb +98 -0
  52. data/lib/active_support/core_ext/date/zones.rb +8 -0
  53. data/lib/active_support/core_ext/date.rb +7 -0
  54. data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
  55. data/lib/active_support/core_ext/date_and_time/compatibility.rb +58 -0
  56. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  57. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  58. data/lib/active_support/core_ext/date_time/blank.rb +18 -0
  59. data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
  60. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  61. data/lib/active_support/core_ext/date_time/conversions.rb +106 -0
  62. data/lib/active_support/core_ext/date_time.rb +7 -0
  63. data/lib/active_support/core_ext/digest/uuid.rb +76 -0
  64. data/lib/active_support/core_ext/digest.rb +3 -0
  65. data/lib/active_support/core_ext/enumerable.rb +267 -0
  66. data/lib/active_support/core_ext/erb/util.rb +201 -0
  67. data/lib/active_support/core_ext/file/atomic.rb +72 -0
  68. data/lib/active_support/core_ext/file.rb +3 -0
  69. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  70. data/lib/active_support/core_ext/hash/deep_merge.rb +42 -0
  71. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  72. data/lib/active_support/core_ext/hash/except.rb +12 -0
  73. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  74. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  75. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  76. data/lib/active_support/core_ext/hash/slice.rb +27 -0
  77. data/lib/active_support/core_ext/hash.rb +10 -0
  78. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  79. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  80. data/lib/active_support/core_ext/integer/time.rb +22 -0
  81. data/lib/active_support/core_ext/integer.rb +5 -0
  82. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  83. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  84. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  85. data/lib/active_support/core_ext/kernel.rb +5 -0
  86. data/lib/active_support/core_ext/load_error.rb +9 -0
  87. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  88. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  89. data/lib/active_support/core_ext/module/attr_internal.rb +49 -0
  90. data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
  91. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
  92. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  93. data/lib/active_support/core_ext/module/delegation.rb +225 -0
  94. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  95. data/lib/active_support/core_ext/module/introspection.rb +62 -0
  96. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  97. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  98. data/lib/active_support/core_ext/module.rb +13 -0
  99. data/lib/active_support/core_ext/name_error.rb +59 -0
  100. data/lib/active_support/core_ext/numeric/bytes.rb +75 -0
  101. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  102. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  103. data/lib/active_support/core_ext/numeric.rb +5 -0
  104. data/lib/active_support/core_ext/object/acts_like.rb +45 -0
  105. data/lib/active_support/core_ext/object/blank.rb +199 -0
  106. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  107. data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
  108. data/lib/active_support/core_ext/object/duplicable.rb +69 -0
  109. data/lib/active_support/core_ext/object/inclusion.rb +37 -0
  110. data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
  111. data/lib/active_support/core_ext/object/json.rb +260 -0
  112. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  113. data/lib/active_support/core_ext/object/to_query.rb +87 -0
  114. data/lib/active_support/core_ext/object/try.rb +158 -0
  115. data/lib/active_support/core_ext/object/with.rb +46 -0
  116. data/lib/active_support/core_ext/object/with_options.rb +101 -0
  117. data/lib/active_support/core_ext/object.rb +17 -0
  118. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  119. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  120. data/lib/active_support/core_ext/pathname.rb +4 -0
  121. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  122. data/lib/active_support/core_ext/range/conversions.rb +62 -0
  123. data/lib/active_support/core_ext/range/each.rb +24 -0
  124. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  125. data/lib/active_support/core_ext/range.rb +6 -0
  126. data/lib/active_support/core_ext/regexp.rb +14 -0
  127. data/lib/active_support/core_ext/securerandom.rb +41 -0
  128. data/lib/active_support/core_ext/string/access.rb +95 -0
  129. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  130. data/lib/active_support/core_ext/string/conversions.rb +60 -0
  131. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  132. data/lib/active_support/core_ext/string/filters.rb +151 -0
  133. data/lib/active_support/core_ext/string/indent.rb +45 -0
  134. data/lib/active_support/core_ext/string/inflections.rb +300 -0
  135. data/lib/active_support/core_ext/string/inquiry.rb +16 -0
  136. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  137. data/lib/active_support/core_ext/string/output_safety.rb +228 -0
  138. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/string/strip.rb +27 -0
  140. data/lib/active_support/core_ext/string/zones.rb +16 -0
  141. data/lib/active_support/core_ext/string.rb +15 -0
  142. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  143. data/lib/active_support/core_ext/symbol.rb +3 -0
  144. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  145. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  146. data/lib/active_support/core_ext/time/calculations.rb +386 -0
  147. data/lib/active_support/core_ext/time/compatibility.rb +32 -0
  148. data/lib/active_support/core_ext/time/conversions.rb +75 -0
  149. data/lib/active_support/core_ext/time/zones.rb +97 -0
  150. data/lib/active_support/core_ext/time.rb +7 -0
  151. data/lib/active_support/core_ext.rb +5 -0
  152. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  153. data/lib/active_support/current_attributes.rb +233 -0
  154. data/lib/active_support/deep_mergeable.rb +53 -0
  155. data/lib/active_support/delegation.rb +202 -0
  156. data/lib/active_support/dependencies/autoload.rb +72 -0
  157. data/lib/active_support/dependencies/interlock.rb +49 -0
  158. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  159. data/lib/active_support/dependencies.rb +98 -0
  160. data/lib/active_support/deprecation/behaviors.rb +148 -0
  161. data/lib/active_support/deprecation/constant_accessor.rb +74 -0
  162. data/lib/active_support/deprecation/deprecators.rb +104 -0
  163. data/lib/active_support/deprecation/disallowed.rb +54 -0
  164. data/lib/active_support/deprecation/method_wrappers.rb +68 -0
  165. data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
  166. data/lib/active_support/deprecation/reporting.rb +179 -0
  167. data/lib/active_support/deprecation.rb +81 -0
  168. data/lib/active_support/deprecator.rb +7 -0
  169. data/lib/active_support/descendants_tracker.rb +112 -0
  170. data/lib/active_support/digest.rb +22 -0
  171. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  172. data/lib/active_support/duration/iso8601_serializer.rb +64 -0
  173. data/lib/active_support/duration.rb +520 -0
  174. data/lib/active_support/encrypted_configuration.rb +126 -0
  175. data/lib/active_support/encrypted_file.rb +133 -0
  176. data/lib/active_support/environment_inquirer.rb +40 -0
  177. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  178. data/lib/active_support/error_reporter.rb +265 -0
  179. data/lib/active_support/evented_file_update_checker.rb +182 -0
  180. data/lib/active_support/execution_context/test_helper.rb +13 -0
  181. data/lib/active_support/execution_context.rb +53 -0
  182. data/lib/active_support/execution_wrapper.rb +150 -0
  183. data/lib/active_support/executor/test_helper.rb +7 -0
  184. data/lib/active_support/executor.rb +8 -0
  185. data/lib/active_support/file_update_checker.rb +164 -0
  186. data/lib/active_support/fork_tracker.rb +43 -0
  187. data/lib/active_support/gem_version.rb +17 -0
  188. data/lib/active_support/gzip.rb +40 -0
  189. data/lib/active_support/hash_with_indifferent_access.rb +445 -0
  190. data/lib/active_support/html_safe_translation.rb +56 -0
  191. data/lib/active_support/i18n.rb +17 -0
  192. data/lib/active_support/i18n_railtie.rb +138 -0
  193. data/lib/active_support/inflections.rb +72 -0
  194. data/lib/active_support/inflector/inflections.rb +273 -0
  195. data/lib/active_support/inflector/methods.rb +387 -0
  196. data/lib/active_support/inflector/transliterate.rb +149 -0
  197. data/lib/active_support/inflector.rb +9 -0
  198. data/lib/active_support/isolated_execution_state.rb +75 -0
  199. data/lib/active_support/json/decoding.rb +76 -0
  200. data/lib/active_support/json/encoding.rb +120 -0
  201. data/lib/active_support/json.rb +4 -0
  202. data/lib/active_support/key_generator.rb +66 -0
  203. data/lib/active_support/lazy_load_hooks.rb +107 -0
  204. data/lib/active_support/locale/en.rb +33 -0
  205. data/lib/active_support/locale/en.yml +141 -0
  206. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  207. data/lib/active_support/log_subscriber.rb +192 -0
  208. data/lib/active_support/logger.rb +55 -0
  209. data/lib/active_support/logger_silence.rb +21 -0
  210. data/lib/active_support/logger_thread_safe_level.rb +47 -0
  211. data/lib/active_support/message_encryptor.rb +374 -0
  212. data/lib/active_support/message_encryptors.rb +141 -0
  213. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  214. data/lib/active_support/message_pack/extensions.rb +305 -0
  215. data/lib/active_support/message_pack/serializer.rb +63 -0
  216. data/lib/active_support/message_pack.rb +50 -0
  217. data/lib/active_support/message_verifier.rb +368 -0
  218. data/lib/active_support/message_verifiers.rb +135 -0
  219. data/lib/active_support/messages/codec.rb +65 -0
  220. data/lib/active_support/messages/metadata.rb +146 -0
  221. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  222. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  223. data/lib/active_support/messages/rotator.rb +59 -0
  224. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  225. data/lib/active_support/multibyte/chars.rb +178 -0
  226. data/lib/active_support/multibyte/unicode.rb +42 -0
  227. data/lib/active_support/multibyte.rb +23 -0
  228. data/lib/active_support/notifications/fanout.rb +446 -0
  229. data/lib/active_support/notifications/instrumenter.rb +240 -0
  230. data/lib/active_support/notifications.rb +281 -0
  231. data/lib/active_support/number_helper/number_converter.rb +190 -0
  232. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  233. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  234. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  235. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  236. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  237. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  238. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  239. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  240. data/lib/active_support/number_helper.rb +479 -0
  241. data/lib/active_support/option_merger.rb +38 -0
  242. data/lib/active_support/ordered_hash.rb +50 -0
  243. data/lib/active_support/ordered_options.rb +147 -0
  244. data/lib/active_support/parameter_filter.rb +157 -0
  245. data/lib/active_support/proxy_object.rb +20 -0
  246. data/lib/active_support/rails.rb +26 -0
  247. data/lib/active_support/railtie.rb +161 -0
  248. data/lib/active_support/reloader.rb +138 -0
  249. data/lib/active_support/rescuable.rb +176 -0
  250. data/lib/active_support/secure_compare_rotator.rb +58 -0
  251. data/lib/active_support/security_utils.rb +38 -0
  252. data/lib/active_support/string_inquirer.rb +35 -0
  253. data/lib/active_support/subscriber.rb +146 -0
  254. data/lib/active_support/syntax_error_proxy.rb +60 -0
  255. data/lib/active_support/tagged_logging.rb +152 -0
  256. data/lib/active_support/test_case.rb +304 -0
  257. data/lib/active_support/testing/assertions.rb +332 -0
  258. data/lib/active_support/testing/autorun.rb +5 -0
  259. data/lib/active_support/testing/constant_lookup.rb +51 -0
  260. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  261. data/lib/active_support/testing/declarative.rb +28 -0
  262. data/lib/active_support/testing/deprecation.rb +82 -0
  263. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  264. data/lib/active_support/testing/file_fixtures.rb +38 -0
  265. data/lib/active_support/testing/isolation.rb +121 -0
  266. data/lib/active_support/testing/method_call_assertions.rb +69 -0
  267. data/lib/active_support/testing/parallelization/server.rb +85 -0
  268. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  269. data/lib/active_support/testing/parallelization.rb +55 -0
  270. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  271. data/lib/active_support/testing/setup_and_teardown.rb +57 -0
  272. data/lib/active_support/testing/stream.rb +41 -0
  273. data/lib/active_support/testing/strict_warnings.rb +43 -0
  274. data/lib/active_support/testing/tagged_logging.rb +27 -0
  275. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  276. data/lib/active_support/testing/time_helpers.rb +269 -0
  277. data/lib/active_support/time.rb +20 -0
  278. data/lib/active_support/time_with_zone.rb +609 -0
  279. data/lib/active_support/values/time_zone.rb +614 -0
  280. data/lib/active_support/version.rb +10 -0
  281. data/lib/active_support/xml_mini/jdom.rb +175 -0
  282. data/lib/active_support/xml_mini/libxml.rb +80 -0
  283. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  284. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  285. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  286. data/lib/active_support/xml_mini/rexml.rb +137 -0
  287. data/lib/active_support/xml_mini.rb +211 -0
  288. data/lib/active_support.rb +144 -0
  289. metadata +526 -0
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/blank"
4
+ require "active_support/number_helper/number_converter"
5
+
6
+ module ActiveSupport
7
+ module NumberHelper
8
+ class NumberToPhoneConverter < NumberConverter # :nodoc:
9
+ def convert
10
+ str = country_code(opts[:country_code]).dup
11
+ str << convert_to_phone_number(number.to_s.strip)
12
+ str << phone_ext(opts[:extension])
13
+ end
14
+
15
+ private
16
+ def convert_to_phone_number(number)
17
+ if opts[:area_code]
18
+ convert_with_area_code(number)
19
+ else
20
+ convert_without_area_code(number)
21
+ end
22
+ end
23
+
24
+ def convert_with_area_code(number)
25
+ default_pattern = /(\d{1,3})(\d{3})(\d{4}$)/
26
+ number.gsub!(regexp_pattern(default_pattern),
27
+ "(\\1) \\2#{delimiter}\\3")
28
+ number
29
+ end
30
+
31
+ def convert_without_area_code(number)
32
+ default_pattern = /(\d{0,3})(\d{3})(\d{4})$/
33
+ number.gsub!(regexp_pattern(default_pattern),
34
+ "\\1#{delimiter}\\2#{delimiter}\\3")
35
+ number.slice!(0, 1) if start_with_delimiter?(number)
36
+ number
37
+ end
38
+
39
+ def start_with_delimiter?(number)
40
+ delimiter.present? && number.start_with?(delimiter)
41
+ end
42
+
43
+ def delimiter
44
+ opts[:delimiter] || "-"
45
+ end
46
+
47
+ def country_code(code)
48
+ code.blank? ? "" : "+#{code}#{delimiter}"
49
+ end
50
+
51
+ def phone_ext(ext)
52
+ ext.blank? ? "" : " x #{ext}"
53
+ end
54
+
55
+ def regexp_pattern(default_pattern)
56
+ opts.fetch :pattern, default_pattern
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/number_helper/number_converter"
4
+
5
+ module ActiveSupport
6
+ module NumberHelper
7
+ class NumberToRoundedConverter < NumberConverter # :nodoc:
8
+ self.namespace = :precision
9
+ self.validate_float = true
10
+
11
+ def convert
12
+ helper = RoundingHelper.new(options)
13
+ rounded_number = helper.round(number)
14
+
15
+ if precision = options[:precision]
16
+ if options[:significant] && precision > 0
17
+ digits = helper.digit_count(rounded_number)
18
+ precision -= digits
19
+ precision = 0 if precision < 0 # don't let it be negative
20
+ end
21
+
22
+ formatted_string =
23
+ if rounded_number.finite?
24
+ s = rounded_number.to_s("F")
25
+ a, b = s.split(".", 2)
26
+ if precision != 0
27
+ b << "0" * precision
28
+ a << "."
29
+ a << b[0, precision]
30
+ end
31
+ a
32
+ else
33
+ # Infinity/NaN
34
+ "%f" % rounded_number
35
+ end
36
+ else
37
+ formatted_string = rounded_number
38
+ end
39
+
40
+ delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
41
+ format_number(delimited_number)
42
+ end
43
+
44
+ private
45
+ def strip_insignificant_zeros
46
+ options[:strip_insignificant_zeros]
47
+ end
48
+
49
+ def format_number(number)
50
+ if strip_insignificant_zeros
51
+ escaped_separator = Regexp.escape(options[:separator])
52
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, "")
53
+ else
54
+ number
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,46 @@
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
+ precision = absolute_precision(number)
14
+ return number unless precision
15
+
16
+ rounded_number = convert_to_decimal(number).round(precision, options.fetch(:round_mode, :default).to_sym)
17
+ rounded_number.zero? ? rounded_number.abs : rounded_number # prevent showing negative zeros
18
+ end
19
+
20
+ def digit_count(number)
21
+ return 1 if number.zero?
22
+ (Math.log10(number.abs) + 1).floor
23
+ end
24
+
25
+ private
26
+ def convert_to_decimal(number)
27
+ case number
28
+ when Float, String
29
+ BigDecimal(number.to_s)
30
+ when Rational
31
+ BigDecimal(number, digit_count(number.to_i) + options[:precision])
32
+ else
33
+ number.to_d
34
+ end
35
+ end
36
+
37
+ def absolute_precision(number)
38
+ if options[:significant] && options[:precision] > 0
39
+ options[:precision] - digit_count(convert_to_decimal(number))
40
+ else
41
+ options[:precision]
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,479 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # = Number Helper
5
+ #
6
+ # Provides methods for formatting numbers into currencies, percentages,
7
+ # phone numbers, and more.
8
+ #
9
+ # Example usage in a class:
10
+ # class Topic
11
+ # include ActiveSupport::NumberHelper
12
+ #
13
+ # def price
14
+ # number_to_currency(@price)
15
+ # end
16
+ # end
17
+ #
18
+ # Example usage in a module:
19
+ # require "active_support/number_helper"
20
+ #
21
+ # module NumberFormatting
22
+ # def format_price(price)
23
+ # ActiveSupport::NumberHelper.number_to_currency(price)
24
+ # end
25
+ # end
26
+ module NumberHelper
27
+ extend ActiveSupport::Autoload
28
+
29
+ eager_autoload do
30
+ autoload :NumberConverter
31
+ autoload :RoundingHelper
32
+ autoload :NumberToRoundedConverter
33
+ autoload :NumberToDelimitedConverter
34
+ autoload :NumberToHumanConverter
35
+ autoload :NumberToHumanSizeConverter
36
+ autoload :NumberToPhoneConverter
37
+ autoload :NumberToCurrencyConverter
38
+ autoload :NumberToPercentageConverter
39
+ end
40
+
41
+ extend self
42
+
43
+ # Formats +number+ into a phone number.
44
+ #
45
+ # number_to_phone(5551234) # => "555-1234"
46
+ # number_to_phone("5551234") # => "555-1234"
47
+ # number_to_phone(1235551234) # => "123-555-1234"
48
+ # number_to_phone("12x34") # => "12x34"
49
+ #
50
+ # number_to_phone(1235551234, delimiter: ".", country_code: 1, extension: 1343)
51
+ # # => "+1.123.555.1234 x 1343"
52
+ #
53
+ # ==== Options
54
+ #
55
+ # [+:area_code+]
56
+ # Whether to use parentheses for the area code. Defaults to false.
57
+ #
58
+ # number_to_phone(1235551234, area_code: true)
59
+ # # => "(123) 555-1234"
60
+ #
61
+ # [+:delimiter+]
62
+ # The digit group delimiter to use. Defaults to <tt>"-"</tt>.
63
+ #
64
+ # number_to_phone(1235551234, delimiter: " ")
65
+ # # => "123 555 1234"
66
+ #
67
+ # [+:country_code+]
68
+ # A country code to prepend.
69
+ #
70
+ # number_to_phone(1235551234, country_code: 1)
71
+ # # => "+1-123-555-1234"
72
+ #
73
+ # [+:extension+]
74
+ # An extension to append.
75
+ #
76
+ # number_to_phone(1235551234, extension: 555)
77
+ # # => "123-555-1234 x 555"
78
+ #
79
+ # [+:pattern+]
80
+ # A regexp that specifies how the digits should be grouped. The first
81
+ # three captures from the regexp are treated as digit groups.
82
+ #
83
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
84
+ # # => "133-1234-5678"
85
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
86
+ # # => "(755) 6123-4567"
87
+ #
88
+ def number_to_phone(number, options = {})
89
+ NumberToPhoneConverter.convert(number, options)
90
+ end
91
+
92
+ # Formats a +number+ into a currency string.
93
+ #
94
+ # number_to_currency(1234567890.50) # => "$1,234,567,890.50"
95
+ # number_to_currency(1234567890.506) # => "$1,234,567,890.51"
96
+ # number_to_currency("12x34") # => "$12x34"
97
+ #
98
+ # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "")
99
+ # # => "&pound;1234567890,50"
100
+ #
101
+ # The currency unit and number formatting of the current locale will be used
102
+ # unless otherwise specified via options. No currency conversion is
103
+ # performed. If the user is given a way to change their locale, they will
104
+ # also be able to change the relative value of the currency displayed with
105
+ # this helper. If your application will ever support multiple locales, you
106
+ # may want to specify a constant +:locale+ option or consider using a
107
+ # library capable of currency conversion.
108
+ #
109
+ # ==== Options
110
+ #
111
+ # [+:locale+]
112
+ # The locale to use for formatting. Defaults to the current locale.
113
+ #
114
+ # number_to_currency(1234567890.506, locale: :fr)
115
+ # # => "1 234 567 890,51 €"
116
+ #
117
+ # [+:precision+]
118
+ # The level of precision. Defaults to 2.
119
+ #
120
+ # number_to_currency(1234567890.123, precision: 3) # => "$1,234,567,890.123"
121
+ # number_to_currency(0.456789, precision: 0) # => "$0"
122
+ #
123
+ # [+:round_mode+]
124
+ # Specifies how rounding is performed. See +BigDecimal.mode+. Defaults to
125
+ # +:default+.
126
+ #
127
+ # number_to_currency(1234567890.01, precision: 0, round_mode: :up)
128
+ # # => "$1,234,567,891"
129
+ #
130
+ # [+:unit+]
131
+ # The denomination of the currency. Defaults to <tt>"$"</tt>.
132
+ #
133
+ # [+:separator+]
134
+ # The decimal separator. Defaults to <tt>"."</tt>.
135
+ #
136
+ # [+:delimiter+]
137
+ # The thousands delimiter. Defaults to <tt>","</tt>.
138
+ #
139
+ # [+:format+]
140
+ # The format for non-negative numbers. <tt>%u</tt> represents the currency,
141
+ # and <tt>%n</tt> represents the number. Defaults to <tt>"%u%n"</tt>.
142
+ #
143
+ # number_to_currency(1234567890.50, format: "%n %u")
144
+ # # => "1,234,567,890.50 $"
145
+ #
146
+ # [+:negative_format+]
147
+ # The format for negative numbers. <tt>%u</tt> and <tt>%n</tt> behave the
148
+ # same as in +:format+, but <tt>%n</tt> represents the absolute value of
149
+ # the number. Defaults to the value of +:format+ prepended with <tt>-</tt>.
150
+ #
151
+ # number_to_currency(-1234567890.50, negative_format: "(%u%n)")
152
+ # # => "($1,234,567,890.50)"
153
+ #
154
+ # [+:strip_insignificant_zeros+]
155
+ # Whether to remove insignificant zeros after the decimal separator.
156
+ # Defaults to false.
157
+ #
158
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
159
+ # # => "$1,234,567,890.5"
160
+ #
161
+ def number_to_currency(number, options = {})
162
+ NumberToCurrencyConverter.convert(number, options)
163
+ end
164
+
165
+ # Formats +number+ as a percentage string.
166
+ #
167
+ # number_to_percentage(100) # => "100.000%"
168
+ # number_to_percentage("99") # => "99.000%"
169
+ # number_to_percentage("99x") # => "99x%"
170
+ #
171
+ # number_to_percentage(12345.6789, delimiter: ".", separator: ",", precision: 2)
172
+ # # => "12.345,68%"
173
+ #
174
+ # ==== Options
175
+ #
176
+ # [+:locale+]
177
+ # The locale to use for formatting. Defaults to the current locale.
178
+ #
179
+ # number_to_percentage(1000, locale: :fr)
180
+ # # => "1000,000%"
181
+ #
182
+ # [+:precision+]
183
+ # The level of precision, or +nil+ to preserve +number+'s precision.
184
+ # Defaults to 2.
185
+ #
186
+ # number_to_percentage(12.3456789, precision: 4) # => "12.3457%"
187
+ # number_to_percentage(99.999, precision: 0) # => "100%"
188
+ # number_to_percentage(99.999, precision: nil) # => "99.999%"
189
+ #
190
+ # [+:round_mode+]
191
+ # Specifies how rounding is performed. See +BigDecimal.mode+. Defaults to
192
+ # +:default+.
193
+ #
194
+ # number_to_percentage(12.3456789, precision: 4, round_mode: :down)
195
+ # # => "12.3456%"
196
+ #
197
+ # [+:significant+]
198
+ # Whether +:precision+ should be applied to significant digits instead of
199
+ # fractional digits. Defaults to false.
200
+ #
201
+ # number_to_percentage(12345.6789) # => "12345.679%"
202
+ # number_to_percentage(12345.6789, significant: true) # => "12300%"
203
+ # number_to_percentage(12345.6789, precision: 2) # => "12345.68%"
204
+ # number_to_percentage(12345.6789, precision: 2, significant: true) # => "12000%"
205
+ #
206
+ # [+:separator+]
207
+ # The decimal separator. Defaults to <tt>"."</tt>.
208
+ #
209
+ # [+:delimiter+]
210
+ # The thousands delimiter. Defaults to <tt>","</tt>.
211
+ #
212
+ # [+:strip_insignificant_zeros+]
213
+ # Whether to remove insignificant zeros after the decimal separator.
214
+ # Defaults to false.
215
+ #
216
+ # [+:format+]
217
+ # The format of the output. <tt>%n</tt> represents the number. Defaults to
218
+ # <tt>"%n%"</tt>.
219
+ #
220
+ # number_to_percentage(100, format: "%n %")
221
+ # # => "100.000 %"
222
+ #
223
+ def number_to_percentage(number, options = {})
224
+ NumberToPercentageConverter.convert(number, options)
225
+ end
226
+
227
+ # Formats +number+ by grouping thousands with a delimiter.
228
+ #
229
+ # number_to_delimited(12345678) # => "12,345,678"
230
+ # number_to_delimited("123456") # => "123,456"
231
+ # number_to_delimited(12345678.9876) # => "12,345,678.9876"
232
+ # number_to_delimited("12x34") # => "12x34"
233
+ #
234
+ # number_to_delimited(12345678.9876, delimiter: ".", separator: ",")
235
+ # # => "12.345.678,9876"
236
+ #
237
+ # ==== Options
238
+ #
239
+ # [+:locale+]
240
+ # The locale to use for formatting. Defaults to the current locale.
241
+ #
242
+ # number_to_delimited(12345678.05, locale: :fr)
243
+ # # => "12 345 678,05"
244
+ #
245
+ # [+:delimiter+]
246
+ # The thousands delimiter. Defaults to <tt>","</tt>.
247
+ #
248
+ # number_to_delimited(12345678, delimiter: ".")
249
+ # # => "12.345.678"
250
+ #
251
+ # [+:separator+]
252
+ # The decimal separator. Defaults to <tt>"."</tt>.
253
+ #
254
+ # number_to_delimited(12345678.05, separator: " ")
255
+ # # => "12,345,678 05"
256
+ #
257
+ # [+:delimiter_pattern+]
258
+ # A regexp to determine the placement of delimiters. Helpful when using
259
+ # currency formats like INR.
260
+ #
261
+ # number_to_delimited("123456.78", delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
262
+ # # => "1,23,456.78"
263
+ #
264
+ def number_to_delimited(number, options = {})
265
+ NumberToDelimitedConverter.convert(number, options)
266
+ end
267
+
268
+ # Formats +number+ to a specific level of precision.
269
+ #
270
+ # number_to_rounded(12345.6789) # => "12345.679"
271
+ # number_to_rounded(12345.6789, precision: 2) # => "12345.68"
272
+ # number_to_rounded(12345.6789, precision: 0) # => "12345"
273
+ # number_to_rounded(12345, precision: 5) # => "12345.00000"
274
+ #
275
+ # ==== Options
276
+ #
277
+ # [+:locale+]
278
+ # The locale to use for formatting. Defaults to the current locale.
279
+ #
280
+ # number_to_rounded(111.234, locale: :fr)
281
+ # # => "111,234"
282
+ #
283
+ # [+:precision+]
284
+ # The level of precision, or +nil+ to preserve +number+'s precision.
285
+ # Defaults to 3.
286
+ #
287
+ # number_to_rounded(12345.6789, precision: nil)
288
+ # # => "12345.6789"
289
+ #
290
+ # [+:round_mode+]
291
+ # Specifies how rounding is performed. See +BigDecimal.mode+. Defaults to
292
+ # +:default+.
293
+ #
294
+ # number_to_rounded(12.34, precision: 0, round_mode: :up)
295
+ # # => "13"
296
+ #
297
+ # [+:significant+]
298
+ # Whether +:precision+ should be applied to significant digits instead of
299
+ # fractional digits. Defaults to false.
300
+ #
301
+ # number_to_rounded(12345.6789) # => "12345.679"
302
+ # number_to_rounded(12345.6789, significant: true) # => "12300"
303
+ # number_to_rounded(12345.6789, precision: 2) # => "12345.68"
304
+ # number_to_rounded(12345.6789, precision: 2, significant: true) # => "12000"
305
+ #
306
+ # [+:separator+]
307
+ # The decimal separator. Defaults to <tt>"."</tt>.
308
+ #
309
+ # [+:delimiter+]
310
+ # The thousands delimiter. Defaults to <tt>","</tt>.
311
+ #
312
+ # [+:strip_insignificant_zeros+]
313
+ # Whether to remove insignificant zeros after the decimal separator.
314
+ # Defaults to false.
315
+ #
316
+ # number_to_rounded(12.34, strip_insignificant_zeros: false) # => "12.340"
317
+ # number_to_rounded(12.34, strip_insignificant_zeros: true) # => "12.34"
318
+ # number_to_rounded(12.3456, strip_insignificant_zeros: true) # => "12.346"
319
+ #
320
+ def number_to_rounded(number, options = {})
321
+ NumberToRoundedConverter.convert(number, options)
322
+ end
323
+
324
+ # Formats +number+ as bytes into a more human-friendly representation.
325
+ # Useful for reporting file sizes to users.
326
+ #
327
+ # number_to_human_size(123) # => "123 Bytes"
328
+ # number_to_human_size(1234) # => "1.21 KB"
329
+ # number_to_human_size(12345) # => "12.1 KB"
330
+ # number_to_human_size(1234567) # => "1.18 MB"
331
+ # number_to_human_size(1234567890) # => "1.15 GB"
332
+ # number_to_human_size(1234567890123) # => "1.12 TB"
333
+ # number_to_human_size(1234567890123456) # => "1.1 PB"
334
+ # number_to_human_size(1234567890123456789) # => "1.07 EB"
335
+ #
336
+ # See #number_to_human if you want to pretty-print a generic number.
337
+ #
338
+ # ==== Options
339
+ #
340
+ # [+:locale+]
341
+ # The locale to use for formatting. Defaults to the current locale.
342
+ #
343
+ # [+:precision+]
344
+ # The level of precision. Defaults to 3.
345
+ #
346
+ # number_to_human_size(123456, precision: 2) # => "120 KB"
347
+ # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
348
+ #
349
+ # [+:round_mode+]
350
+ # Specifies how rounding is performed. See +BigDecimal.mode+. Defaults to
351
+ # +:default+.
352
+ #
353
+ # number_to_human_size(123456, precision: 2, round_mode: :up)
354
+ # # => "130 KB"
355
+ #
356
+ # [+:significant+]
357
+ # Whether +:precision+ should be applied to significant digits instead of
358
+ # fractional digits. Defaults to true.
359
+ #
360
+ # [+:separator+]
361
+ # The decimal separator. Defaults to <tt>"."</tt>.
362
+ #
363
+ # number_to_human_size(1234567, separator: ",")
364
+ # # => "1,18 MB"
365
+ #
366
+ # [+:delimiter+]
367
+ # The thousands delimiter. Defaults to <tt>","</tt>.
368
+ #
369
+ # [+:strip_insignificant_zeros+]
370
+ # Whether to remove insignificant zeros after the decimal separator.
371
+ # Defaults to true.
372
+ #
373
+ def number_to_human_size(number, options = {})
374
+ NumberToHumanSizeConverter.convert(number, options)
375
+ end
376
+
377
+ # Formats +number+ into a more human-friendly representation. Useful for
378
+ # numbers that can become very large and too hard to read.
379
+ #
380
+ # number_to_human(123) # => "123"
381
+ # number_to_human(1234) # => "1.23 Thousand"
382
+ # number_to_human(12345) # => "12.3 Thousand"
383
+ # number_to_human(1234567) # => "1.23 Million"
384
+ # number_to_human(1234567890) # => "1.23 Billion"
385
+ # number_to_human(1234567890123) # => "1.23 Trillion"
386
+ # number_to_human(1234567890123456) # => "1.23 Quadrillion"
387
+ # number_to_human(1234567890123456789) # => "1230 Quadrillion"
388
+ #
389
+ # See #number_to_human_size if you want to pretty-print a file size.
390
+ #
391
+ # ==== Options
392
+ #
393
+ # [+:locale+]
394
+ # The locale to use for formatting. Defaults to the current locale.
395
+ #
396
+ # [+:precision+]
397
+ # The level of precision. Defaults to 3.
398
+ #
399
+ # number_to_human(123456, precision: 2) # => "120 Thousand"
400
+ # number_to_human(123456, precision: 4) # => "123.5 Thousand"
401
+ #
402
+ # [+:round_mode+]
403
+ # Specifies how rounding is performed. See +BigDecimal.mode+. Defaults to
404
+ # +:default+.
405
+ #
406
+ # number_to_human(123456, precision: 2, round_mode: :up)
407
+ # # => "130 Thousand"
408
+ #
409
+ # [+:significant+]
410
+ # Whether +:precision+ should be applied to significant digits instead of
411
+ # fractional digits. Defaults to true.
412
+ #
413
+ # [+:separator+]
414
+ # The decimal separator. Defaults to <tt>"."</tt>.
415
+ #
416
+ # number_to_human(123456, precision: 4, separator: ",")
417
+ # # => "123,5 Thousand"
418
+ #
419
+ # [+:delimiter+]
420
+ # The thousands delimiter. Defaults to <tt>","</tt>.
421
+ #
422
+ # [+:strip_insignificant_zeros+]
423
+ # Whether to remove insignificant zeros after the decimal separator.
424
+ # Defaults to true.
425
+ #
426
+ # number_to_human(1000000) # => "1 Million"
427
+ # number_to_human(1000000, strip_insignificant_zeros: false) # => "1.00 Million"
428
+ # number_to_human(10.01) # => "10"
429
+ # number_to_human(10.01, strip_insignificant_zeros: false) # => "10.0"
430
+ #
431
+ # [+:format+]
432
+ # The format of the output. <tt>%n</tt> represents the number, and
433
+ # <tt>%u</tt> represents the quantifier (e.g., "Thousand"). Defaults to
434
+ # <tt>"%n %u"</tt>.
435
+ #
436
+ # [+:units+]
437
+ # A Hash of custom unit quantifier names.
438
+ #
439
+ # number_to_human(1, units: { unit: "m", thousand: "km" }) # => "1 m"
440
+ # number_to_human(100, units: { unit: "m", thousand: "km" }) # => "100 m"
441
+ # number_to_human(1000, units: { unit: "m", thousand: "km" }) # => "1 km"
442
+ # number_to_human(100000, units: { unit: "m", thousand: "km" }) # => "100 km"
443
+ # number_to_human(10000000, units: { unit: "m", thousand: "km" }) # => "10000 km"
444
+ #
445
+ # The following keys are supported for integer units: +:unit+, +:ten+,
446
+ # +:hundred+, +:thousand+, +:million+, +:billion+, +:trillion+,
447
+ # +:quadrillion+. Additionally, the following keys are supported for
448
+ # fractional units: +:deci+, +:centi+, +:mili+, +:micro+, +:nano+,
449
+ # +:pico+, +:femto+.
450
+ #
451
+ # The Hash can also be defined as a scope in an I18n locale. For example:
452
+ #
453
+ # en:
454
+ # distance:
455
+ # centi:
456
+ # one: "centimeter"
457
+ # other: "centimeters"
458
+ # unit:
459
+ # one: "meter"
460
+ # other: "meters"
461
+ # thousand:
462
+ # one: "kilometer"
463
+ # other: "kilometers"
464
+ #
465
+ # Then it can be specified by name:
466
+ #
467
+ # number_to_human(1, units: :distance) # => "1 meter"
468
+ # number_to_human(100, units: :distance) # => "100 meters"
469
+ # number_to_human(1000, units: :distance) # => "1 kilometer"
470
+ # number_to_human(100000, units: :distance) # => "100 kilometers"
471
+ # number_to_human(10000000, units: :distance) # => "10000 kilometers"
472
+ # number_to_human(0.1, units: :distance) # => "10 centimeters"
473
+ # number_to_human(0.01, units: :distance) # => "1 centimeter"
474
+ #
475
+ def number_to_human(number, options = {})
476
+ NumberToHumanConverter.convert(number, options)
477
+ end
478
+ end
479
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/deep_merge"
4
+
5
+ module ActiveSupport
6
+ class OptionMerger # :nodoc:
7
+ instance_methods.each do |method|
8
+ undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
9
+ end
10
+
11
+ def initialize(context, options)
12
+ @context, @options = context, options
13
+ end
14
+
15
+ private
16
+ def method_missing(method, *arguments, &block)
17
+ options = nil
18
+ if arguments.size == 1 && arguments.first.is_a?(Proc)
19
+ proc = arguments.shift
20
+ arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
21
+ elsif arguments.last.respond_to?(:to_hash)
22
+ options = @options.deep_merge(arguments.pop)
23
+ else
24
+ options = @options
25
+ end
26
+
27
+ if options
28
+ @context.__send__(method, *arguments, **options, &block)
29
+ else
30
+ @context.__send__(method, *arguments, &block)
31
+ end
32
+ end
33
+
34
+ def respond_to_missing?(...)
35
+ @context.respond_to?(...)
36
+ end
37
+ end
38
+ end