activesupport 5.0.0 → 6.1.0

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 (268) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +343 -590
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -4
  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 +11 -5
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +45 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +81 -79
  13. data/lib/active_support/cache/memory_store.rb +69 -41
  14. data/lib/active_support/cache/null_store.rb +11 -4
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +74 -37
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +332 -161
  19. data/lib/active_support/callbacks.rb +657 -586
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +59 -19
  23. data/lib/active_support/configurable.rb +15 -17
  24. data/lib/active_support/configuration_file.rb +46 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +20 -18
  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 +3 -1
  30. data/lib/active_support/core_ext/array/inquiry.rb +3 -1
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +9 -7
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +6 -6
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -26
  39. data/lib/active_support/core_ext/class.rb +4 -2
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +3 -1
  42. data/lib/active_support/core_ext/date/calculations.rb +16 -13
  43. data/lib/active_support/core_ext/date/conversions.rb +23 -21
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +7 -5
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +82 -53
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -5
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +9 -9
  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 +3 -1
  51. data/lib/active_support/core_ext/date_time/calculations.rb +23 -11
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +15 -2
  53. data/lib/active_support/core_ext/date_time/conversions.rb +14 -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 +165 -29
  58. data/lib/active_support/core_ext/file/atomic.rb +7 -5
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +40 -39
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -2
  65. data/lib/active_support/core_ext/hash/keys.rb +9 -36
  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 +3 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +3 -1
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -4
  77. data/lib/active_support/core_ext/load_error.rb +2 -23
  78. data/lib/active_support/core_ext/marshal.rb +6 -2
  79. data/lib/active_support/core_ext/module/aliasing.rb +5 -48
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +7 -5
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +53 -59
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +31 -24
  84. data/lib/active_support/core_ext/module/concerning.rb +16 -11
  85. data/lib/active_support/core_ext/module/delegation.rb +159 -44
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -26
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +13 -12
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +129 -134
  94. data/lib/active_support/core_ext/numeric/time.rb +18 -26
  95. data/lib/active_support/core_ext/numeric.rb +5 -4
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +14 -2
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +4 -2
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -62
  101. data/lib/active_support/core_ext/object/inclusion.rb +3 -1
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +42 -15
  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 +20 -8
  107. data/lib/active_support/core_ext/object/with_options.rb +15 -2
  108. data/lib/active_support/core_ext/object.rb +14 -12
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  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 +28 -6
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +47 -4
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +78 -29
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +10 -5
  126. data/lib/active_support/core_ext/string/output_safety.rb +86 -31
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  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 +117 -45
  135. data/lib/active_support/core_ext/time/compatibility.rb +13 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +18 -12
  137. data/lib/active_support/core_ext/time/zones.rb +9 -7
  138. data/lib/active_support/core_ext/time.rb +7 -5
  139. data/lib/active_support/core_ext/uri.rb +12 -7
  140. data/lib/active_support/core_ext.rb +3 -2
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +208 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +7 -1
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  146. data/lib/active_support/dependencies.rb +172 -98
  147. data/lib/active_support/deprecation/behaviors.rb +45 -13
  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 +32 -17
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +35 -7
  153. data/lib/active_support/deprecation/reporting.rb +61 -16
  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 +20 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +67 -66
  158. data/lib/active_support/duration/iso8601_serializer.rb +25 -17
  159. data/lib/active_support/duration.rb +349 -46
  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 +88 -112
  164. data/lib/active_support/execution_wrapper.rb +25 -13
  165. data/lib/active_support/executor.rb +3 -1
  166. data/lib/active_support/file_update_checker.rb +56 -51
  167. data/lib/active_support/fork_tracker.rb +62 -0
  168. data/lib/active_support/gem_version.rb +4 -2
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +153 -49
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +30 -20
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +28 -15
  175. data/lib/active_support/inflector/methods.rb +120 -109
  176. data/lib/active_support/inflector/transliterate.rb +60 -25
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +30 -29
  179. data/lib/active_support/json/encoding.rb +22 -11
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +6 -36
  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 +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +11 -9
  186. data/lib/active_support/log_subscriber.rb +51 -18
  187. data/lib/active_support/logger.rb +9 -22
  188. data/lib/active_support/logger_silence.rb +14 -21
  189. data/lib/active_support/logger_thread_safe_level.rb +55 -8
  190. data/lib/active_support/message_encryptor.rb +170 -53
  191. data/lib/active_support/message_verifier.rb +91 -20
  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 +24 -78
  196. data/lib/active_support/multibyte/unicode.rb +21 -352
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +121 -19
  199. data/lib/active_support/notifications/instrumenter.rb +78 -14
  200. data/lib/active_support/notifications.rb +80 -12
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -3
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -12
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -13
  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 +5 -4
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +18 -55
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +45 -16
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +6 -4
  213. data/lib/active_support/ordered_options.rb +23 -9
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +7 -5
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +8 -9
  218. data/lib/active_support/railtie.rb +62 -11
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +20 -11
  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 +12 -3
  224. data/lib/active_support/subscriber.rb +77 -23
  225. data/lib/active_support/tagged_logging.rb +52 -17
  226. data/lib/active_support/test_case.rb +106 -29
  227. data/lib/active_support/testing/assertions.rb +144 -8
  228. data/lib/active_support/testing/autorun.rb +5 -10
  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 +4 -2
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +19 -24
  234. data/lib/active_support/testing/method_call_assertions.rb +31 -2
  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 +30 -29
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +125 -24
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +142 -55
  244. data/lib/active_support/values/time_zone.rb +160 -53
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +115 -114
  247. data/lib/active_support/xml_mini/libxml.rb +15 -14
  248. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  249. data/lib/active_support/xml_mini/nokogiri.rb +13 -13
  250. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +44 -42
  253. data/lib/active_support.rb +19 -10
  254. metadata +79 -37
  255. data/lib/active_support/concurrency/latch.rb +0 -19
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/hash/compact.rb +0 -20
  258. data/lib/active_support/core_ext/hash/transform_values.rb +0 -29
  259. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  260. data/lib/active_support/core_ext/kernel/debugger.rb +0 -3
  261. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -3
  262. data/lib/active_support/core_ext/module/qualified_const.rb +0 -70
  263. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  264. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  265. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  266. data/lib/active_support/core_ext/struct.rb +0 -3
  267. data/lib/active_support/core_ext/time/marshal.rb +0 -3
  268. 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 NumberToHumanConverter < NumberConverter # :nodoc:
@@ -9,38 +13,35 @@ module ActiveSupport
9
13
  self.validate_float = true
10
14
 
11
15
  def convert # :nodoc:
16
+ @number = RoundingHelper.new(options).round(number)
12
17
  @number = Float(number)
13
18
 
14
- # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
19
+ # For backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files.
15
20
  unless options.key?(:strip_insignificant_zeros)
16
21
  options[:strip_insignificant_zeros] = true
17
22
  end
18
23
 
19
24
  units = opts[:units]
20
25
  exponent = calculate_exponent(units)
21
- @number = number / (10 ** exponent)
26
+ @number = number / (10**exponent)
22
27
 
23
- until (rounded_number = NumberToRoundedConverter.convert(number, options)) != NumberToRoundedConverter.convert(1000, options)
24
- @number = number / 1000.0
25
- exponent += 3
26
- end
28
+ rounded_number = NumberToRoundedConverter.convert(number, options)
27
29
  unit = determine_unit(units, exponent)
28
- format.gsub('%n'.freeze, rounded_number).gsub('%u'.freeze, unit).strip
30
+ format.gsub("%n", rounded_number).gsub("%u", unit).strip
29
31
  end
30
32
 
31
33
  private
32
-
33
34
  def format
34
- options[:format] || translate_in_locale('human.decimal_units.format')
35
+ options[:format] || translate_in_locale("human.decimal_units.format")
35
36
  end
36
37
 
37
38
  def determine_unit(units, exponent)
38
39
  exp = DECIMAL_UNITS[exponent]
39
40
  case units
40
41
  when Hash
41
- units[exp] || ''
42
+ units[exp] || ""
42
43
  when String, Symbol
43
- I18n.translate("#{units}.#{exp}", :locale => options[:locale], :count => number.to_i)
44
+ I18n.translate("#{units}.#{exp}", locale: options[:locale], count: number.to_i)
44
45
  else
45
46
  translate_in_locale("human.decimal_units.units.#{exp}", count: number.to_i)
46
47
  end
@@ -56,7 +57,7 @@ module ActiveSupport
56
57
  when Hash
57
58
  units
58
59
  when String, Symbol
59
- I18n.translate(units.to_s, :locale => options[:locale], :raise => true)
60
+ I18n.translate(units.to_s, locale: options[:locale], raise: true)
60
61
  when nil
61
62
  translate_in_locale("human.decimal_units.units", raise: true)
62
63
  else
@@ -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 NumberToHumanSizeConverter < NumberConverter #:nodoc:
@@ -7,13 +11,9 @@ module ActiveSupport
7
11
  self.validate_float = true
8
12
 
9
13
  def convert
10
- if opts.key?(:prefix)
11
- ActiveSupport::Deprecation.warn('The :prefix option of `number_to_human_size` is deprecated and will be removed in Rails 5.1 with no replacement.')
12
- end
13
-
14
14
  @number = Float(number)
15
15
 
16
- # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
16
+ # For backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files.
17
17
  unless options.key?(:strip_insignificant_zeros)
18
18
  options[:strip_insignificant_zeros] = true
19
19
  end
@@ -21,24 +21,23 @@ module ActiveSupport
21
21
  if smaller_than_base?
22
22
  number_to_format = number.to_i.to_s
23
23
  else
24
- human_size = number / (base ** exponent)
24
+ human_size = number / (base**exponent)
25
25
  number_to_format = NumberToRoundedConverter.convert(human_size, options)
26
26
  end
27
- conversion_format.gsub('%n'.freeze, number_to_format).gsub('%u'.freeze, unit)
27
+ conversion_format.gsub("%n", number_to_format).gsub("%u", unit)
28
28
  end
29
29
 
30
30
  private
31
-
32
31
  def conversion_format
33
- translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
32
+ translate_number_value_with_default("human.storage_units.format", locale: options[:locale], raise: true)
34
33
  end
35
34
 
36
35
  def unit
37
- translate_number_value_with_default(storage_unit_key, :locale => options[:locale], :count => number.to_i, :raise => true)
36
+ translate_number_value_with_default(storage_unit_key, locale: options[:locale], count: number.to_i, raise: true)
38
37
  end
39
38
 
40
39
  def storage_unit_key
41
- key_end = smaller_than_base? ? 'byte' : STORAGE_UNITS[exponent]
40
+ key_end = smaller_than_base? ? "byte" : STORAGE_UNITS[exponent]
42
41
  "human.storage_units.units.#{key_end}"
43
42
  end
44
43
 
@@ -54,9 +53,8 @@ module ActiveSupport
54
53
  end
55
54
 
56
55
  def base
57
- opts[:prefix] == :si ? 1000 : 1024
56
+ 1024
58
57
  end
59
58
  end
60
59
  end
61
60
  end
62
-
@@ -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 NumberToPercentageConverter < NumberConverter # :nodoc:
@@ -5,7 +9,7 @@ module ActiveSupport
5
9
 
6
10
  def convert
7
11
  rounded_number = NumberToRoundedConverter.convert(number, options)
8
- options[:format].gsub('%n'.freeze, rounded_number)
12
+ options[:format].gsub("%n", rounded_number)
9
13
  end
10
14
  end
11
15
  end
@@ -1,14 +1,17 @@
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 NumberToPhoneConverter < NumberConverter #:nodoc:
4
8
  def convert
5
- str = country_code(opts[:country_code])
9
+ str = country_code(opts[:country_code]).dup
6
10
  str << convert_to_phone_number(number.to_s.strip)
7
11
  str << phone_ext(opts[:extension])
8
12
  end
9
13
 
10
14
  private
11
-
12
15
  def convert_to_phone_number(number)
13
16
  if opts[:area_code]
14
17
  convert_with_area_code(number)
@@ -51,8 +54,6 @@ module ActiveSupport
51
54
  def regexp_pattern(default_pattern)
52
55
  opts.fetch :pattern, default_pattern
53
56
  end
54
-
55
57
  end
56
58
  end
57
59
  end
58
-
@@ -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,40 +9,28 @@ module ActiveSupport
5
9
  self.validate_float = true
6
10
 
7
11
  def convert
8
- precision = options.delete :precision
9
-
10
- if precision
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)
19
14
 
20
- if options.delete(:significant) && precision > 0
21
- digits, rounded_number = digits_and_rounded_number(precision)
15
+ if precision = options[:precision]
16
+ if options[:significant] && precision > 0
17
+ digits = helper.digit_count(rounded_number)
22
18
  precision -= digits
23
19
  precision = 0 if precision < 0 # don't let it be negative
24
- 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
28
20
  end
29
21
 
30
22
  formatted_string =
31
- if BigDecimal === rounded_number && rounded_number.finite?
32
- s = rounded_number.to_s('F')
33
- s << '0'.freeze * precision
34
- a, b = s.split('.'.freeze, 2)
35
- a << '.'.freeze
36
- a << b[0, precision]
37
- else
23
+ if rounded_number.nan? || rounded_number.infinite? || rounded_number == rounded_number.to_i
38
24
  "%00.#{precision}f" % rounded_number
25
+ else
26
+ s = rounded_number.to_s("F")
27
+ s << "0" * precision
28
+ a, b = s.split(".", 2)
29
+ a << "."
30
+ a << b[0, precision]
39
31
  end
40
32
  else
41
- formatted_string = number
33
+ formatted_string = rounded_number
42
34
  end
43
35
 
44
36
  delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
@@ -46,27 +38,6 @@ module ActiveSupport
46
38
  end
47
39
 
48
40
  private
49
-
50
- def digits_and_rounded_number(precision)
51
- if zero?
52
- [1, 0]
53
- else
54
- digits = digit_count(number)
55
- multiplier = 10 ** (digits - precision)
56
- rounded_number = calculate_rounded_number(multiplier)
57
- digits = digit_count(rounded_number) # After rounding, the number of digits may have changed
58
- [digits, rounded_number]
59
- end
60
- end
61
-
62
- def calculate_rounded_number(multiplier)
63
- (number / BigDecimal.new(multiplier.to_f.to_s)).round * multiplier
64
- end
65
-
66
- def digit_count(number)
67
- number.zero? ? 1 : (Math.log10(absolute_number(number)) + 1).floor
68
- end
69
-
70
41
  def strip_insignificant_zeros
71
42
  options[:strip_insignificant_zeros]
72
43
  end
@@ -74,19 +45,11 @@ module ActiveSupport
74
45
  def format_number(number)
75
46
  if strip_insignificant_zeros
76
47
  escaped_separator = Regexp.escape(options[:separator])
77
- 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/, "")
78
49
  else
79
50
  number
80
51
  end
81
52
  end
82
-
83
- def absolute_number(number)
84
- number.respond_to?(:abs) ? number.abs : number.to_d.abs
85
- end
86
-
87
- def zero?
88
- number.respond_to?(:zero?) ? number.zero? : number.to_d.zero?
89
- end
90
53
  end
91
54
  end
92
55
  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))
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
@@ -45,7 +48,7 @@ module ActiveSupport
45
48
  #
46
49
  # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
47
50
  # # => "(755) 6123-4567"
48
- # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/))
51
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
49
52
  # # => "133-1234-5678"
50
53
  def number_to_phone(number, options = {})
51
54
  NumberToPhoneConverter.convert(number, options)
@@ -68,6 +71,8 @@ module ActiveSupport
68
71
  # (defaults to current locale).
69
72
  # * <tt>:precision</tt> - Sets the level of precision (defaults
70
73
  # to 2).
74
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
75
+ # (defaults to :default. See BigDecimal::mode)
71
76
  # * <tt>:unit</tt> - Sets the denomination of the currency
72
77
  # (defaults to "$").
73
78
  # * <tt>:separator</tt> - Sets the separator between the units
@@ -78,10 +83,13 @@ module ActiveSupport
78
83
  # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
79
84
  # currency, and <tt>%n</tt> for the number.
80
85
  # * <tt>:negative_format</tt> - Sets the format for negative
81
- # numbers (defaults to prepending an hyphen to the formatted
86
+ # numbers (defaults to prepending a hyphen to the formatted
82
87
  # number given by <tt>:format</tt>). Accepts the same fields
83
88
  # than <tt>:format</tt>, except <tt>%n</tt> is here the
84
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+).
85
93
  #
86
94
  # ==== Examples
87
95
  #
@@ -91,12 +99,20 @@ module ActiveSupport
91
99
  # number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €"
92
100
  # number_to_currency('123a456') # => "$123a456"
93
101
  #
102
+ # number_to_currency("123a456", raise: true) # => InvalidNumberError
103
+ #
104
+ # number_to_currency(-0.456789, precision: 0)
105
+ # # => "$0"
94
106
  # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
95
107
  # # => "($1,234,567,890.50)"
96
108
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '')
97
109
  # # => "&pound;1234567890,50"
98
110
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
99
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"
100
116
  def number_to_currency(number, options = {})
101
117
  NumberToCurrencyConverter.convert(number, options)
102
118
  end
@@ -109,7 +125,9 @@ module ActiveSupport
109
125
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
110
126
  # (defaults to current locale).
111
127
  # * <tt>:precision</tt> - Sets the precision of the number
112
- # (defaults to 3). Keeps the number's precision if nil.
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)
113
131
  # * <tt>:significant</tt> - If +true+, precision will be the number
114
132
  # of significant_digits. If +false+, the number of fractional
115
133
  # digits (defaults to +false+).
@@ -125,15 +143,16 @@ module ActiveSupport
125
143
  #
126
144
  # ==== Examples
127
145
  #
128
- # number_to_percentage(100) # => "100.000%"
129
- # number_to_percentage('98') # => "98.000%"
130
- # number_to_percentage(100, precision: 0) # => "100%"
131
- # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
132
- # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
133
- # number_to_percentage(1000, locale: :fr) # => "1000,000%"
134
- # number_to_percentage(1000, precision: nil) # => "1000%"
135
- # number_to_percentage('98a') # => "98a%"
136
- # number_to_percentage(100, format: '%n %') # => "100.000 %"
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%"
137
156
  def number_to_percentage(number, options = {})
138
157
  NumberToPercentageConverter.convert(number, options)
139
158
  end
@@ -183,7 +202,9 @@ module ActiveSupport
183
202
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
184
203
  # (defaults to current locale).
185
204
  # * <tt>:precision</tt> - Sets the precision of the number
186
- # (defaults to 3). Keeps the number's precision if nil.
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)
187
208
  # * <tt>:significant</tt> - If +true+, precision will be the number
188
209
  # of significant_digits. If +false+, the number of fractional
189
210
  # digits (defaults to +false+).
@@ -205,6 +226,7 @@ module ActiveSupport
205
226
  # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
206
227
  # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
207
228
  # number_to_rounded(13, precision: nil) # => "13"
229
+ # number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
208
230
  # number_to_rounded(111.234, locale: :fr) # => "111,234"
209
231
  #
210
232
  # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
@@ -218,7 +240,7 @@ module ActiveSupport
218
240
  end
219
241
 
220
242
  # Formats the bytes in +number+ into a more understandable
221
- # representation (e.g., giving it 1500 yields 1.5 KB). This
243
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
222
244
  # method is useful for reporting file sizes to users. You can
223
245
  # customize the format in the +options+ hash.
224
246
  #
@@ -231,6 +253,8 @@ module ActiveSupport
231
253
  # (defaults to current locale).
232
254
  # * <tt>:precision</tt> - Sets the precision of the number
233
255
  # (defaults to 3).
256
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
257
+ # (defaults to :default. See BigDecimal::mode)
234
258
  # * <tt>:significant</tt> - If +true+, precision will be the number
235
259
  # of significant_digits. If +false+, the number of fractional
236
260
  # digits (defaults to +true+)
@@ -254,6 +278,7 @@ module ActiveSupport
254
278
  # number_to_human_size(1234567890123456789) # => "1.07 EB"
255
279
  # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
256
280
  # number_to_human_size(483989, precision: 2) # => "470 KB"
281
+ # number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
257
282
  # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
258
283
  # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
259
284
  # number_to_human_size(524288000, precision: 5) # => "500 MB"
@@ -262,7 +287,7 @@ module ActiveSupport
262
287
  end
263
288
 
264
289
  # Pretty prints (formats and approximates) a number in a way it
265
- # is more readable by humans (eg.: 1200000000 becomes "1.2
290
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
266
291
  # Billion"). This is useful for numbers that can get very large
267
292
  # (and too hard to read).
268
293
  #
@@ -270,7 +295,7 @@ module ActiveSupport
270
295
  # size.
271
296
  #
272
297
  # You can also define your own unit-quantifier names if you want
273
- # to use other decimal units (eg.: 1500 becomes "1.5
298
+ # to use other decimal units (e.g.: 1500 becomes "1.5
274
299
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
275
300
  # define a wide range of unit quantifiers, even fractional ones
276
301
  # (centi, deci, mili, etc).
@@ -281,6 +306,8 @@ module ActiveSupport
281
306
  # (defaults to current locale).
282
307
  # * <tt>:precision</tt> - Sets the precision of the number
283
308
  # (defaults to 3).
309
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
310
+ # (defaults to :default. See BigDecimal::mode)
284
311
  # * <tt>:significant</tt> - If +true+, precision will be the number
285
312
  # of significant_digits. If +false+, the number of fractional
286
313
  # digits (defaults to +true+)
@@ -318,6 +345,8 @@ module ActiveSupport
318
345
  # number_to_human(1234567890123456789) # => "1230 Quadrillion"
319
346
  # number_to_human(489939, precision: 2) # => "490 Thousand"
320
347
  # number_to_human(489939, precision: 4) # => "489.9 Thousand"
348
+ # number_to_human(489939, precision: 2
349
+ # , round_mode: :down) # => "480 Thousand"
321
350
  # number_to_human(1234567, precision: 4,
322
351
  # significant: false) # => "1.2346 Million"
323
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,7 +1,9 @@
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
@@ -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,5 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/blank"
4
+
1
5
  module ActiveSupport
2
- # Usually key value pairs are handled something like this:
6
+ # +OrderedOptions+ inherits from +Hash+ and provides dynamic accessor methods.
7
+ #
8
+ # With a +Hash+, key-value pairs are typically managed like this:
3
9
  #
4
10
  # h = {}
5
11
  # h[:boy] = 'John'
@@ -8,7 +14,7 @@ module ActiveSupport
8
14
  # h[:girl] # => 'Mary'
9
15
  # h[:dog] # => nil
10
16
  #
11
- # Using +OrderedOptions+, the above code could be reduced to:
17
+ # Using +OrderedOptions+, the above code can be written as:
12
18
  #
13
19
  # h = ActiveSupport::OrderedOptions.new
14
20
  # h.boy = 'John'
@@ -20,7 +26,7 @@ module ActiveSupport
20
26
  # To raise an exception when the value is blank, append a
21
27
  # bang to the key name, like:
22
28
  #
23
- # h.dog! # => raises KeyError: key not found: :dog
29
+ # h.dog! # => raises KeyError: :dog is blank
24
30
  #
25
31
  class OrderedOptions < Hash
26
32
  alias_method :_get, :[] # preserve the original #[] method
@@ -35,14 +41,14 @@ module ActiveSupport
35
41
  end
36
42
 
37
43
  def method_missing(name, *args)
38
- name_string = name.to_s
39
- if name_string.chomp!('=')
44
+ name_string = +name.to_s
45
+ if name_string.chomp!("=")
40
46
  self[name_string] = args.first
41
47
  else
42
- bangs = name_string.chomp!('!')
48
+ bangs = name_string.chomp!("!")
43
49
 
44
50
  if bangs
45
- fetch(name_string.to_sym).presence || raise(KeyError.new("#{name_string} is blank."))
51
+ self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
46
52
  else
47
53
  self[name_string]
48
54
  end
@@ -52,6 +58,14 @@ module ActiveSupport
52
58
  def respond_to_missing?(name, include_private)
53
59
  true
54
60
  end
61
+
62
+ def extractable_options?
63
+ true
64
+ end
65
+
66
+ def inspect
67
+ "#<#{self.class.name} #{super}>"
68
+ end
55
69
  end
56
70
 
57
71
  # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
@@ -66,9 +80,9 @@ module ActiveSupport
66
80
  def initialize(parent = nil)
67
81
  if parent.kind_of?(OrderedOptions)
68
82
  # use the faster _get when dealing with OrderedOptions
69
- super() { |h,k| parent._get(k) }
83
+ super() { |h, k| parent._get(k) }
70
84
  elsif parent
71
- super() { |h,k| parent[k] }
85
+ super() { |h, k| parent[k] }
72
86
  else
73
87
  super()
74
88
  end