activesupport 6.0.3.4 → 6.1.7.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +456 -398
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +5 -4
- data/lib/active_support/cache/mem_cache_store.rb +29 -18
- data/lib/active_support/cache/memory_store.rb +46 -26
- data/lib/active_support/cache/redis_cache_store.rb +27 -27
- data/lib/active_support/cache/strategy/local_cache.rb +21 -6
- data/lib/active_support/cache.rb +92 -45
- data/lib/active_support/callbacks.rb +65 -56
- data/lib/active_support/concern.rb +46 -2
- data/lib/active_support/configurable.rb +3 -3
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/digest/uuid.rb +1 -0
- data/lib/active_support/core_ext/enumerable.rb +76 -4
- data/lib/active_support/core_ext/hash/conversions.rb +2 -2
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +1 -1
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +38 -28
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +13 -2
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range/compare_range.rb +9 -3
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/inflections.rb +38 -4
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +38 -10
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +22 -1
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +5 -1
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +9 -2
- data/lib/active_support/dependencies/zeitwerk_integration.rb +4 -1
- data/lib/active_support/dependencies.rb +43 -19
- data/lib/active_support/deprecation/behaviors.rb +15 -2
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +3 -2
- data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +6 -2
- data/lib/active_support/digest.rb +2 -0
- data/lib/active_support/duration/iso8601_serializer.rb +15 -9
- data/lib/active_support/duration.rb +75 -25
- data/lib/active_support/encrypted_file.rb +19 -2
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +69 -133
- data/lib/active_support/execution_wrapper.rb +16 -13
- data/lib/active_support/fork_tracker.rb +64 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +48 -24
- data/lib/active_support/i18n_railtie.rb +14 -19
- data/lib/active_support/inflector/inflections.rb +1 -2
- data/lib/active_support/inflector/methods.rb +36 -33
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +5 -1
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +8 -0
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +34 -12
- data/lib/active_support/message_encryptor.rb +4 -7
- data/lib/active_support/message_verifier.rb +5 -5
- data/lib/active_support/messages/metadata.rb +9 -1
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +6 -5
- data/lib/active_support/multibyte/chars.rb +4 -42
- data/lib/active_support/multibyte/unicode.rb +9 -83
- data/lib/active_support/notifications/fanout.rb +23 -8
- data/lib/active_support/notifications/instrumenter.rb +6 -15
- data/lib/active_support/notifications.rb +32 -5
- data/lib/active_support/number_helper/number_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -7
- data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/number_helper.rb +29 -14
- data/lib/active_support/option_merger.rb +3 -2
- data/lib/active_support/ordered_options.rb +8 -2
- data/lib/active_support/parameter_filter.rb +16 -11
- data/lib/active_support/per_thread_registry.rb +2 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +1 -1
- data/lib/active_support/rescuable.rb +4 -4
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +12 -7
- data/lib/active_support/tagged_logging.rb +30 -5
- data/lib/active_support/testing/assertions.rb +18 -11
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/parallelization.rb +12 -95
- data/lib/active_support/testing/time_helpers.rb +40 -3
- data/lib/active_support/time_with_zone.rb +67 -43
- data/lib/active_support/values/time_zone.rb +22 -10
- data/lib/active_support/xml_mini/rexml.rb +8 -1
- data/lib/active_support.rb +13 -1
- metadata +35 -36
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -5,53 +5,19 @@ module ActiveSupport
|
|
5
5
|
module Unicode
|
6
6
|
extend self
|
7
7
|
|
8
|
-
# A list of all available normalization forms.
|
9
|
-
# See https://www.unicode.org/reports/tr15/tr15-29.html for more
|
10
|
-
# information about normalization.
|
11
|
-
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
|
12
|
-
|
13
|
-
NORMALIZATION_FORM_ALIASES = { # :nodoc:
|
14
|
-
c: :nfc,
|
15
|
-
d: :nfd,
|
16
|
-
kc: :nfkc,
|
17
|
-
kd: :nfkd
|
18
|
-
}
|
19
|
-
|
20
8
|
# The Unicode version that is supported by the implementation
|
21
9
|
UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"]
|
22
10
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
|
28
|
-
attr_accessor :default_normalization_form
|
29
|
-
@default_normalization_form = :kc
|
30
|
-
|
31
|
-
# Unpack the string at grapheme boundaries. Returns a list of character
|
32
|
-
# lists.
|
33
|
-
#
|
34
|
-
# Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
|
35
|
-
# Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
|
36
|
-
def unpack_graphemes(string)
|
37
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
38
|
-
ActiveSupport::Multibyte::Unicode#unpack_graphemes is deprecated and will be
|
39
|
-
removed from Rails 6.1. Use string.scan(/\X/).map(&:codepoints) instead.
|
40
|
-
MSG
|
41
|
-
|
42
|
-
string.scan(/\X/).map(&:codepoints)
|
11
|
+
def default_normalization_form
|
12
|
+
ActiveSupport::Deprecation.warn(
|
13
|
+
"ActiveSupport::Multibyte::Unicode.default_normalization_form is deprecated and will be removed in Rails 7.0."
|
14
|
+
)
|
43
15
|
end
|
44
16
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
50
|
-
ActiveSupport::Multibyte::Unicode#pack_graphemes is deprecated and will be
|
51
|
-
removed from Rails 6.1. Use array.flatten.pack("U*") instead.
|
52
|
-
MSG
|
53
|
-
|
54
|
-
unpacked.flatten.pack("U*")
|
17
|
+
def default_normalization_form=(_)
|
18
|
+
ActiveSupport::Deprecation.warn(
|
19
|
+
"ActiveSupport::Multibyte::Unicode.default_normalization_form= is deprecated and will be removed in Rails 7.0."
|
20
|
+
)
|
55
21
|
end
|
56
22
|
|
57
23
|
# Decompose composed characters to the decomposed form.
|
@@ -76,7 +42,7 @@ module ActiveSupport
|
|
76
42
|
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
|
77
43
|
# encoding is entirely CP1252 or ISO-8859-1.
|
78
44
|
def tidy_bytes(string, force = false)
|
79
|
-
return string if string.empty?
|
45
|
+
return string if string.empty? || string.ascii_only?
|
80
46
|
return recode_windows1252_chars(string) if force
|
81
47
|
string.scrub { |bad| recode_windows1252_chars(bad) }
|
82
48
|
end
|
@@ -107,46 +73,6 @@ module ActiveSupport
|
|
107
73
|
end
|
108
74
|
end
|
109
75
|
|
110
|
-
# Returns the KC normalization of the string by default. NFKC is
|
111
|
-
# considered the best normalization form for passing strings to databases
|
112
|
-
# and validations.
|
113
|
-
#
|
114
|
-
# * <tt>string</tt> - The string to perform normalization on.
|
115
|
-
# * <tt>form</tt> - The form you want to normalize in. Should be one of
|
116
|
-
# the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
|
117
|
-
# Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
|
118
|
-
def normalize(string, form = nil)
|
119
|
-
form ||= @default_normalization_form
|
120
|
-
|
121
|
-
# See https://www.unicode.org/reports/tr15, Table 1
|
122
|
-
if alias_form = NORMALIZATION_FORM_ALIASES[form]
|
123
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
124
|
-
ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
|
125
|
-
removed from Rails 6.1. Use String#unicode_normalize(:#{alias_form}) instead.
|
126
|
-
MSG
|
127
|
-
|
128
|
-
string.unicode_normalize(alias_form)
|
129
|
-
else
|
130
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
131
|
-
ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
|
132
|
-
removed from Rails 6.1. Use String#unicode_normalize instead.
|
133
|
-
MSG
|
134
|
-
|
135
|
-
raise ArgumentError, "#{form} is not a valid normalization variant", caller
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
%w(downcase upcase swapcase).each do |method|
|
140
|
-
define_method(method) do |string|
|
141
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
142
|
-
ActiveSupport::Multibyte::Unicode##{method} is deprecated and
|
143
|
-
will be removed from Rails 6.1. Use String methods directly.
|
144
|
-
MSG
|
145
|
-
|
146
|
-
string.send(method)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
76
|
private
|
151
77
|
def recode_windows1252_chars(string)
|
152
78
|
string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "mutex_m"
|
4
4
|
require "concurrent/map"
|
5
5
|
require "set"
|
6
|
+
require "active_support/core_ext/object/try"
|
6
7
|
|
7
8
|
module ActiveSupport
|
8
9
|
module Notifications
|
@@ -20,8 +21,8 @@ module ActiveSupport
|
|
20
21
|
super
|
21
22
|
end
|
22
23
|
|
23
|
-
def subscribe(pattern = nil, callable = nil, &block)
|
24
|
-
subscriber = Subscribers.new(pattern, callable || block)
|
24
|
+
def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
|
25
|
+
subscriber = Subscribers.new(pattern, callable || block, monotonic)
|
25
26
|
synchronize do
|
26
27
|
if String === pattern
|
27
28
|
@string_subscribers[pattern] << subscriber
|
@@ -84,8 +85,8 @@ module ActiveSupport
|
|
84
85
|
end
|
85
86
|
|
86
87
|
module Subscribers # :nodoc:
|
87
|
-
def self.new(pattern, listener)
|
88
|
-
subscriber_class = Timed
|
88
|
+
def self.new(pattern, listener, monotonic)
|
89
|
+
subscriber_class = monotonic ? MonotonicTimed : Timed
|
89
90
|
|
90
91
|
if listener.respond_to?(:start) && listener.respond_to?(:finish)
|
91
92
|
subscriber_class = Evented
|
@@ -103,10 +104,6 @@ module ActiveSupport
|
|
103
104
|
wrap_all pattern, subscriber_class.new(pattern, listener)
|
104
105
|
end
|
105
106
|
|
106
|
-
def self.event_object_subscriber(pattern, block)
|
107
|
-
wrap_all pattern, EventObject.new(pattern, block)
|
108
|
-
end
|
109
|
-
|
110
107
|
def self.wrap_all(pattern, subscriber)
|
111
108
|
unless pattern
|
112
109
|
AllMessages.new(subscriber)
|
@@ -190,6 +187,23 @@ module ActiveSupport
|
|
190
187
|
end
|
191
188
|
end
|
192
189
|
|
190
|
+
class MonotonicTimed < Evented # :nodoc:
|
191
|
+
def publish(name, *args)
|
192
|
+
@delegate.call name, *args
|
193
|
+
end
|
194
|
+
|
195
|
+
def start(name, id, payload)
|
196
|
+
timestack = Thread.current[:_timestack_monotonic] ||= []
|
197
|
+
timestack.push Concurrent.monotonic_time
|
198
|
+
end
|
199
|
+
|
200
|
+
def finish(name, id, payload)
|
201
|
+
timestack = Thread.current[:_timestack_monotonic]
|
202
|
+
started = timestack.pop
|
203
|
+
@delegate.call(name, started, Concurrent.monotonic_time, id, payload)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
193
207
|
class EventObject < Evented
|
194
208
|
def start(name, id, payload)
|
195
209
|
stack = Thread.current[:_event_stack] ||= []
|
@@ -201,6 +215,7 @@ module ActiveSupport
|
|
201
215
|
def finish(name, id, payload)
|
202
216
|
stack = Thread.current[:_event_stack]
|
203
217
|
event = stack.pop
|
218
|
+
event.payload = payload
|
204
219
|
event.finish!
|
205
220
|
@delegate.call event
|
206
221
|
end
|
@@ -52,14 +52,8 @@ module ActiveSupport
|
|
52
52
|
end
|
53
53
|
|
54
54
|
class Event
|
55
|
-
attr_reader :name, :time, :end, :transaction_id, :
|
56
|
-
|
57
|
-
def self.clock_gettime_supported? # :nodoc:
|
58
|
-
defined?(Process::CLOCK_THREAD_CPUTIME_ID) &&
|
59
|
-
!Gem.win_platform? &&
|
60
|
-
!RUBY_PLATFORM.match?(/solaris/i)
|
61
|
-
end
|
62
|
-
private_class_method :clock_gettime_supported?
|
55
|
+
attr_reader :name, :time, :end, :transaction_id, :children
|
56
|
+
attr_accessor :payload
|
63
57
|
|
64
58
|
def initialize(name, start, ending, transaction_id, payload)
|
65
59
|
@name = name
|
@@ -88,11 +82,6 @@ module ActiveSupport
|
|
88
82
|
@allocation_count_finish = now_allocations
|
89
83
|
end
|
90
84
|
|
91
|
-
def end=(ending)
|
92
|
-
ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
|
93
|
-
@end = ending
|
94
|
-
end
|
95
|
-
|
96
85
|
# Returns the CPU time (in milliseconds) passed since the call to
|
97
86
|
# +start!+ and the call to +finish!+
|
98
87
|
def cpu_time
|
@@ -140,11 +129,13 @@ module ActiveSupport
|
|
140
129
|
Concurrent.monotonic_time
|
141
130
|
end
|
142
131
|
|
143
|
-
|
132
|
+
begin
|
133
|
+
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
|
134
|
+
|
144
135
|
def now_cpu
|
145
136
|
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
|
146
137
|
end
|
147
|
-
|
138
|
+
rescue
|
148
139
|
def now_cpu
|
149
140
|
0
|
150
141
|
end
|
@@ -38,6 +38,19 @@ module ActiveSupport
|
|
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 = []
|
@@ -135,6 +148,16 @@ module ActiveSupport
|
|
135
148
|
# during the execution of the block. The callback is unsubscribed automatically
|
136
149
|
# after that.
|
137
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
|
+
#
|
138
161
|
# === Manual Unsubscription
|
139
162
|
#
|
140
163
|
# The +subscribe+ method returns a subscriber object:
|
@@ -155,7 +178,7 @@ module ActiveSupport
|
|
155
178
|
#
|
156
179
|
# Subscribers using a regexp or other pattern-matching object will remain subscribed
|
157
180
|
# to all events that match their original pattern, unless those events match a string
|
158
|
-
# passed to
|
181
|
+
# passed to +unsubscribe+:
|
159
182
|
#
|
160
183
|
# subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
|
161
184
|
# ActiveSupport::Notifications.unsubscribe('render_template.action_view')
|
@@ -208,12 +231,16 @@ module ActiveSupport
|
|
208
231
|
# ActiveSupport::Notifications.subscribe(/render/) do |event|
|
209
232
|
# @event = event
|
210
233
|
# end
|
211
|
-
def subscribe(
|
212
|
-
notifier.subscribe(
|
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)
|
213
240
|
end
|
214
241
|
|
215
|
-
def subscribed(callback,
|
216
|
-
subscriber = subscribe(
|
242
|
+
def subscribed(callback, pattern = nil, monotonic: false, &block)
|
243
|
+
subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic)
|
217
244
|
yield
|
218
245
|
ensure
|
219
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 (
|
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
|
|
@@ -9,15 +9,11 @@ module ActiveSupport
|
|
9
9
|
|
10
10
|
def convert
|
11
11
|
number = self.number.to_s.strip
|
12
|
-
number_f = number.to_f
|
13
12
|
format = options[:format]
|
14
13
|
|
15
|
-
if
|
16
|
-
|
17
|
-
|
18
|
-
unless options[:precision] == 0 && number < 0.5
|
19
|
-
format = options[:negative_format]
|
20
|
-
end
|
14
|
+
if number.sub!(/^-/, "") &&
|
15
|
+
(options[:precision] != 0 || number.to_f > 0.5)
|
16
|
+
format = options[:negative_format]
|
21
17
|
end
|
22
18
|
|
23
19
|
rounded_number = NumberToRoundedConverter.convert(number, options)
|
@@ -16,7 +16,7 @@ module ActiveSupport
|
|
16
16
|
@number = RoundingHelper.new(options).round(number)
|
17
17
|
@number = Float(number)
|
18
18
|
|
19
|
-
#
|
19
|
+
# For backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files.
|
20
20
|
unless options.key?(:strip_insignificant_zeros)
|
21
21
|
options[:strip_insignificant_zeros] = true
|
22
22
|
end
|
@@ -13,7 +13,7 @@ module ActiveSupport
|
|
13
13
|
def convert
|
14
14
|
@number = Float(number)
|
15
15
|
|
16
|
-
#
|
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
|
@@ -20,14 +20,18 @@ module ActiveSupport
|
|
20
20
|
end
|
21
21
|
|
22
22
|
formatted_string =
|
23
|
-
if
|
23
|
+
if rounded_number.finite?
|
24
24
|
s = rounded_number.to_s("F")
|
25
|
-
s << "0" * precision
|
26
25
|
a, b = s.split(".", 2)
|
27
|
-
|
28
|
-
|
26
|
+
if precision != 0
|
27
|
+
b << "0" * precision
|
28
|
+
a << "."
|
29
|
+
a << b[0, precision]
|
30
|
+
end
|
31
|
+
a
|
29
32
|
else
|
30
|
-
|
33
|
+
# Infinity/NaN
|
34
|
+
"%f" % rounded_number
|
31
35
|
end
|
32
36
|
else
|
33
37
|
formatted_string = rounded_number
|
@@ -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
|
-
|
15
|
-
|
16
|
-
|
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(
|
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
|
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
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/dependencies/autoload"
|
4
|
-
|
5
3
|
module ActiveSupport
|
6
4
|
module NumberHelper
|
7
5
|
extend ActiveSupport::Autoload
|
@@ -73,6 +71,8 @@ module ActiveSupport
|
|
73
71
|
# (defaults to current locale).
|
74
72
|
# * <tt>:precision</tt> - Sets the level of precision (defaults
|
75
73
|
# to 2).
|
74
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
75
|
+
# (defaults to :default. See BigDecimal::mode)
|
76
76
|
# * <tt>:unit</tt> - Sets the denomination of the currency
|
77
77
|
# (defaults to "$").
|
78
78
|
# * <tt>:separator</tt> - Sets the separator between the units
|
@@ -111,6 +111,8 @@ module ActiveSupport
|
|
111
111
|
# # => "1234567890,50 £"
|
112
112
|
# number_to_currency(1234567890.50, strip_insignificant_zeros: true)
|
113
113
|
# # => "$1,234,567,890.5"
|
114
|
+
# number_to_currency(1234567890.50, precision: 0, round_mode: :up)
|
115
|
+
# # => "$1,234,567,891"
|
114
116
|
def number_to_currency(number, options = {})
|
115
117
|
NumberToCurrencyConverter.convert(number, options)
|
116
118
|
end
|
@@ -124,6 +126,8 @@ module ActiveSupport
|
|
124
126
|
# (defaults to current locale).
|
125
127
|
# * <tt>:precision</tt> - Sets the precision of the number
|
126
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)
|
127
131
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
128
132
|
# of significant_digits. If +false+, the number of fractional
|
129
133
|
# digits (defaults to +false+).
|
@@ -139,15 +143,16 @@ module ActiveSupport
|
|
139
143
|
#
|
140
144
|
# ==== Examples
|
141
145
|
#
|
142
|
-
# number_to_percentage(100)
|
143
|
-
# number_to_percentage('98')
|
144
|
-
# number_to_percentage(100, precision: 0)
|
145
|
-
# number_to_percentage(1000, delimiter: '.', separator: ',')
|
146
|
-
# number_to_percentage(302.24398923423, precision: 5)
|
147
|
-
# number_to_percentage(1000, locale: :fr)
|
148
|
-
# number_to_percentage(1000, precision: nil)
|
149
|
-
# number_to_percentage('98a')
|
150
|
-
# number_to_percentage(100, format: '%n %')
|
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%"
|
151
156
|
def number_to_percentage(number, options = {})
|
152
157
|
NumberToPercentageConverter.convert(number, options)
|
153
158
|
end
|
@@ -198,6 +203,8 @@ module ActiveSupport
|
|
198
203
|
# (defaults to current locale).
|
199
204
|
# * <tt>:precision</tt> - Sets the precision of the number
|
200
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)
|
201
208
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
202
209
|
# of significant_digits. If +false+, the number of fractional
|
203
210
|
# digits (defaults to +false+).
|
@@ -219,6 +226,7 @@ module ActiveSupport
|
|
219
226
|
# number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
|
220
227
|
# number_to_rounded(13, precision: 5, significant: true) # => "13.000"
|
221
228
|
# number_to_rounded(13, precision: nil) # => "13"
|
229
|
+
# number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
|
222
230
|
# number_to_rounded(111.234, locale: :fr) # => "111,234"
|
223
231
|
#
|
224
232
|
# number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
|
@@ -232,7 +240,7 @@ module ActiveSupport
|
|
232
240
|
end
|
233
241
|
|
234
242
|
# Formats the bytes in +number+ into a more understandable
|
235
|
-
# representation (e.g., giving it 1500 yields 1.
|
243
|
+
# representation (e.g., giving it 1500 yields 1.46 KB). This
|
236
244
|
# method is useful for reporting file sizes to users. You can
|
237
245
|
# customize the format in the +options+ hash.
|
238
246
|
#
|
@@ -245,6 +253,8 @@ module ActiveSupport
|
|
245
253
|
# (defaults to current locale).
|
246
254
|
# * <tt>:precision</tt> - Sets the precision of the number
|
247
255
|
# (defaults to 3).
|
256
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
257
|
+
# (defaults to :default. See BigDecimal::mode)
|
248
258
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
249
259
|
# of significant_digits. If +false+, the number of fractional
|
250
260
|
# digits (defaults to +true+)
|
@@ -268,6 +278,7 @@ module ActiveSupport
|
|
268
278
|
# number_to_human_size(1234567890123456789) # => "1.07 EB"
|
269
279
|
# number_to_human_size(1234567, precision: 2) # => "1.2 MB"
|
270
280
|
# number_to_human_size(483989, precision: 2) # => "470 KB"
|
281
|
+
# number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
|
271
282
|
# number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
|
272
283
|
# number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
|
273
284
|
# number_to_human_size(524288000, precision: 5) # => "500 MB"
|
@@ -276,7 +287,7 @@ module ActiveSupport
|
|
276
287
|
end
|
277
288
|
|
278
289
|
# Pretty prints (formats and approximates) a number in a way it
|
279
|
-
# is more readable by humans (
|
290
|
+
# is more readable by humans (e.g.: 1200000000 becomes "1.2
|
280
291
|
# Billion"). This is useful for numbers that can get very large
|
281
292
|
# (and too hard to read).
|
282
293
|
#
|
@@ -284,7 +295,7 @@ module ActiveSupport
|
|
284
295
|
# size.
|
285
296
|
#
|
286
297
|
# You can also define your own unit-quantifier names if you want
|
287
|
-
# to use other decimal units (
|
298
|
+
# to use other decimal units (e.g.: 1500 becomes "1.5
|
288
299
|
# kilometers", 0.150 becomes "150 milliliters", etc). You may
|
289
300
|
# define a wide range of unit quantifiers, even fractional ones
|
290
301
|
# (centi, deci, mili, etc).
|
@@ -295,6 +306,8 @@ module ActiveSupport
|
|
295
306
|
# (defaults to current locale).
|
296
307
|
# * <tt>:precision</tt> - Sets the precision of the number
|
297
308
|
# (defaults to 3).
|
309
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
310
|
+
# (defaults to :default. See BigDecimal::mode)
|
298
311
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
299
312
|
# of significant_digits. If +false+, the number of fractional
|
300
313
|
# digits (defaults to +true+)
|
@@ -332,6 +345,8 @@ module ActiveSupport
|
|
332
345
|
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
|
333
346
|
# number_to_human(489939, precision: 2) # => "490 Thousand"
|
334
347
|
# number_to_human(489939, precision: 4) # => "489.9 Thousand"
|
348
|
+
# number_to_human(489939, precision: 2
|
349
|
+
# , round_mode: :down) # => "480 Thousand"
|
335
350
|
# number_to_human(1234567, precision: 4,
|
336
351
|
# significant: false) # => "1.2346 Million"
|
337
352
|
# number_to_human(1234567, precision: 1,
|
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/hash/deep_merge"
|
4
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
class OptionMerger #:nodoc:
|
7
8
|
instance_methods.each do |method|
|
8
|
-
undef_method(method)
|
9
|
+
undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
|
9
10
|
end
|
10
11
|
|
11
12
|
def initialize(context, options)
|
@@ -37,7 +38,7 @@ module ActiveSupport
|
|
37
38
|
end
|
38
39
|
else
|
39
40
|
def invoke_method(method, arguments, options, &block)
|
40
|
-
arguments << options if options
|
41
|
+
arguments << options.dup if options
|
41
42
|
@context.__send__(method, *arguments, &block)
|
42
43
|
end
|
43
44
|
end
|
@@ -3,7 +3,9 @@
|
|
3
3
|
require "active_support/core_ext/object/blank"
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
|
-
#
|
6
|
+
# +OrderedOptions+ inherits from +Hash+ and provides dynamic accessor methods.
|
7
|
+
#
|
8
|
+
# With a +Hash+, key-value pairs are typically managed like this:
|
7
9
|
#
|
8
10
|
# h = {}
|
9
11
|
# h[:boy] = 'John'
|
@@ -12,7 +14,7 @@ module ActiveSupport
|
|
12
14
|
# h[:girl] # => 'Mary'
|
13
15
|
# h[:dog] # => nil
|
14
16
|
#
|
15
|
-
# Using +OrderedOptions+, the above code
|
17
|
+
# Using +OrderedOptions+, the above code can be written as:
|
16
18
|
#
|
17
19
|
# h = ActiveSupport::OrderedOptions.new
|
18
20
|
# h.boy = 'John'
|
@@ -60,6 +62,10 @@ module ActiveSupport
|
|
60
62
|
def extractable_options?
|
61
63
|
true
|
62
64
|
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
"#<#{self.class.name} #{super}>"
|
68
|
+
end
|
63
69
|
end
|
64
70
|
|
65
71
|
# +InheritableOptions+ provides a constructor to build an +OrderedOptions+
|