activesupport 5.2.7.1 → 6.1.4.6

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -434
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +34 -34
  10. data/lib/active_support/cache/mem_cache_store.rb +39 -24
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +72 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +148 -78
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +70 -3
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  36. data/lib/active_support/core_ext/enumerable.rb +171 -75
  37. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  38. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  39. data/lib/active_support/core_ext/hash/except.rb +2 -2
  40. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  41. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  42. data/lib/active_support/core_ext/hash.rb +1 -2
  43. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  44. data/lib/active_support/core_ext/kernel.rb +0 -1
  45. data/lib/active_support/core_ext/load_error.rb +1 -1
  46. data/lib/active_support/core_ext/marshal.rb +2 -0
  47. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  48. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  49. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  50. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  51. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  52. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  53. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  54. data/lib/active_support/core_ext/module.rb +0 -1
  55. data/lib/active_support/core_ext/name_error.rb +29 -2
  56. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  57. data/lib/active_support/core_ext/numeric.rb +0 -1
  58. data/lib/active_support/core_ext/object/blank.rb +1 -2
  59. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  60. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  61. data/lib/active_support/core_ext/object/json.rb +14 -2
  62. data/lib/active_support/core_ext/object/try.rb +17 -7
  63. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  64. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  65. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  66. data/lib/active_support/core_ext/range/each.rb +0 -1
  67. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  68. data/lib/active_support/core_ext/regexp.rb +8 -5
  69. data/lib/active_support/core_ext/securerandom.rb +23 -3
  70. data/lib/active_support/core_ext/string/access.rb +5 -16
  71. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  72. data/lib/active_support/core_ext/string/filters.rb +42 -1
  73. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  74. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  75. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  76. data/lib/active_support/core_ext/string/output_safety.rb +70 -41
  77. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  78. data/lib/active_support/core_ext/string/strip.rb +3 -1
  79. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  80. data/lib/active_support/core_ext/symbol.rb +3 -0
  81. data/lib/active_support/core_ext/time/calculations.rb +51 -3
  82. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  83. data/lib/active_support/core_ext/uri.rb +6 -1
  84. data/lib/active_support/core_ext.rb +1 -1
  85. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  86. data/lib/active_support/current_attributes.rb +16 -2
  87. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/deprecation/behaviors.rb +16 -3
  90. data/lib/active_support/deprecation/disallowed.rb +56 -0
  91. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  92. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  93. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  94. data/lib/active_support/deprecation/reporting.rb +50 -7
  95. data/lib/active_support/deprecation.rb +6 -1
  96. data/lib/active_support/descendants_tracker.rb +59 -9
  97. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  98. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  99. data/lib/active_support/duration.rb +78 -30
  100. data/lib/active_support/encrypted_configuration.rb +0 -4
  101. data/lib/active_support/encrypted_file.rb +22 -4
  102. data/lib/active_support/environment_inquirer.rb +20 -0
  103. data/lib/active_support/evented_file_update_checker.rb +82 -117
  104. data/lib/active_support/execution_wrapper.rb +2 -1
  105. data/lib/active_support/file_update_checker.rb +0 -1
  106. data/lib/active_support/fork_tracker.rb +64 -0
  107. data/lib/active_support/gem_version.rb +4 -4
  108. data/lib/active_support/hash_with_indifferent_access.rb +70 -42
  109. data/lib/active_support/i18n.rb +1 -0
  110. data/lib/active_support/i18n_railtie.rb +15 -8
  111. data/lib/active_support/inflector/inflections.rb +2 -7
  112. data/lib/active_support/inflector/methods.rb +49 -58
  113. data/lib/active_support/inflector/transliterate.rb +47 -18
  114. data/lib/active_support/json/decoding.rb +25 -26
  115. data/lib/active_support/json/encoding.rb +11 -3
  116. data/lib/active_support/key_generator.rb +1 -33
  117. data/lib/active_support/lazy_load_hooks.rb +5 -2
  118. data/lib/active_support/locale/en.rb +33 -0
  119. data/lib/active_support/locale/en.yml +7 -3
  120. data/lib/active_support/log_subscriber.rb +39 -9
  121. data/lib/active_support/logger.rb +2 -17
  122. data/lib/active_support/logger_silence.rb +11 -19
  123. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  124. data/lib/active_support/message_encryptor.rb +8 -13
  125. data/lib/active_support/message_verifier.rb +10 -10
  126. data/lib/active_support/messages/metadata.rb +11 -2
  127. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  128. data/lib/active_support/messages/rotator.rb +10 -9
  129. data/lib/active_support/multibyte/chars.rb +10 -68
  130. data/lib/active_support/multibyte/unicode.rb +15 -327
  131. data/lib/active_support/notifications/fanout.rb +116 -16
  132. data/lib/active_support/notifications/instrumenter.rb +71 -9
  133. data/lib/active_support/notifications.rb +72 -8
  134. data/lib/active_support/number_helper/number_converter.rb +5 -6
  135. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  136. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  137. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  138. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  139. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  140. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  141. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  142. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  143. data/lib/active_support/number_helper.rb +38 -12
  144. data/lib/active_support/option_merger.rb +22 -3
  145. data/lib/active_support/ordered_hash.rb +1 -1
  146. data/lib/active_support/ordered_options.rb +13 -3
  147. data/lib/active_support/parameter_filter.rb +133 -0
  148. data/lib/active_support/per_thread_registry.rb +1 -1
  149. data/lib/active_support/rails.rb +1 -10
  150. data/lib/active_support/railtie.rb +23 -1
  151. data/lib/active_support/reloader.rb +4 -5
  152. data/lib/active_support/rescuable.rb +4 -4
  153. data/lib/active_support/secure_compare_rotator.rb +51 -0
  154. data/lib/active_support/security_utils.rb +19 -12
  155. data/lib/active_support/string_inquirer.rb +4 -3
  156. data/lib/active_support/subscriber.rb +72 -28
  157. data/lib/active_support/tagged_logging.rb +42 -8
  158. data/lib/active_support/test_case.rb +91 -0
  159. data/lib/active_support/testing/assertions.rb +30 -9
  160. data/lib/active_support/testing/deprecation.rb +0 -1
  161. data/lib/active_support/testing/file_fixtures.rb +2 -0
  162. data/lib/active_support/testing/isolation.rb +2 -2
  163. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  164. data/lib/active_support/testing/parallelization/server.rb +78 -0
  165. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  166. data/lib/active_support/testing/parallelization.rb +51 -0
  167. data/lib/active_support/testing/stream.rb +1 -2
  168. data/lib/active_support/testing/time_helpers.rb +47 -12
  169. data/lib/active_support/time_with_zone.rb +81 -47
  170. data/lib/active_support/values/time_zone.rb +32 -17
  171. data/lib/active_support/xml_mini/jdom.rb +2 -3
  172. data/lib/active_support/xml_mini/libxml.rb +2 -2
  173. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  174. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  176. data/lib/active_support/xml_mini/rexml.rb +10 -3
  177. data/lib/active_support/xml_mini.rb +2 -10
  178. data/lib/active_support.rb +14 -1
  179. metadata +55 -29
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -13,14 +13,15 @@ module ActiveSupport
13
13
  @notifier = notifier
14
14
  end
15
15
 
16
- # Instrument the given block by measuring the time taken to execute it
17
- # and publish it. Notice that events get sent even if an error occurs
18
- # in the passed-in block.
16
+ # Given a block, instrument it by measuring the time taken to execute
17
+ # and publish it. Without a block, simply send a message via the
18
+ # notifier. Notice that events get sent even if an error occurs in the
19
+ # passed-in block.
19
20
  def instrument(name, payload = {})
20
21
  # some of the listeners might have state
21
22
  listeners_state = start name, payload
22
23
  begin
23
- yield payload
24
+ yield payload if block_given?
24
25
  rescue Exception => e
25
26
  payload[:exception] = [e.class.name, e.message]
26
27
  payload[:exception_object] = e
@@ -45,15 +46,14 @@ module ActiveSupport
45
46
  end
46
47
 
47
48
  private
48
-
49
49
  def unique_id
50
50
  SecureRandom.hex(10)
51
51
  end
52
52
  end
53
53
 
54
54
  class Event
55
- attr_reader :name, :time, :transaction_id, :payload, :children
56
- attr_accessor :end
55
+ attr_reader :name, :time, :end, :transaction_id, :children
56
+ attr_accessor :payload
57
57
 
58
58
  def initialize(name, start, ending, transaction_id, payload)
59
59
  @name = name
@@ -62,7 +62,42 @@ module ActiveSupport
62
62
  @transaction_id = transaction_id
63
63
  @end = ending
64
64
  @children = []
65
- @duration = nil
65
+ @cpu_time_start = 0
66
+ @cpu_time_finish = 0
67
+ @allocation_count_start = 0
68
+ @allocation_count_finish = 0
69
+ end
70
+
71
+ # Record information at the time this event starts
72
+ def start!
73
+ @time = now
74
+ @cpu_time_start = now_cpu
75
+ @allocation_count_start = now_allocations
76
+ end
77
+
78
+ # Record information at the time this event finishes
79
+ def finish!
80
+ @cpu_time_finish = now_cpu
81
+ @end = now
82
+ @allocation_count_finish = now_allocations
83
+ end
84
+
85
+ # Returns the CPU time (in milliseconds) passed since the call to
86
+ # +start!+ and the call to +finish!+
87
+ def cpu_time
88
+ (@cpu_time_finish - @cpu_time_start) * 1000
89
+ end
90
+
91
+ # Returns the idle time time (in milliseconds) passed since the call to
92
+ # +start!+ and the call to +finish!+
93
+ def idle_time
94
+ duration - cpu_time
95
+ end
96
+
97
+ # Returns the number of allocations made since the call to +start!+ and
98
+ # the call to +finish!+
99
+ def allocations
100
+ @allocation_count_finish - @allocation_count_start
66
101
  end
67
102
 
68
103
  # Returns the difference in milliseconds between when the execution of the
@@ -78,7 +113,7 @@ module ActiveSupport
78
113
  #
79
114
  # @event.duration # => 1000.138
80
115
  def duration
81
- @duration ||= 1000.0 * (self.end - time)
116
+ 1000.0 * (self.end - time)
82
117
  end
83
118
 
84
119
  def <<(event)
@@ -88,6 +123,33 @@ module ActiveSupport
88
123
  def parent_of?(event)
89
124
  @children.include? event
90
125
  end
126
+
127
+ private
128
+ def now
129
+ Concurrent.monotonic_time
130
+ end
131
+
132
+ begin
133
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
134
+
135
+ def now_cpu
136
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
137
+ end
138
+ rescue
139
+ def now_cpu
140
+ 0
141
+ end
142
+ end
143
+
144
+ if defined?(JRUBY_VERSION)
145
+ def now_allocations
146
+ 0
147
+ end
148
+ else
149
+ def now_allocations
150
+ GC.stat :total_allocated_objects
151
+ end
152
+ end
91
153
  end
92
154
  end
93
155
  end
@@ -34,10 +34,23 @@ module ActiveSupport
34
34
  # name # => String, name of the event (such as 'render' from above)
35
35
  # start # => Time, when the instrumented block started execution
36
36
  # finish # => Time, when the instrumented block ended execution
37
- # id # => String, unique ID for this notification
37
+ # id # => String, unique ID for the instrumenter that fired the event
38
38
  # payload # => Hash, the payload
39
39
  # end
40
40
  #
41
+ # Here, the +start+ and +finish+ values represent wall-clock time. If you are
42
+ # concerned about accuracy, you can register a monotonic subscriber.
43
+ #
44
+ # ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
45
+ # name # => String, name of the event (such as 'render' from above)
46
+ # start # => Monotonic time, when the instrumented block started execution
47
+ # finish # => Monotonic time, when the instrumented block ended execution
48
+ # id # => String, unique ID for the instrumenter that fired the event
49
+ # payload # => Hash, the payload
50
+ # end
51
+ #
52
+ # The +start+ and +finish+ values above represent monotonic time.
53
+ #
41
54
  # For instance, let's store all "render" events in an array:
42
55
  #
43
56
  # events = []
@@ -59,7 +72,7 @@ module ActiveSupport
59
72
  # event.payload # => { extra: :information }
60
73
  #
61
74
  # The block in the <tt>subscribe</tt> call gets the name of the event, start
62
- # timestamp, end timestamp, a string with a unique identifier for that event
75
+ # timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
63
76
  # (something like "535801666f04d0298cd6"), and a hash with the payload, in
64
77
  # that order.
65
78
  #
@@ -67,9 +80,12 @@ module ActiveSupport
67
80
  # have a key <tt>:exception</tt> with an array of two elements as value: a string with
68
81
  # the name of the exception class, and the exception message.
69
82
  # The <tt>:exception_object</tt> key of the payload will have the exception
70
- # itself as the value.
83
+ # itself as the value:
71
84
  #
72
- # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
85
+ # event.payload[:exception] # => ["ArgumentError", "Invalid value"]
86
+ # event.payload[:exception_object] # => #<ArgumentError: Invalid value>
87
+ #
88
+ # As the earlier example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
73
89
  # is able to take the arguments as they come and provide an object-oriented
74
90
  # interface to that data.
75
91
  #
@@ -132,6 +148,16 @@ module ActiveSupport
132
148
  # during the execution of the block. The callback is unsubscribed automatically
133
149
  # after that.
134
150
  #
151
+ # To record +started+ and +finished+ values with monotonic time,
152
+ # specify the optional <tt>:monotonic</tt> option to the
153
+ # <tt>subscribed</tt> method. The <tt>:monotonic</tt> option is set
154
+ # to +false+ by default.
155
+ #
156
+ # callback = lambda {|name, started, finished, unique_id, payload| ... }
157
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do
158
+ # ...
159
+ # end
160
+ #
135
161
  # === Manual Unsubscription
136
162
  #
137
163
  # The +subscribe+ method returns a subscriber object:
@@ -150,6 +176,15 @@ module ActiveSupport
150
176
  #
151
177
  # ActiveSupport::Notifications.unsubscribe("render")
152
178
  #
179
+ # Subscribers using a regexp or other pattern-matching object will remain subscribed
180
+ # to all events that match their original pattern, unless those events match a string
181
+ # passed to +unsubscribe+:
182
+ #
183
+ # subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
184
+ # ActiveSupport::Notifications.unsubscribe('render_template.action_view')
185
+ # subscriber.matches?('render_template.action_view') # => false
186
+ # subscriber.matches?('render_partial.action_view') # => true
187
+ #
153
188
  # == Default Queue
154
189
  #
155
190
  # Notifications ships with a queue implementation that consumes and publishes events
@@ -171,12 +206,41 @@ module ActiveSupport
171
206
  end
172
207
  end
173
208
 
174
- def subscribe(*args, &block)
175
- notifier.subscribe(*args, &block)
209
+ # Subscribe to a given event name with the passed +block+.
210
+ #
211
+ # You can subscribe to events by passing a String to match exact event
212
+ # names, or by passing a Regexp to match all events that match a pattern.
213
+ #
214
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
215
+ # @event = ActiveSupport::Notifications::Event.new(*args)
216
+ # end
217
+ #
218
+ # The +block+ will receive five parameters with information about the event:
219
+ #
220
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
221
+ # name # => String, name of the event (such as 'render' from above)
222
+ # start # => Time, when the instrumented block started execution
223
+ # finish # => Time, when the instrumented block ended execution
224
+ # id # => String, unique ID for the instrumenter that fired the event
225
+ # payload # => Hash, the payload
226
+ # end
227
+ #
228
+ # If the block passed to the method only takes one parameter,
229
+ # it will yield an event object to the block:
230
+ #
231
+ # ActiveSupport::Notifications.subscribe(/render/) do |event|
232
+ # @event = event
233
+ # end
234
+ def subscribe(pattern = nil, callback = nil, &block)
235
+ notifier.subscribe(pattern, callback, monotonic: false, &block)
236
+ end
237
+
238
+ def monotonic_subscribe(pattern = nil, callback = nil, &block)
239
+ notifier.subscribe(pattern, callback, monotonic: true, &block)
176
240
  end
177
241
 
178
- def subscribed(callback, *args, &block)
179
- subscriber = subscribe(*args, &callback)
242
+ def subscribed(callback, pattern = nil, monotonic: false, &block)
243
+ subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic)
180
244
  yield
181
245
  ensure
182
246
  unsubscribe(subscriber)
@@ -30,7 +30,7 @@ module ActiveSupport
30
30
  # If set to true, precision will mean the number of significant digits instead
31
31
  # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
32
32
  significant: false,
33
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
33
+ # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
34
34
  strip_insignificant_zeros: false
35
35
  },
36
36
 
@@ -136,7 +136,6 @@ module ActiveSupport
136
136
  end
137
137
 
138
138
  private
139
-
140
139
  def options
141
140
  @options ||= format_options.merge(opts)
142
141
  end
@@ -162,12 +161,12 @@ module ActiveSupport
162
161
  options
163
162
  end
164
163
 
165
- def translate_number_value_with_default(key, i18n_options = {})
166
- I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
164
+ def translate_number_value_with_default(key, **i18n_options)
165
+ I18n.translate(key, **{ default: default_value(key), scope: :number }.merge!(i18n_options))
167
166
  end
168
167
 
169
- def translate_in_locale(key, i18n_options = {})
170
- translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
168
+ def translate_in_locale(key, **i18n_options)
169
+ translate_number_value_with_default(key, **{ locale: options[:locale] }.merge(i18n_options))
171
170
  end
172
171
 
173
172
  def default_value(key)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/numeric/inquiry"
3
+ require "active_support/number_helper/number_converter"
4
4
 
5
5
  module ActiveSupport
6
6
  module NumberHelper
@@ -11,21 +11,16 @@ module ActiveSupport
11
11
  number = self.number.to_s.strip
12
12
  format = options[:format]
13
13
 
14
- if number.to_f.negative?
14
+ if number.sub!(/^-/, "") &&
15
+ (options[:precision] != 0 || number.to_f > 0.5)
15
16
  format = options[:negative_format]
16
- number = absolute_value(number)
17
17
  end
18
18
 
19
19
  rounded_number = NumberToRoundedConverter.convert(number, options)
20
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
20
+ format.gsub("%n", rounded_number).gsub("%u", options[:unit])
21
21
  end
22
22
 
23
23
  private
24
-
25
- def absolute_value(number)
26
- number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "")
27
- end
28
-
29
24
  def options
30
25
  @options ||= begin
31
26
  defaults = default_format_options.merge(i18n_opts)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToDelimitedConverter < NumberConverter #:nodoc:
@@ -12,9 +14,8 @@ module ActiveSupport
12
14
  end
13
15
 
14
16
  private
15
-
16
17
  def parts
17
- left, right = number.to_s.split(".".freeze)
18
+ left, right = number.to_s.split(".")
18
19
  left.gsub!(delimiter_pattern) do |digit_to_delimit|
19
20
  "#{digit_to_delimit}#{options[:delimiter]}"
20
21
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToHumanConverter < NumberConverter # :nodoc:
@@ -14,7 +16,7 @@ module ActiveSupport
14
16
  @number = RoundingHelper.new(options).round(number)
15
17
  @number = Float(number)
16
18
 
17
- # 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.
18
20
  unless options.key?(:strip_insignificant_zeros)
19
21
  options[:strip_insignificant_zeros] = true
20
22
  end
@@ -25,11 +27,10 @@ module ActiveSupport
25
27
 
26
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
35
  options[:format] || translate_in_locale("human.decimal_units.format")
35
36
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToHumanSizeConverter < NumberConverter #:nodoc:
@@ -11,7 +13,7 @@ module ActiveSupport
11
13
  def convert
12
14
  @number = Float(number)
13
15
 
14
- # 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.
15
17
  unless options.key?(:strip_insignificant_zeros)
16
18
  options[:strip_insignificant_zeros] = true
17
19
  end
@@ -22,11 +24,10 @@ module ActiveSupport
22
24
  human_size = number / (base**exponent)
23
25
  number_to_format = NumberToRoundedConverter.convert(human_size, options)
24
26
  end
25
- conversion_format.gsub("%n".freeze, number_to_format).gsub("%u".freeze, unit)
27
+ conversion_format.gsub("%n", number_to_format).gsub("%u", unit)
26
28
  end
27
29
 
28
30
  private
29
-
30
31
  def conversion_format
31
32
  translate_number_value_with_default("human.storage_units.format", locale: options[:locale], raise: true)
32
33
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToPercentageConverter < NumberConverter # :nodoc:
@@ -7,7 +9,7 @@ module ActiveSupport
7
9
 
8
10
  def convert
9
11
  rounded_number = NumberToRoundedConverter.convert(number, options)
10
- options[:format].gsub("%n".freeze, rounded_number)
12
+ options[:format].gsub("%n", rounded_number)
11
13
  end
12
14
  end
13
15
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToPhoneConverter < NumberConverter #:nodoc:
@@ -10,7 +12,6 @@ module ActiveSupport
10
12
  end
11
13
 
12
14
  private
13
-
14
15
  def convert_to_phone_number(number)
15
16
  if opts[:area_code]
16
17
  convert_with_area_code(number)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToRoundedConverter < NumberConverter # :nodoc:
@@ -18,14 +20,18 @@ module ActiveSupport
18
20
  end
19
21
 
20
22
  formatted_string =
21
- if BigDecimal === rounded_number && rounded_number.finite?
23
+ if rounded_number.finite?
22
24
  s = rounded_number.to_s("F")
23
- s << "0".freeze * precision
24
- a, b = s.split(".".freeze, 2)
25
- a << ".".freeze
26
- a << b[0, precision]
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
27
32
  else
28
- "%00.#{precision}f" % rounded_number
33
+ # Infinity/NaN
34
+ "%f" % rounded_number
29
35
  end
30
36
  else
31
37
  formatted_string = rounded_number
@@ -36,7 +42,6 @@ module ActiveSupport
36
42
  end
37
43
 
38
44
  private
39
-
40
45
  def strip_insignificant_zeros
41
46
  options[:strip_insignificant_zeros]
42
47
  end
@@ -10,57 +10,41 @@ module ActiveSupport
10
10
  end
11
11
 
12
12
  def round(number)
13
+ precision = absolute_precision(number)
13
14
  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
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
20
18
  end
21
19
 
22
20
  def digit_count(number)
23
21
  return 1 if number.zero?
24
- (Math.log10(absolute_number(number)) + 1).floor
22
+ (Math.log10(number.abs) + 1).floor
25
23
  end
26
24
 
27
25
  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
26
  def convert_to_decimal(number)
43
27
  case number
44
28
  when Float, String
45
29
  BigDecimal(number.to_s)
46
30
  when Rational
47
- BigDecimal(number, digit_count(number.to_i) + precision)
31
+ BigDecimal(number, digit_count(number.to_i) + options[:precision])
48
32
  else
49
33
  number.to_d
50
34
  end
51
35
  end
52
36
 
53
- def precision
54
- options[:precision]
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
55
43
  end
56
44
 
57
45
  def significant
58
46
  options[:significant]
59
47
  end
60
-
61
- def absolute_number(number)
62
- number.respond_to?(:abs) ? number.abs : number.to_d.abs
63
- end
64
48
  end
65
49
  end
66
50
  end