activesupport 4.2.11.1 → 6.0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (263) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -411
  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 +48 -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 +58 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +95 -91
  13. data/lib/active_support/cache/memory_store.rb +39 -36
  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 +75 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +331 -217
  19. data/lib/active_support/callbacks.rb +650 -592
  20. data/lib/active_support/concern.rb +35 -6
  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 +13 -14
  24. data/lib/active_support/core_ext/array/access.rb +41 -1
  25. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  26. data/lib/active_support/core_ext/array/extract.rb +21 -0
  27. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  28. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  29. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  30. data/lib/active_support/core_ext/array/prepend_and_append.rb +4 -6
  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 +3 -1
  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 +45 -31
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +20 -6
  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 +25 -23
  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 +154 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -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 +7 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +114 -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/compact.rb +4 -23
  61. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  63. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  64. data/lib/active_support/core_ext/hash/except.rb +12 -9
  65. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  66. data/lib/active_support/core_ext/hash/keys.rb +19 -42
  67. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  68. data/lib/active_support/core_ext/hash/slice.rb +5 -27
  69. data/lib/active_support/core_ext/hash/transform_values.rb +4 -22
  70. data/lib/active_support/core_ext/hash.rb +10 -9
  71. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  72. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  73. data/lib/active_support/core_ext/integer/time.rb +11 -18
  74. data/lib/active_support/core_ext/integer.rb +5 -3
  75. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  76. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  78. data/lib/active_support/core_ext/kernel.rb +5 -5
  79. data/lib/active_support/core_ext/load_error.rb +3 -22
  80. data/lib/active_support/core_ext/marshal.rb +8 -8
  81. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  82. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  83. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +46 -46
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  87. data/lib/active_support/core_ext/module/delegation.rb +133 -30
  88. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  89. data/lib/active_support/core_ext/module/introspection.rb +44 -19
  90. data/lib/active_support/core_ext/module/reachable.rb +5 -7
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -11
  94. data/lib/active_support/core_ext/name_error.rb +22 -2
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +129 -136
  97. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  99. data/lib/active_support/core_ext/numeric.rb +5 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  101. data/lib/active_support/core_ext/object/blank.rb +27 -3
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  104. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  105. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +51 -20
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  110. data/lib/active_support/core_ext/object/try.rb +81 -23
  111. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  112. data/lib/active_support/core_ext/object.rb +14 -13
  113. data/lib/active_support/core_ext/range/compare_range.rb +76 -0
  114. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  115. data/lib/active_support/core_ext/range/each.rb +18 -17
  116. data/lib/active_support/core_ext/range/include_range.rb +7 -21
  117. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  118. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  119. data/lib/active_support/core_ext/range.rb +7 -4
  120. data/lib/active_support/core_ext/regexp.rb +2 -0
  121. data/lib/active_support/core_ext/securerandom.rb +45 -0
  122. data/lib/active_support/core_ext/string/access.rb +16 -6
  123. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  124. data/lib/active_support/core_ext/string/conversions.rb +7 -4
  125. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  126. data/lib/active_support/core_ext/string/filters.rb +48 -6
  127. data/lib/active_support/core_ext/string/indent.rb +6 -4
  128. data/lib/active_support/core_ext/string/inflections.rb +66 -24
  129. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  130. data/lib/active_support/core_ext/string/multibyte.rb +16 -7
  131. data/lib/active_support/core_ext/string/output_safety.rb +93 -40
  132. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  133. data/lib/active_support/core_ext/string/strip.rb +6 -5
  134. data/lib/active_support/core_ext/string/zones.rb +4 -2
  135. data/lib/active_support/core_ext/string.rb +15 -13
  136. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  137. data/lib/active_support/core_ext/time/calculations.rb +115 -52
  138. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  139. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  140. data/lib/active_support/core_ext/time/zones.rb +41 -7
  141. data/lib/active_support/core_ext/time.rb +7 -6
  142. data/lib/active_support/core_ext/uri.rb +6 -7
  143. data/lib/active_support/core_ext.rb +3 -1
  144. data/lib/active_support/current_attributes.rb +203 -0
  145. data/lib/active_support/dependencies/autoload.rb +2 -0
  146. data/lib/active_support/dependencies/interlock.rb +57 -0
  147. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  148. data/lib/active_support/dependencies.rb +208 -166
  149. data/lib/active_support/deprecation/behaviors.rb +44 -11
  150. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  151. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  152. data/lib/active_support/deprecation/method_wrappers.rb +61 -21
  153. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  154. data/lib/active_support/deprecation/reporting.rb +32 -12
  155. data/lib/active_support/deprecation.rb +12 -9
  156. data/lib/active_support/descendants_tracker.rb +57 -9
  157. data/lib/active_support/digest.rb +20 -0
  158. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  159. data/lib/active_support/duration/iso8601_serializer.rb +53 -0
  160. data/lib/active_support/duration.rb +315 -40
  161. data/lib/active_support/encrypted_configuration.rb +45 -0
  162. data/lib/active_support/encrypted_file.rb +100 -0
  163. data/lib/active_support/evented_file_update_checker.rb +234 -0
  164. data/lib/active_support/execution_wrapper.rb +129 -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/gem_version.rb +6 -4
  168. data/lib/active_support/gzip.rb +7 -5
  169. data/lib/active_support/hash_with_indifferent_access.rb +129 -30
  170. data/lib/active_support/i18n.rb +9 -6
  171. data/lib/active_support/i18n_railtie.rb +50 -14
  172. data/lib/active_support/inflections.rb +13 -11
  173. data/lib/active_support/inflector/inflections.rb +58 -13
  174. data/lib/active_support/inflector/methods.rb +159 -145
  175. data/lib/active_support/inflector/transliterate.rb +84 -34
  176. data/lib/active_support/inflector.rb +7 -5
  177. data/lib/active_support/json/decoding.rb +32 -30
  178. data/lib/active_support/json/encoding.rb +17 -60
  179. data/lib/active_support/json.rb +4 -2
  180. data/lib/active_support/key_generator.rb +11 -43
  181. data/lib/active_support/lazy_load_hooks.rb +53 -20
  182. data/lib/active_support/locale/en.rb +33 -0
  183. data/lib/active_support/locale/en.yml +2 -0
  184. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  185. data/lib/active_support/log_subscriber.rb +44 -19
  186. data/lib/active_support/logger.rb +9 -23
  187. data/lib/active_support/logger_silence.rb +32 -14
  188. data/lib/active_support/logger_thread_safe_level.rb +32 -8
  189. data/lib/active_support/message_encryptor.rb +166 -53
  190. data/lib/active_support/message_verifier.rb +149 -16
  191. data/lib/active_support/messages/metadata.rb +72 -0
  192. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  193. data/lib/active_support/messages/rotator.rb +56 -0
  194. data/lib/active_support/multibyte/chars.rb +56 -63
  195. data/lib/active_support/multibyte/unicode.rb +56 -290
  196. data/lib/active_support/multibyte.rb +4 -2
  197. data/lib/active_support/notifications/fanout.rb +109 -22
  198. data/lib/active_support/notifications/instrumenter.rb +107 -16
  199. data/lib/active_support/notifications.rb +51 -10
  200. data/lib/active_support/number_helper/number_converter.rb +16 -15
  201. data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -15
  202. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  203. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -10
  204. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -9
  205. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  206. data/lib/active_support/number_helper/number_to_phone_converter.rb +15 -5
  207. data/lib/active_support/number_helper/number_to_rounded_converter.rb +25 -57
  208. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  209. data/lib/active_support/number_helper.rb +105 -68
  210. data/lib/active_support/option_merger.rb +24 -4
  211. data/lib/active_support/ordered_hash.rb +7 -5
  212. data/lib/active_support/ordered_options.rb +27 -5
  213. data/lib/active_support/parameter_filter.rb +128 -0
  214. data/lib/active_support/per_thread_registry.rb +9 -4
  215. data/lib/active_support/proxy_object.rb +2 -0
  216. data/lib/active_support/rails.rb +10 -8
  217. data/lib/active_support/railtie.rb +43 -9
  218. data/lib/active_support/reloader.rb +130 -0
  219. data/lib/active_support/rescuable.rb +108 -53
  220. data/lib/active_support/security_utils.rb +15 -11
  221. data/lib/active_support/string_inquirer.rb +11 -4
  222. data/lib/active_support/subscriber.rb +74 -30
  223. data/lib/active_support/tagged_logging.rb +25 -13
  224. data/lib/active_support/test_case.rb +107 -44
  225. data/lib/active_support/testing/assertions.rb +151 -20
  226. data/lib/active_support/testing/autorun.rb +4 -2
  227. data/lib/active_support/testing/constant_lookup.rb +2 -1
  228. data/lib/active_support/testing/declarative.rb +3 -1
  229. data/lib/active_support/testing/deprecation.rb +13 -10
  230. data/lib/active_support/testing/file_fixtures.rb +38 -0
  231. data/lib/active_support/testing/isolation.rb +35 -26
  232. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  233. data/lib/active_support/testing/parallelization.rb +134 -0
  234. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  235. data/lib/active_support/testing/stream.rb +43 -0
  236. data/lib/active_support/testing/tagged_logging.rb +3 -1
  237. data/lib/active_support/testing/time_helpers.rb +84 -20
  238. data/lib/active_support/time.rb +14 -12
  239. data/lib/active_support/time_with_zone.rb +179 -39
  240. data/lib/active_support/values/time_zone.rb +203 -63
  241. data/lib/active_support/version.rb +3 -1
  242. data/lib/active_support/xml_mini/jdom.rb +116 -115
  243. data/lib/active_support/xml_mini/libxml.rb +16 -13
  244. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  245. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  246. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  247. data/lib/active_support/xml_mini/rexml.rb +11 -9
  248. data/lib/active_support/xml_mini.rb +38 -46
  249. data/lib/active_support.rb +13 -11
  250. metadata +84 -26
  251. data/lib/active_support/concurrency/latch.rb +0 -27
  252. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  253. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  254. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  255. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  256. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  257. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  258. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  259. data/lib/active_support/core_ext/object/itself.rb +0 -15
  260. data/lib/active_support/core_ext/struct.rb +0 -6
  261. data/lib/active_support/core_ext/thread.rb +0 -86
  262. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  263. 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,35 @@ 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 BigDecimal === rounded_number && rounded_number.finite?
24
+ s = rounded_number.to_s("F")
25
+ s << "0" * precision
26
+ a, b = s.split(".", 2)
27
+ a << "."
28
+ a << b[0, precision]
29
+ else
30
+ "%00.#{precision}f" % rounded_number
31
+ end
24
32
  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
33
+ formatted_string = rounded_number
28
34
  end
29
35
 
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
36
  delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
40
37
  format_number(delimited_number)
41
38
  end
42
39
 
43
40
  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
41
  def strip_insignificant_zeros
66
42
  options[:strip_insignificant_zeros]
67
43
  end
@@ -69,19 +45,11 @@ module ActiveSupport
69
45
  def format_number(number)
70
46
  if strip_insignificant_zeros
71
47
  escaped_separator = Regexp.escape(options[:separator])
72
- number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
48
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, "")
73
49
  else
74
50
  number
75
51
  end
76
52
  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
53
  end
86
54
  end
87
55
  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
@@ -1,9 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/dependencies/autoload"
4
+
1
5
  module ActiveSupport
2
6
  module NumberHelper
3
7
  extend ActiveSupport::Autoload
4
8
 
5
9
  eager_autoload do
6
10
  autoload :NumberConverter
11
+ autoload :RoundingHelper
7
12
  autoload :NumberToRoundedConverter
8
13
  autoload :NumberToDelimitedConverter
9
14
  autoload :NumberToHumanConverter
@@ -15,7 +20,7 @@ module ActiveSupport
15
20
 
16
21
  extend self
17
22
 
18
- # Formats a +number+ into a US phone number (e.g., (555)
23
+ # Formats a +number+ into a phone number (US by default e.g., (555)
19
24
  # 123-9876). You can customize the format in the +options+ hash.
20
25
  #
21
26
  # ==== Options
@@ -27,19 +32,26 @@ module ActiveSupport
27
32
  # end of the generated number.
28
33
  # * <tt>:country_code</tt> - Sets the country code for the phone
29
34
  # number.
35
+ # * <tt>:pattern</tt> - Specifies how the number is divided into three
36
+ # groups with the custom regexp to override the default format.
30
37
  # ==== Examples
31
38
  #
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
39
+ # number_to_phone(5551234) # => "555-1234"
40
+ # number_to_phone('5551234') # => "555-1234"
41
+ # number_to_phone(1235551234) # => "123-555-1234"
42
+ # number_to_phone(1235551234, area_code: true) # => "(123) 555-1234"
43
+ # number_to_phone(1235551234, delimiter: ' ') # => "123 555 1234"
44
+ # number_to_phone(1235551234, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
45
+ # number_to_phone(1235551234, country_code: 1) # => "+1-123-555-1234"
46
+ # number_to_phone('123a456') # => "123a456"
40
47
  #
41
48
  # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
42
- # # => +1.123.555.1234 x 1343
49
+ # # => "+1.123.555.1234 x 1343"
50
+ #
51
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
52
+ # # => "(755) 6123-4567"
53
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
54
+ # # => "133-1234-5678"
43
55
  def number_to_phone(number, options = {})
44
56
  NumberToPhoneConverter.convert(number, options)
45
57
  end
@@ -47,6 +59,14 @@ module ActiveSupport
47
59
  # Formats a +number+ into a currency string (e.g., $13.65). You
48
60
  # can customize the format in the +options+ hash.
49
61
  #
62
+ # The currency unit and number formatting of the current locale will be used
63
+ # unless otherwise specified in the provided options. No currency conversion
64
+ # is performed. If the user is given a way to change their locale, they will
65
+ # also be able to change the relative value of the currency displayed with
66
+ # this helper. If your application will ever support multiple locales, you
67
+ # may want to specify a constant <tt>:locale</tt> option or consider
68
+ # using a library capable of currency conversion.
69
+ #
50
70
  # ==== Options
51
71
  #
52
72
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
@@ -63,25 +83,34 @@ 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"
85
114
  def number_to_currency(number, options = {})
86
115
  NumberToCurrencyConverter.convert(number, options)
87
116
  end
@@ -94,9 +123,9 @@ module ActiveSupport
94
123
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
95
124
  # (defaults to current locale).
96
125
  # * <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
126
+ # (defaults to 3). Keeps the number's precision if +nil+.
127
+ # * <tt>:significant</tt> - If +true+, precision will be the number
128
+ # of significant_digits. If +false+, the number of fractional
100
129
  # digits (defaults to +false+).
101
130
  # * <tt>:separator</tt> - Sets the separator between the
102
131
  # fractional and integer digits (defaults to ".").
@@ -110,14 +139,15 @@ module ActiveSupport
110
139
  #
111
140
  # ==== Examples
112
141
  #
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 %
142
+ # number_to_percentage(100) # => "100.000%"
143
+ # number_to_percentage('98') # => "98.000%"
144
+ # number_to_percentage(100, precision: 0) # => "100%"
145
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
146
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
147
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
148
+ # number_to_percentage(1000, precision: nil) # => "1000%"
149
+ # number_to_percentage('98a') # => "98a%"
150
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
121
151
  def number_to_percentage(number, options = {})
122
152
  NumberToPercentageConverter.convert(number, options)
123
153
  end
@@ -134,19 +164,25 @@ module ActiveSupport
134
164
  # to ",").
135
165
  # * <tt>:separator</tt> - Sets the separator between the
136
166
  # fractional and integer digits (defaults to ".").
167
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
168
+ # deriving the placement of delimiter. Helpful when using currency formats
169
+ # like INR.
137
170
  #
138
171
  # ==== Examples
139
172
  #
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
173
+ # number_to_delimited(12345678) # => "12,345,678"
174
+ # number_to_delimited('123456') # => "123,456"
175
+ # number_to_delimited(12345678.05) # => "12,345,678.05"
176
+ # number_to_delimited(12345678, delimiter: '.') # => "12.345.678"
177
+ # number_to_delimited(12345678, delimiter: ',') # => "12,345,678"
178
+ # number_to_delimited(12345678.05, separator: ' ') # => "12,345,678 05"
179
+ # number_to_delimited(12345678.05, locale: :fr) # => "12 345 678,05"
180
+ # number_to_delimited('112a') # => "112a"
148
181
  # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
149
- # # => 98 765 432,98
182
+ # # => "98 765 432,98"
183
+ # number_to_delimited("123456.78",
184
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
185
+ # # => "1,23,456.78"
150
186
  def number_to_delimited(number, options = {})
151
187
  NumberToDelimitedConverter.convert(number, options)
152
188
  end
@@ -161,9 +197,9 @@ module ActiveSupport
161
197
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
162
198
  # (defaults to current locale).
163
199
  # * <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
200
+ # (defaults to 3). Keeps the number's precision if +nil+.
201
+ # * <tt>:significant</tt> - If +true+, precision will be the number
202
+ # of significant_digits. If +false+, the number of fractional
167
203
  # digits (defaults to +false+).
168
204
  # * <tt>:separator</tt> - Sets the separator between the
169
205
  # fractional and integer digits (defaults to ".").
@@ -175,21 +211,22 @@ module ActiveSupport
175
211
  #
176
212
  # ==== Examples
177
213
  #
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
214
+ # number_to_rounded(111.2345) # => "111.235"
215
+ # number_to_rounded(111.2345, precision: 2) # => "111.23"
216
+ # number_to_rounded(13, precision: 5) # => "13.00000"
217
+ # number_to_rounded(389.32314, precision: 0) # => "389"
218
+ # number_to_rounded(111.2345, significant: true) # => "111"
219
+ # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
220
+ # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
221
+ # number_to_rounded(13, precision: nil) # => "13"
222
+ # number_to_rounded(111.234, locale: :fr) # => "111,234"
186
223
  #
187
224
  # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
188
- # # => 13
225
+ # # => "13"
189
226
  #
190
- # number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
227
+ # number_to_rounded(389.32314, precision: 4, significant: true) # => "389.3"
191
228
  # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
192
- # # => 1.111,23
229
+ # # => "1.111,23"
193
230
  def number_to_rounded(number, options = {})
194
231
  NumberToRoundedConverter.convert(number, options)
195
232
  end
@@ -208,8 +245,8 @@ module ActiveSupport
208
245
  # (defaults to current locale).
209
246
  # * <tt>:precision</tt> - Sets the precision of the number
210
247
  # (defaults to 3).
211
- # * <tt>:significant</tt> - If +true+, precision will be the #
212
- # of significant_digits. If +false+, the # of fractional
248
+ # * <tt>:significant</tt> - If +true+, precision will be the number
249
+ # of significant_digits. If +false+, the number of fractional
213
250
  # digits (defaults to +true+)
214
251
  # * <tt>:separator</tt> - Sets the separator between the
215
252
  # fractional and integer digits (defaults to ".").
@@ -218,20 +255,20 @@ module ActiveSupport
218
255
  # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
219
256
  # insignificant zeros after the decimal separator (defaults to
220
257
  # +true+)
221
- # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
222
- # prefix (defaults to :binary)
223
258
  #
224
259
  # ==== Examples
225
260
  #
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
261
+ # number_to_human_size(123) # => "123 Bytes"
262
+ # number_to_human_size(1234) # => "1.21 KB"
263
+ # number_to_human_size(12345) # => "12.1 KB"
264
+ # number_to_human_size(1234567) # => "1.18 MB"
265
+ # number_to_human_size(1234567890) # => "1.15 GB"
266
+ # number_to_human_size(1234567890123) # => "1.12 TB"
267
+ # number_to_human_size(1234567890123456) # => "1.1 PB"
268
+ # number_to_human_size(1234567890123456789) # => "1.07 EB"
269
+ # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
270
+ # number_to_human_size(483989, precision: 2) # => "470 KB"
271
+ # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
235
272
  # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
236
273
  # number_to_human_size(524288000, precision: 5) # => "500 MB"
237
274
  def number_to_human_size(number, options = {})
@@ -258,8 +295,8 @@ module ActiveSupport
258
295
  # (defaults to current locale).
259
296
  # * <tt>:precision</tt> - Sets the precision of the number
260
297
  # (defaults to 3).
261
- # * <tt>:significant</tt> - If +true+, precision will be the #
262
- # of significant_digits. If +false+, the # of fractional
298
+ # * <tt>:significant</tt> - If +true+, precision will be the number
299
+ # of significant_digits. If +false+, the number of fractional
263
300
  # digits (defaults to +true+)
264
301
  # * <tt>:separator</tt> - Sets the separator between the
265
302
  # fractional and integer digits (defaults to ".").
@@ -1,9 +1,11 @@
1
- require 'active_support/core_ext/hash/deep_merge'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/deep_merge"
2
4
 
3
5
  module ActiveSupport
4
6
  class OptionMerger #:nodoc:
5
7
  instance_methods.each do |method|
6
- undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
8
+ undef_method(method) if !/^(__|instance_eval|class|object_id)/.match?(method)
7
9
  end
8
10
 
9
11
  def initialize(context, options)
@@ -12,14 +14,32 @@ module ActiveSupport
12
14
 
13
15
  private
14
16
  def method_missing(method, *arguments, &block)
17
+ options = nil
15
18
  if arguments.first.is_a?(Proc)
16
19
  proc = arguments.pop
17
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)
18
23
  else
19
- arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
24
+ options = @options
20
25
  end
21
26
 
22
- @context.__send__(method, *arguments, &block)
27
+ invoke_method(method, arguments, options, &block)
28
+ end
29
+
30
+ if RUBY_VERSION >= "2.7"
31
+ def invoke_method(method, arguments, options, &block)
32
+ if options
33
+ @context.__send__(method, *arguments, **options, &block)
34
+ else
35
+ @context.__send__(method, *arguments, &block)
36
+ end
37
+ end
38
+ else
39
+ def invoke_method(method, arguments, options, &block)
40
+ arguments << options if options
41
+ @context.__send__(method, *arguments, &block)
42
+ end
23
43
  end
24
44
  end
25
45
  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)
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/blank"
4
+
1
5
  module ActiveSupport
2
6
  # Usually key value pairs are handled something like this:
3
7
  #
@@ -6,6 +10,7 @@ module ActiveSupport
6
10
  # h[:girl] = 'Mary'
7
11
  # h[:boy] # => 'John'
8
12
  # h[:girl] # => 'Mary'
13
+ # h[:dog] # => nil
9
14
  #
10
15
  # Using +OrderedOptions+, the above code could be reduced to:
11
16
  #
@@ -14,6 +19,13 @@ module ActiveSupport
14
19
  # h.girl = 'Mary'
15
20
  # h.boy # => 'John'
16
21
  # h.girl # => 'Mary'
22
+ # h.dog # => nil
23
+ #
24
+ # To raise an exception when the value is blank, append a
25
+ # bang to the key name, like:
26
+ #
27
+ # h.dog! # => raises KeyError: :dog is blank
28
+ #
17
29
  class OrderedOptions < Hash
18
30
  alias_method :_get, :[] # preserve the original #[] method
19
31
  protected :_get # make it protected
@@ -27,17 +39,27 @@ module ActiveSupport
27
39
  end
28
40
 
29
41
  def method_missing(name, *args)
30
- name_string = name.to_s
31
- if name_string.chomp!('=')
42
+ name_string = +name.to_s
43
+ if name_string.chomp!("=")
32
44
  self[name_string] = args.first
33
45
  else
34
- self[name]
46
+ bangs = name_string.chomp!("!")
47
+
48
+ if bangs
49
+ self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
50
+ else
51
+ self[name_string]
52
+ end
35
53
  end
36
54
  end
37
55
 
38
56
  def respond_to_missing?(name, include_private)
39
57
  true
40
58
  end
59
+
60
+ def extractable_options?
61
+ true
62
+ end
41
63
  end
42
64
 
43
65
  # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
@@ -52,9 +74,9 @@ module ActiveSupport
52
74
  def initialize(parent = nil)
53
75
  if parent.kind_of?(OrderedOptions)
54
76
  # use the faster _get when dealing with OrderedOptions
55
- super() { |h,k| parent._get(k) }
77
+ super() { |h, k| parent._get(k) }
56
78
  elsif parent
57
- super() { |h,k| parent[k] }
79
+ super() { |h, k| parent[k] }
58
80
  else
59
81
  super()
60
82
  end