activesupport 4.2.11.1 → 6.1.7.3

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 (272) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +464 -391
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -7
  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 +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +34 -6
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +61 -55
  12. data/lib/active_support/cache/mem_cache_store.rb +115 -100
  13. data/lib/active_support/cache/memory_store.rb +81 -58
  14. data/lib/active_support/cache/null_store.rb +11 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +90 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +386 -225
  19. data/lib/active_support/callbacks.rb +661 -594
  20. data/lib/active_support/concern.rb +80 -7
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +226 -0
  23. data/lib/active_support/configurable.rb +16 -17
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +41 -1
  26. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  32. data/lib/active_support/core_ext/array.rb +9 -6
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -48
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -25
  39. data/lib/active_support/core_ext/class.rb +4 -3
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +14 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  43. data/lib/active_support/core_ext/date/conversions.rb +27 -24
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +6 -4
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +167 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +19 -3
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -13
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +37 -19
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
  53. data/lib/active_support/core_ext/date_time/conversions.rb +16 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +8 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +186 -22
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +13 -10
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +20 -43
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +10 -8
  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 -9
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +63 -69
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +148 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +19 -14
  85. data/lib/active_support/core_ext/module/delegation.rb +164 -51
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -22
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  90. data/lib/active_support/core_ext/module.rb +13 -11
  91. data/lib/active_support/core_ext/name_error.rb +51 -4
  92. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +133 -136
  94. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  95. data/lib/active_support/core_ext/numeric.rb +5 -3
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +27 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  101. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  102. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  103. data/lib/active_support/core_ext/object/json.rb +63 -21
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +81 -23
  107. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  108. data/lib/active_support/core_ext/object.rb +14 -13
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  111. data/lib/active_support/core_ext/range/each.rb +18 -17
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +45 -0
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  119. data/lib/active_support/core_ext/string/conversions.rb +8 -4
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +48 -6
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +102 -26
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  126. data/lib/active_support/core_ext/string/output_safety.rb +125 -40
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +6 -5
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +137 -53
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +22 -13
  137. data/lib/active_support/core_ext/time/zones.rb +41 -7
  138. data/lib/active_support/core_ext/time.rb +7 -6
  139. data/lib/active_support/core_ext/uri.rb +11 -8
  140. data/lib/active_support/core_ext.rb +3 -1
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +210 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +241 -175
  147. data/lib/active_support/deprecation/behaviors.rb +58 -12
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  153. data/lib/active_support/deprecation/reporting.rb +81 -18
  154. data/lib/active_support/deprecation.rb +17 -9
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +22 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  158. data/lib/active_support/duration/iso8601_serializer.rb +59 -0
  159. data/lib/active_support/duration.rb +364 -39
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +170 -0
  164. data/lib/active_support/execution_wrapper.rb +132 -0
  165. data/lib/active_support/executor.rb +8 -0
  166. data/lib/active_support/file_update_checker.rb +62 -37
  167. data/lib/active_support/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +7 -5
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +171 -48
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +47 -16
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +58 -14
  175. data/lib/active_support/inflector/methods.rb +186 -169
  176. data/lib/active_support/inflector/transliterate.rb +83 -33
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +32 -30
  179. data/lib/active_support/json/encoding.rb +22 -61
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +11 -43
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +9 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  186. data/lib/active_support/log_subscriber.rb +52 -19
  187. data/lib/active_support/logger.rb +10 -24
  188. data/lib/active_support/logger_silence.rb +14 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -10
  190. data/lib/active_support/message_encryptor.rb +167 -57
  191. data/lib/active_support/message_verifier.rb +151 -18
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +35 -80
  196. data/lib/active_support/multibyte/unicode.rb +22 -330
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +125 -23
  199. data/lib/active_support/notifications/instrumenter.rb +98 -16
  200. data/lib/active_support/notifications.rb +82 -14
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -14
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +14 -11
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +12 -10
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +15 -5
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +29 -57
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +123 -71
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +7 -5
  213. data/lib/active_support/ordered_options.rb +35 -7
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +10 -4
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +10 -11
  218. data/lib/active_support/railtie.rb +66 -10
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +112 -57
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +13 -4
  224. data/lib/active_support/subscriber.rb +80 -31
  225. data/lib/active_support/tagged_logging.rb +54 -17
  226. data/lib/active_support/test_case.rb +107 -44
  227. data/lib/active_support/testing/assertions.rb +158 -20
  228. data/lib/active_support/testing/autorun.rb +4 -2
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +13 -10
  232. data/lib/active_support/testing/file_fixtures.rb +38 -0
  233. data/lib/active_support/testing/isolation.rb +35 -26
  234. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  239. data/lib/active_support/testing/stream.rb +43 -0
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +121 -20
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +215 -51
  244. data/lib/active_support/values/time_zone.rb +223 -71
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +116 -115
  247. data/lib/active_support/xml_mini/libxml.rb +16 -13
  248. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  249. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  250. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +38 -46
  253. data/lib/active_support.rb +25 -11
  254. metadata +100 -43
  255. data/lib/active_support/concurrency/latch.rb +0 -27
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  258. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  259. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  260. data/lib/active_support/core_ext/hash/compact.rb +0 -24
  261. data/lib/active_support/core_ext/hash/transform_values.rb +0 -23
  262. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  263. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  264. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  265. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  266. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  267. data/lib/active_support/core_ext/object/itself.rb +0 -15
  268. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  269. data/lib/active_support/core_ext/struct.rb +0 -6
  270. data/lib/active_support/core_ext/thread.rb +0 -86
  271. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  272. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/number_helper/number_converter"
4
+
1
5
  module ActiveSupport
2
6
  module NumberHelper
3
7
  class NumberToRoundedConverter < NumberConverter # :nodoc:
@@ -5,63 +9,39 @@ module ActiveSupport
5
9
  self.validate_float = true
6
10
 
7
11
  def convert
8
- precision = options.delete :precision
9
- significant = options.delete :significant
10
-
11
- case number
12
- when Float, String
13
- @number = BigDecimal(number.to_s)
14
- when Rational
15
- @number = BigDecimal(number, digit_count(number.to_i) + precision)
16
- else
17
- @number = number.to_d
18
- end
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
19
21
 
20
- if significant && precision > 0
21
- digits, rounded_number = digits_and_rounded_number(precision)
22
- precision -= digits
23
- precision = 0 if precision < 0 # don't let it be negative
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
24
36
  else
25
- rounded_number = number.round(precision)
26
- rounded_number = rounded_number.to_i if precision == 0 && rounded_number.finite?
27
- rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
37
+ formatted_string = rounded_number
28
38
  end
29
39
 
30
- formatted_string =
31
- if BigDecimal === rounded_number && rounded_number.finite?
32
- s = rounded_number.to_s('F') + '0'*precision
33
- a, b = s.split('.', 2)
34
- a + '.' + b[0, precision]
35
- else
36
- "%00.#{precision}f" % rounded_number
37
- end
38
-
39
40
  delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
40
41
  format_number(delimited_number)
41
42
  end
42
43
 
43
44
  private
44
-
45
- def digits_and_rounded_number(precision)
46
- if zero?
47
- [1, 0]
48
- else
49
- digits = digit_count(number)
50
- multiplier = 10 ** (digits - precision)
51
- rounded_number = calculate_rounded_number(multiplier)
52
- digits = digit_count(rounded_number) # After rounding, the number of digits may have changed
53
- [digits, rounded_number]
54
- end
55
- end
56
-
57
- def calculate_rounded_number(multiplier)
58
- (number / BigDecimal.new(multiplier.to_f.to_s)).round * multiplier
59
- end
60
-
61
- def digit_count(number)
62
- number.zero? ? 1 : (Math.log10(absolute_number(number)) + 1).floor
63
- end
64
-
65
45
  def strip_insignificant_zeros
66
46
  options[:strip_insignificant_zeros]
67
47
  end
@@ -69,19 +49,11 @@ module ActiveSupport
69
49
  def format_number(number)
70
50
  if strip_insignificant_zeros
71
51
  escaped_separator = Regexp.escape(options[:separator])
72
- number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
52
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, "")
73
53
  else
74
54
  number
75
55
  end
76
56
  end
77
-
78
- def absolute_number(number)
79
- number.respond_to?(:abs) ? number.abs : number.to_d.abs
80
- end
81
-
82
- def zero?
83
- number.respond_to?(:zero?) ? number.zero? : number.to_d.zero?
84
- end
85
57
  end
86
58
  end
87
59
  end
@@ -0,0 +1,50 @@
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 significant && options[:precision] > 0
39
+ options[:precision] - digit_count(convert_to_decimal(number))
40
+ else
41
+ options[:precision]
42
+ end
43
+ end
44
+
45
+ def significant
46
+ options[:significant]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  module NumberHelper
3
5
  extend ActiveSupport::Autoload
4
6
 
5
7
  eager_autoload do
6
8
  autoload :NumberConverter
9
+ autoload :RoundingHelper
7
10
  autoload :NumberToRoundedConverter
8
11
  autoload :NumberToDelimitedConverter
9
12
  autoload :NumberToHumanConverter
@@ -15,7 +18,7 @@ module ActiveSupport
15
18
 
16
19
  extend self
17
20
 
18
- # 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)
19
22
  # 123-9876). You can customize the format in the +options+ hash.
20
23
  #
21
24
  # ==== Options
@@ -27,19 +30,26 @@ module ActiveSupport
27
30
  # end of the generated number.
28
31
  # * <tt>:country_code</tt> - Sets the country code for the phone
29
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.
30
35
  # ==== Examples
31
36
  #
32
- # number_to_phone(5551234) # => 555-1234
33
- # number_to_phone('5551234') # => 555-1234
34
- # number_to_phone(1235551234) # => 123-555-1234
35
- # number_to_phone(1235551234, area_code: true) # => (123) 555-1234
36
- # number_to_phone(1235551234, delimiter: ' ') # => 123 555 1234
37
- # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
38
- # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
39
- # 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"
40
45
  #
41
46
  # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
42
- # # => +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"
43
53
  def number_to_phone(number, options = {})
44
54
  NumberToPhoneConverter.convert(number, options)
45
55
  end
@@ -47,12 +57,22 @@ module ActiveSupport
47
57
  # Formats a +number+ into a currency string (e.g., $13.65). You
48
58
  # can customize the format in the +options+ hash.
49
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
+ #
50
68
  # ==== Options
51
69
  #
52
70
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
53
71
  # (defaults to current locale).
54
72
  # * <tt>:precision</tt> - Sets the level of precision (defaults
55
73
  # to 2).
74
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
75
+ # (defaults to :default. See BigDecimal::mode)
56
76
  # * <tt>:unit</tt> - Sets the denomination of the currency
57
77
  # (defaults to "$").
58
78
  # * <tt>:separator</tt> - Sets the separator between the units
@@ -63,25 +83,36 @@ module ActiveSupport
63
83
  # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
64
84
  # currency, and <tt>%n</tt> for the number.
65
85
  # * <tt>:negative_format</tt> - Sets the format for negative
66
- # numbers (defaults to prepending an hyphen to the formatted
86
+ # numbers (defaults to prepending a hyphen to the formatted
67
87
  # number given by <tt>:format</tt>). Accepts the same fields
68
88
  # than <tt>:format</tt>, except <tt>%n</tt> is here the
69
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+).
70
93
  #
71
94
  # ==== Examples
72
95
  #
73
- # number_to_currency(1234567890.50) # => $1,234,567,890.50
74
- # number_to_currency(1234567890.506) # => $1,234,567,890.51
75
- # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
76
- # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
77
- # 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"
101
+ #
102
+ # number_to_currency("123a456", raise: true) # => InvalidNumberError
78
103
  #
104
+ # number_to_currency(-0.456789, precision: 0)
105
+ # # => "$0"
79
106
  # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
80
- # # => ($1,234,567,890.50)
107
+ # # => "($1,234,567,890.50)"
81
108
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '')
82
- # # => &pound;1234567890,50
109
+ # # => "&pound;1234567890,50"
83
110
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
84
- # # => 1234567890,50 &pound;
111
+ # # => "1234567890,50 &pound;"
112
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
113
+ # # => "$1,234,567,890.5"
114
+ # number_to_currency(1234567890.50, precision: 0, round_mode: :up)
115
+ # # => "$1,234,567,891"
85
116
  def number_to_currency(number, options = {})
86
117
  NumberToCurrencyConverter.convert(number, options)
87
118
  end
@@ -94,9 +125,11 @@ module ActiveSupport
94
125
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
95
126
  # (defaults to current locale).
96
127
  # * <tt>:precision</tt> - Sets the precision of the number
97
- # (defaults to 3).
98
- # * <tt>:significant</tt> - If +true+, precision will be the #
99
- # of significant_digits. If +false+, the # of fractional
128
+ # (defaults to 3). Keeps the number's precision if +nil+.
129
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
130
+ # (defaults to :default. See BigDecimal::mode)
131
+ # * <tt>:significant</tt> - If +true+, precision will be the number
132
+ # of significant_digits. If +false+, the number of fractional
100
133
  # digits (defaults to +false+).
101
134
  # * <tt>:separator</tt> - Sets the separator between the
102
135
  # fractional and integer digits (defaults to ".").
@@ -110,14 +143,16 @@ module ActiveSupport
110
143
  #
111
144
  # ==== Examples
112
145
  #
113
- # number_to_percentage(100) # => 100.000%
114
- # number_to_percentage('98') # => 98.000%
115
- # number_to_percentage(100, precision: 0) # => 100%
116
- # number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
117
- # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
118
- # number_to_percentage(1000, locale: :fr) # => 1 000,000%
119
- # number_to_percentage('98a') # => 98a%
120
- # number_to_percentage(100, format: '%n %') # => 100 %
146
+ # number_to_percentage(100) # => "100.000%"
147
+ # number_to_percentage('98') # => "98.000%"
148
+ # number_to_percentage(100, precision: 0) # => "100%"
149
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
150
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
151
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
152
+ # number_to_percentage(1000, precision: nil) # => "1000%"
153
+ # number_to_percentage('98a') # => "98a%"
154
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
155
+ # number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%"
121
156
  def number_to_percentage(number, options = {})
122
157
  NumberToPercentageConverter.convert(number, options)
123
158
  end
@@ -134,19 +169,25 @@ module ActiveSupport
134
169
  # to ",").
135
170
  # * <tt>:separator</tt> - Sets the separator between the
136
171
  # fractional and integer digits (defaults to ".").
172
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
173
+ # deriving the placement of delimiter. Helpful when using currency formats
174
+ # like INR.
137
175
  #
138
176
  # ==== Examples
139
177
  #
140
- # number_to_delimited(12345678) # => 12,345,678
141
- # number_to_delimited('123456') # => 123,456
142
- # number_to_delimited(12345678.05) # => 12,345,678.05
143
- # number_to_delimited(12345678, delimiter: '.') # => 12.345.678
144
- # number_to_delimited(12345678, delimiter: ',') # => 12,345,678
145
- # number_to_delimited(12345678.05, separator: ' ') # => 12,345,678 05
146
- # number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
147
- # number_to_delimited('112a') # => 112a
178
+ # number_to_delimited(12345678) # => "12,345,678"
179
+ # number_to_delimited('123456') # => "123,456"
180
+ # number_to_delimited(12345678.05) # => "12,345,678.05"
181
+ # number_to_delimited(12345678, delimiter: '.') # => "12.345.678"
182
+ # number_to_delimited(12345678, delimiter: ',') # => "12,345,678"
183
+ # number_to_delimited(12345678.05, separator: ' ') # => "12,345,678 05"
184
+ # number_to_delimited(12345678.05, locale: :fr) # => "12 345 678,05"
185
+ # number_to_delimited('112a') # => "112a"
148
186
  # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
149
- # # => 98 765 432,98
187
+ # # => "98 765 432,98"
188
+ # number_to_delimited("123456.78",
189
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
190
+ # # => "1,23,456.78"
150
191
  def number_to_delimited(number, options = {})
151
192
  NumberToDelimitedConverter.convert(number, options)
152
193
  end
@@ -161,9 +202,11 @@ module ActiveSupport
161
202
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
162
203
  # (defaults to current locale).
163
204
  # * <tt>:precision</tt> - Sets the precision of the number
164
- # (defaults to 3).
165
- # * <tt>:significant</tt> - If +true+, precision will be the #
166
- # of significant_digits. If +false+, the # of fractional
205
+ # (defaults to 3). Keeps the number's precision if +nil+.
206
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
207
+ # (defaults to :default. See BigDecimal::mode)
208
+ # * <tt>:significant</tt> - If +true+, precision will be the number
209
+ # of significant_digits. If +false+, the number of fractional
167
210
  # digits (defaults to +false+).
168
211
  # * <tt>:separator</tt> - Sets the separator between the
169
212
  # fractional and integer digits (defaults to ".").
@@ -175,27 +218,29 @@ module ActiveSupport
175
218
  #
176
219
  # ==== Examples
177
220
  #
178
- # number_to_rounded(111.2345) # => 111.235
179
- # number_to_rounded(111.2345, precision: 2) # => 111.23
180
- # number_to_rounded(13, precision: 5) # => 13.00000
181
- # number_to_rounded(389.32314, precision: 0) # => 389
182
- # number_to_rounded(111.2345, significant: true) # => 111
183
- # number_to_rounded(111.2345, precision: 1, significant: true) # => 100
184
- # number_to_rounded(13, precision: 5, significant: true) # => 13.000
185
- # number_to_rounded(111.234, locale: :fr) # => 111,234
221
+ # number_to_rounded(111.2345) # => "111.235"
222
+ # number_to_rounded(111.2345, precision: 2) # => "111.23"
223
+ # number_to_rounded(13, precision: 5) # => "13.00000"
224
+ # number_to_rounded(389.32314, precision: 0) # => "389"
225
+ # number_to_rounded(111.2345, significant: true) # => "111"
226
+ # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
227
+ # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
228
+ # number_to_rounded(13, precision: nil) # => "13"
229
+ # number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
230
+ # number_to_rounded(111.234, locale: :fr) # => "111,234"
186
231
  #
187
232
  # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
188
- # # => 13
233
+ # # => "13"
189
234
  #
190
- # number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
235
+ # number_to_rounded(389.32314, precision: 4, significant: true) # => "389.3"
191
236
  # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
192
- # # => 1.111,23
237
+ # # => "1.111,23"
193
238
  def number_to_rounded(number, options = {})
194
239
  NumberToRoundedConverter.convert(number, options)
195
240
  end
196
241
 
197
242
  # Formats the bytes in +number+ into a more understandable
198
- # representation (e.g., giving it 1500 yields 1.5 KB). This
243
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
199
244
  # method is useful for reporting file sizes to users. You can
200
245
  # customize the format in the +options+ hash.
201
246
  #
@@ -208,8 +253,10 @@ module ActiveSupport
208
253
  # (defaults to current locale).
209
254
  # * <tt>:precision</tt> - Sets the precision of the number
210
255
  # (defaults to 3).
211
- # * <tt>:significant</tt> - If +true+, precision will be the #
212
- # of significant_digits. If +false+, the # of fractional
256
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
257
+ # (defaults to :default. See BigDecimal::mode)
258
+ # * <tt>:significant</tt> - If +true+, precision will be the number
259
+ # of significant_digits. If +false+, the number of fractional
213
260
  # digits (defaults to +true+)
214
261
  # * <tt>:separator</tt> - Sets the separator between the
215
262
  # fractional and integer digits (defaults to ".").
@@ -218,20 +265,21 @@ module ActiveSupport
218
265
  # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
219
266
  # insignificant zeros after the decimal separator (defaults to
220
267
  # +true+)
221
- # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
222
- # prefix (defaults to :binary)
223
268
  #
224
269
  # ==== Examples
225
270
  #
226
- # number_to_human_size(123) # => 123 Bytes
227
- # number_to_human_size(1234) # => 1.21 KB
228
- # number_to_human_size(12345) # => 12.1 KB
229
- # number_to_human_size(1234567) # => 1.18 MB
230
- # number_to_human_size(1234567890) # => 1.15 GB
231
- # number_to_human_size(1234567890123) # => 1.12 TB
232
- # number_to_human_size(1234567, precision: 2) # => 1.2 MB
233
- # number_to_human_size(483989, precision: 2) # => 470 KB
234
- # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
271
+ # number_to_human_size(123) # => "123 Bytes"
272
+ # number_to_human_size(1234) # => "1.21 KB"
273
+ # number_to_human_size(12345) # => "12.1 KB"
274
+ # number_to_human_size(1234567) # => "1.18 MB"
275
+ # number_to_human_size(1234567890) # => "1.15 GB"
276
+ # number_to_human_size(1234567890123) # => "1.12 TB"
277
+ # number_to_human_size(1234567890123456) # => "1.1 PB"
278
+ # number_to_human_size(1234567890123456789) # => "1.07 EB"
279
+ # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
280
+ # number_to_human_size(483989, precision: 2) # => "470 KB"
281
+ # number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
282
+ # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
235
283
  # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
236
284
  # number_to_human_size(524288000, precision: 5) # => "500 MB"
237
285
  def number_to_human_size(number, options = {})
@@ -239,7 +287,7 @@ module ActiveSupport
239
287
  end
240
288
 
241
289
  # Pretty prints (formats and approximates) a number in a way it
242
- # is more readable by humans (eg.: 1200000000 becomes "1.2
290
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
243
291
  # Billion"). This is useful for numbers that can get very large
244
292
  # (and too hard to read).
245
293
  #
@@ -247,7 +295,7 @@ module ActiveSupport
247
295
  # size.
248
296
  #
249
297
  # You can also define your own unit-quantifier names if you want
250
- # to use other decimal units (eg.: 1500 becomes "1.5
298
+ # to use other decimal units (e.g.: 1500 becomes "1.5
251
299
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
252
300
  # define a wide range of unit quantifiers, even fractional ones
253
301
  # (centi, deci, mili, etc).
@@ -258,8 +306,10 @@ module ActiveSupport
258
306
  # (defaults to current locale).
259
307
  # * <tt>:precision</tt> - Sets the precision of the number
260
308
  # (defaults to 3).
261
- # * <tt>:significant</tt> - If +true+, precision will be the #
262
- # of significant_digits. If +false+, the # of fractional
309
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
310
+ # (defaults to :default. See BigDecimal::mode)
311
+ # * <tt>:significant</tt> - If +true+, precision will be the number
312
+ # of significant_digits. If +false+, the number of fractional
263
313
  # digits (defaults to +true+)
264
314
  # * <tt>:separator</tt> - Sets the separator between the
265
315
  # fractional and integer digits (defaults to ".").
@@ -295,6 +345,8 @@ module ActiveSupport
295
345
  # number_to_human(1234567890123456789) # => "1230 Quadrillion"
296
346
  # number_to_human(489939, precision: 2) # => "490 Thousand"
297
347
  # number_to_human(489939, precision: 4) # => "489.9 Thousand"
348
+ # number_to_human(489939, precision: 2
349
+ # , round_mode: :down) # => "480 Thousand"
298
350
  # number_to_human(1234567, precision: 4,
299
351
  # significant: false) # => "1.2346 Million"
300
352
  # number_to_human(1234567, precision: 1,
@@ -1,9 +1,12 @@
1
- require 'active_support/core_ext/hash/deep_merge'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/deep_merge"
4
+ require "active_support/core_ext/symbol/starts_ends_with"
2
5
 
3
6
  module ActiveSupport
4
7
  class OptionMerger #:nodoc:
5
8
  instance_methods.each do |method|
6
- undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
9
+ undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
7
10
  end
8
11
 
9
12
  def initialize(context, options)
@@ -12,14 +15,32 @@ module ActiveSupport
12
15
 
13
16
  private
14
17
  def method_missing(method, *arguments, &block)
18
+ options = nil
15
19
  if arguments.first.is_a?(Proc)
16
20
  proc = arguments.pop
17
21
  arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
22
+ elsif arguments.last.respond_to?(:to_hash)
23
+ options = @options.deep_merge(arguments.pop)
18
24
  else
19
- arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
25
+ options = @options
20
26
  end
21
27
 
22
- @context.__send__(method, *arguments, &block)
28
+ invoke_method(method, arguments, options, &block)
29
+ end
30
+
31
+ if RUBY_VERSION >= "2.7"
32
+ def invoke_method(method, arguments, options, &block)
33
+ if options
34
+ @context.__send__(method, *arguments, **options, &block)
35
+ else
36
+ @context.__send__(method, *arguments, &block)
37
+ end
38
+ end
39
+ else
40
+ def invoke_method(method, arguments, options, &block)
41
+ arguments << options.dup if options
42
+ @context.__send__(method, *arguments, &block)
43
+ end
23
44
  end
24
45
  end
25
46
  end
@@ -1,11 +1,13 @@
1
- require 'yaml'
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
2
4
 
3
5
  YAML.add_builtin_type("omap") do |type, val|
4
- ActiveSupport::OrderedHash[val.map{ |v| v.to_a.first }]
6
+ ActiveSupport::OrderedHash[val.map { |v| v.to_a.first }]
5
7
  end
6
8
 
7
9
  module ActiveSupport
8
- # <tt>ActiveSupport::OrderedHash</tt> implements a hash that preserves
10
+ # DEPRECATED: <tt>ActiveSupport::OrderedHash</tt> implements a hash that preserves
9
11
  # insertion order.
10
12
  #
11
13
  # oh = ActiveSupport::OrderedHash.new
@@ -14,7 +16,7 @@ module ActiveSupport
14
16
  # oh.keys # => [:a, :b], this order is guaranteed
15
17
  #
16
18
  # Also, maps the +omap+ feature for YAML files
17
- # (See http://yaml.org/type/omap.html) to support ordered items
19
+ # (See https://yaml.org/type/omap.html) to support ordered items
18
20
  # when loading from yaml.
19
21
  #
20
22
  # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
@@ -25,7 +27,7 @@ module ActiveSupport
25
27
  end
26
28
 
27
29
  def encode_with(coder)
28
- coder.represent_seq '!omap', map { |k,v| { k => v } }
30
+ coder.represent_seq "!omap", map { |k, v| { k => v } }
29
31
  end
30
32
 
31
33
  def select(*args, &block)