activesupport 5.2.8.1 → 6.0.6.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 +472 -410
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +27 -1
- data/lib/active_support/cache/file_store.rb +32 -32
- data/lib/active_support/cache/mem_cache_store.rb +12 -7
- data/lib/active_support/cache/memory_store.rb +15 -9
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +47 -20
- data/lib/active_support/cache/strategy/local_cache.rb +22 -22
- data/lib/active_support/cache.rb +71 -48
- data/lib/active_support/callbacks.rb +16 -8
- data/lib/active_support/concern.rb +24 -1
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +7 -11
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +11 -16
- data/lib/active_support/core_ext/class/subclasses.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/enumerable.rb +97 -73
- data/lib/active_support/core_ext/hash/compact.rb +2 -26
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +0 -29
- data/lib/active_support/core_ext/hash/slice.rb +3 -25
- data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
- data/lib/active_support/core_ext/module/delegation.rb +41 -8
- data/lib/active_support/core_ext/module/introspection.rb +38 -13
- data/lib/active_support/core_ext/module/reachable.rb +1 -6
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
- data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +2 -1
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +28 -13
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_range.rb +6 -0
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
- data/lib/active_support/core_ext/regexp.rb +0 -4
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +8 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +7 -2
- data/lib/active_support/core_ext/string/multibyte.rb +4 -3
- data/lib/active_support/core_ext/string/output_safety.rb +68 -10
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +34 -3
- data/lib/active_support/core_ext/uri.rb +1 -0
- data/lib/active_support/current_attributes.rb +8 -0
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/dependencies.rb +74 -18
- data/lib/active_support/deprecation/behaviors.rb +1 -1
- data/lib/active_support/deprecation/method_wrappers.rb +17 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +28 -5
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/descendants_tracker.rb +55 -9
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +3 -5
- data/lib/active_support/duration.rb +7 -8
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +3 -2
- data/lib/active_support/evented_file_update_checker.rb +39 -10
- data/lib/active_support/execution_wrapper.rb +2 -1
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +22 -18
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +13 -1
- data/lib/active_support/inflector/inflections.rb +1 -5
- data/lib/active_support/inflector/methods.rb +16 -29
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +23 -24
- data/lib/active_support/json/encoding.rb +6 -2
- data/lib/active_support/key_generator.rb +0 -32
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/log_subscriber.rb +31 -9
- data/lib/active_support/logger.rb +1 -16
- data/lib/active_support/logger_silence.rb +28 -12
- data/lib/active_support/logger_thread_safe_level.rb +26 -4
- data/lib/active_support/message_encryptor.rb +4 -6
- data/lib/active_support/message_verifier.rb +5 -5
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotator.rb +4 -4
- data/lib/active_support/multibyte/chars.rb +29 -49
- data/lib/active_support/multibyte/unicode.rb +44 -282
- data/lib/active_support/notifications/fanout.rb +98 -13
- data/lib/active_support/notifications/instrumenter.rb +80 -9
- data/lib/active_support/notifications.rb +41 -4
- data/lib/active_support/number_helper/number_converter.rb +4 -5
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -4
- data/lib/active_support/number_helper/rounding_helper.rb +1 -1
- data/lib/active_support/number_helper.rb +11 -0
- data/lib/active_support/option_merger.rb +21 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +5 -1
- data/lib/active_support/parameter_filter.rb +128 -0
- data/lib/active_support/rails.rb +0 -6
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/string_inquirer.rb +0 -1
- data/lib/active_support/subscriber.rb +65 -26
- data/lib/active_support/tagged_logging.rb +13 -4
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +15 -1
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +134 -0
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +7 -9
- data/lib/active_support/time_with_zone.rb +15 -5
- data/lib/active_support/values/time_zone.rb +12 -7
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support.rb +2 -1
- metadata +38 -9
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- 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
|
-
#
|
17
|
-
# and publish it.
|
18
|
-
#
|
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,20 @@ 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
|
-
|
55
|
+
attr_reader :name, :time, :end, :transaction_id, :payload, :children
|
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?
|
57
63
|
|
58
64
|
def initialize(name, start, ending, transaction_id, payload)
|
59
65
|
@name = name
|
@@ -62,7 +68,47 @@ module ActiveSupport
|
|
62
68
|
@transaction_id = transaction_id
|
63
69
|
@end = ending
|
64
70
|
@children = []
|
65
|
-
@
|
71
|
+
@cpu_time_start = 0
|
72
|
+
@cpu_time_finish = 0
|
73
|
+
@allocation_count_start = 0
|
74
|
+
@allocation_count_finish = 0
|
75
|
+
end
|
76
|
+
|
77
|
+
# Record information at the time this event starts
|
78
|
+
def start!
|
79
|
+
@time = now
|
80
|
+
@cpu_time_start = now_cpu
|
81
|
+
@allocation_count_start = now_allocations
|
82
|
+
end
|
83
|
+
|
84
|
+
# Record information at the time this event finishes
|
85
|
+
def finish!
|
86
|
+
@cpu_time_finish = now_cpu
|
87
|
+
@end = now
|
88
|
+
@allocation_count_finish = now_allocations
|
89
|
+
end
|
90
|
+
|
91
|
+
def end=(ending)
|
92
|
+
ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
|
93
|
+
@end = ending
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the CPU time (in milliseconds) passed since the call to
|
97
|
+
# +start!+ and the call to +finish!+
|
98
|
+
def cpu_time
|
99
|
+
(@cpu_time_finish - @cpu_time_start) * 1000
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the idle time time (in milliseconds) passed since the call to
|
103
|
+
# +start!+ and the call to +finish!+
|
104
|
+
def idle_time
|
105
|
+
duration - cpu_time
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the number of allocations made since the call to +start!+ and
|
109
|
+
# the call to +finish!+
|
110
|
+
def allocations
|
111
|
+
@allocation_count_finish - @allocation_count_start
|
66
112
|
end
|
67
113
|
|
68
114
|
# Returns the difference in milliseconds between when the execution of the
|
@@ -78,7 +124,7 @@ module ActiveSupport
|
|
78
124
|
#
|
79
125
|
# @event.duration # => 1000.138
|
80
126
|
def duration
|
81
|
-
|
127
|
+
1000.0 * (self.end - time)
|
82
128
|
end
|
83
129
|
|
84
130
|
def <<(event)
|
@@ -88,6 +134,31 @@ module ActiveSupport
|
|
88
134
|
def parent_of?(event)
|
89
135
|
@children.include? event
|
90
136
|
end
|
137
|
+
|
138
|
+
private
|
139
|
+
def now
|
140
|
+
Concurrent.monotonic_time
|
141
|
+
end
|
142
|
+
|
143
|
+
if clock_gettime_supported?
|
144
|
+
def now_cpu
|
145
|
+
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
|
146
|
+
end
|
147
|
+
else
|
148
|
+
def now_cpu
|
149
|
+
0
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
if defined?(JRUBY_VERSION)
|
154
|
+
def now_allocations
|
155
|
+
0
|
156
|
+
end
|
157
|
+
else
|
158
|
+
def now_allocations
|
159
|
+
GC.stat :total_allocated_objects
|
160
|
+
end
|
161
|
+
end
|
91
162
|
end
|
92
163
|
end
|
93
164
|
end
|
@@ -34,7 +34,7 @@ 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
|
37
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
38
38
|
# payload # => Hash, the payload
|
39
39
|
# end
|
40
40
|
#
|
@@ -59,7 +59,7 @@ module ActiveSupport
|
|
59
59
|
# event.payload # => { extra: :information }
|
60
60
|
#
|
61
61
|
# 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
|
62
|
+
# timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
|
63
63
|
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
|
64
64
|
# that order.
|
65
65
|
#
|
@@ -67,9 +67,12 @@ module ActiveSupport
|
|
67
67
|
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
|
68
68
|
# the name of the exception class, and the exception message.
|
69
69
|
# The <tt>:exception_object</tt> key of the payload will have the exception
|
70
|
-
# itself as the value
|
70
|
+
# itself as the value:
|
71
71
|
#
|
72
|
-
#
|
72
|
+
# event.payload[:exception] # => ["ArgumentError", "Invalid value"]
|
73
|
+
# event.payload[:exception_object] # => #<ArgumentError: Invalid value>
|
74
|
+
#
|
75
|
+
# As the earlier example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
|
73
76
|
# is able to take the arguments as they come and provide an object-oriented
|
74
77
|
# interface to that data.
|
75
78
|
#
|
@@ -150,6 +153,15 @@ module ActiveSupport
|
|
150
153
|
#
|
151
154
|
# ActiveSupport::Notifications.unsubscribe("render")
|
152
155
|
#
|
156
|
+
# Subscribers using a regexp or other pattern-matching object will remain subscribed
|
157
|
+
# to all events that match their original pattern, unless those events match a string
|
158
|
+
# passed to `unsubscribe`:
|
159
|
+
#
|
160
|
+
# subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
|
161
|
+
# ActiveSupport::Notifications.unsubscribe('render_template.action_view')
|
162
|
+
# subscriber.matches?('render_template.action_view') # => false
|
163
|
+
# subscriber.matches?('render_partial.action_view') # => true
|
164
|
+
#
|
153
165
|
# == Default Queue
|
154
166
|
#
|
155
167
|
# Notifications ships with a queue implementation that consumes and publishes events
|
@@ -171,6 +183,31 @@ module ActiveSupport
|
|
171
183
|
end
|
172
184
|
end
|
173
185
|
|
186
|
+
# Subscribe to a given event name with the passed +block+.
|
187
|
+
#
|
188
|
+
# You can subscribe to events by passing a String to match exact event
|
189
|
+
# names, or by passing a Regexp to match all events that match a pattern.
|
190
|
+
#
|
191
|
+
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
|
192
|
+
# @event = ActiveSupport::Notifications::Event.new(*args)
|
193
|
+
# end
|
194
|
+
#
|
195
|
+
# The +block+ will receive five parameters with information about the event:
|
196
|
+
#
|
197
|
+
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
|
198
|
+
# name # => String, name of the event (such as 'render' from above)
|
199
|
+
# start # => Time, when the instrumented block started execution
|
200
|
+
# finish # => Time, when the instrumented block ended execution
|
201
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
202
|
+
# payload # => Hash, the payload
|
203
|
+
# end
|
204
|
+
#
|
205
|
+
# If the block passed to the method only takes one parameter,
|
206
|
+
# it will yield an event object to the block:
|
207
|
+
#
|
208
|
+
# ActiveSupport::Notifications.subscribe(/render/) do |event|
|
209
|
+
# @event = event
|
210
|
+
# end
|
174
211
|
def subscribe(*args, &block)
|
175
212
|
notifier.subscribe(*args, &block)
|
176
213
|
end
|
@@ -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/
|
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.
|
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"
|
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("."
|
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:
|
@@ -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"
|
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:
|
@@ -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"
|
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"
|
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:
|
@@ -20,9 +22,9 @@ module ActiveSupport
|
|
20
22
|
formatted_string =
|
21
23
|
if BigDecimal === rounded_number && rounded_number.finite?
|
22
24
|
s = rounded_number.to_s("F")
|
23
|
-
s << "0"
|
24
|
-
a, b = s.split("."
|
25
|
-
a << "."
|
25
|
+
s << "0" * precision
|
26
|
+
a, b = s.split(".", 2)
|
27
|
+
a << "."
|
26
28
|
a << b[0, precision]
|
27
29
|
else
|
28
30
|
"%00.#{precision}f" % rounded_number
|
@@ -36,7 +38,6 @@ module ActiveSupport
|
|
36
38
|
end
|
37
39
|
|
38
40
|
private
|
39
|
-
|
40
41
|
def strip_insignificant_zeros
|
41
42
|
options[:strip_insignificant_zeros]
|
42
43
|
end
|
@@ -26,7 +26,7 @@ module ActiveSupport
|
|
26
26
|
|
27
27
|
private
|
28
28
|
def round_without_significant(number)
|
29
|
-
number = number.round(precision)
|
29
|
+
number = number.round(precision, BigDecimal.mode(BigDecimal::ROUND_MODE))
|
30
30
|
number = number.to_i if precision == 0 && number.finite?
|
31
31
|
number = number.abs if number.zero? # prevent showing negative zeros
|
32
32
|
number
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/dependencies/autoload"
|
4
|
+
|
3
5
|
module ActiveSupport
|
4
6
|
module NumberHelper
|
5
7
|
extend ActiveSupport::Autoload
|
@@ -85,6 +87,9 @@ module ActiveSupport
|
|
85
87
|
# number given by <tt>:format</tt>). Accepts the same fields
|
86
88
|
# than <tt>:format</tt>, except <tt>%n</tt> is here the
|
87
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+).
|
88
93
|
#
|
89
94
|
# ==== Examples
|
90
95
|
#
|
@@ -94,12 +99,18 @@ module ActiveSupport
|
|
94
99
|
# number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €"
|
95
100
|
# number_to_currency('123a456') # => "$123a456"
|
96
101
|
#
|
102
|
+
# number_to_currency("123a456", raise: true) # => InvalidNumberError
|
103
|
+
#
|
104
|
+
# number_to_currency(-0.456789, precision: 0)
|
105
|
+
# # => "$0"
|
97
106
|
# number_to_currency(-1234567890.50, negative_format: '(%u%n)')
|
98
107
|
# # => "($1,234,567,890.50)"
|
99
108
|
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '')
|
100
109
|
# # => "£1234567890,50"
|
101
110
|
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '', format: '%n %u')
|
102
111
|
# # => "1234567890,50 £"
|
112
|
+
# number_to_currency(1234567890.50, strip_insignificant_zeros: true)
|
113
|
+
# # => "$1,234,567,890.5"
|
103
114
|
def number_to_currency(number, options = {})
|
104
115
|
NumberToCurrencyConverter.convert(number, options)
|
105
116
|
end
|
@@ -5,7 +5,7 @@ require "active_support/core_ext/hash/deep_merge"
|
|
5
5
|
module ActiveSupport
|
6
6
|
class OptionMerger #:nodoc:
|
7
7
|
instance_methods.each do |method|
|
8
|
-
undef_method(method) if
|
8
|
+
undef_method(method) if !/^(__|instance_eval|class|object_id)/.match?(method)
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(context, options)
|
@@ -14,14 +14,32 @@ module ActiveSupport
|
|
14
14
|
|
15
15
|
private
|
16
16
|
def method_missing(method, *arguments, &block)
|
17
|
+
options = nil
|
17
18
|
if arguments.first.is_a?(Proc)
|
18
19
|
proc = arguments.pop
|
19
20
|
arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
|
21
|
+
elsif arguments.last.respond_to?(:to_hash)
|
22
|
+
options = @options.deep_merge(arguments.pop)
|
20
23
|
else
|
21
|
-
|
24
|
+
options = @options
|
22
25
|
end
|
23
26
|
|
24
|
-
|
27
|
+
invoke_method(method, arguments, options, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
if RUBY_VERSION >= "2.7"
|
31
|
+
def invoke_method(method, arguments, options, &block)
|
32
|
+
if options
|
33
|
+
@context.__send__(method, *arguments, **options, &block)
|
34
|
+
else
|
35
|
+
@context.__send__(method, *arguments, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
def invoke_method(method, arguments, options, &block)
|
40
|
+
arguments << options.dup if options
|
41
|
+
@context.__send__(method, *arguments, &block)
|
42
|
+
end
|
25
43
|
end
|
26
44
|
end
|
27
45
|
end
|
@@ -16,7 +16,7 @@ module ActiveSupport
|
|
16
16
|
# oh.keys # => [:a, :b], this order is guaranteed
|
17
17
|
#
|
18
18
|
# Also, maps the +omap+ feature for YAML files
|
19
|
-
# (See
|
19
|
+
# (See https://yaml.org/type/omap.html) to support ordered items
|
20
20
|
# when loading from yaml.
|
21
21
|
#
|
22
22
|
# <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
|
@@ -39,7 +39,7 @@ module ActiveSupport
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def method_missing(name, *args)
|
42
|
-
name_string = name.to_s
|
42
|
+
name_string = +name.to_s
|
43
43
|
if name_string.chomp!("=")
|
44
44
|
self[name_string] = args.first
|
45
45
|
else
|
@@ -56,6 +56,10 @@ module ActiveSupport
|
|
56
56
|
def respond_to_missing?(name, include_private)
|
57
57
|
true
|
58
58
|
end
|
59
|
+
|
60
|
+
def extractable_options?
|
61
|
+
true
|
62
|
+
end
|
59
63
|
end
|
60
64
|
|
61
65
|
# +InheritableOptions+ provides a constructor to build an +OrderedOptions+
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/duplicable"
|
4
|
+
require "active_support/core_ext/array/extract"
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
# +ParameterFilter+ allows you to specify keys for sensitive data from
|
8
|
+
# hash-like object and replace corresponding value. Filtering only certain
|
9
|
+
# sub-keys from a hash is possible by using the dot notation:
|
10
|
+
# 'credit_card.number'. If a proc is given, each key and value of a hash and
|
11
|
+
# all sub-hashes are passed to it, where the value or the key can be replaced
|
12
|
+
# using String#replace or similar methods.
|
13
|
+
#
|
14
|
+
# ActiveSupport::ParameterFilter.new([:password])
|
15
|
+
# => replaces the value to all keys matching /password/i with "[FILTERED]"
|
16
|
+
#
|
17
|
+
# ActiveSupport::ParameterFilter.new([:foo, "bar"])
|
18
|
+
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
|
19
|
+
#
|
20
|
+
# ActiveSupport::ParameterFilter.new(["credit_card.code"])
|
21
|
+
# => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
|
22
|
+
# change { file: { code: "xxxx"} }
|
23
|
+
#
|
24
|
+
# ActiveSupport::ParameterFilter.new([-> (k, v) do
|
25
|
+
# v.reverse! if k =~ /secret/i
|
26
|
+
# end])
|
27
|
+
# => reverses the value to all keys matching /secret/i
|
28
|
+
class ParameterFilter
|
29
|
+
FILTERED = "[FILTERED]" # :nodoc:
|
30
|
+
|
31
|
+
# Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
|
32
|
+
# Other types of filters are treated as +String+ using +to_s+.
|
33
|
+
# For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
|
34
|
+
#
|
35
|
+
# ==== Options
|
36
|
+
#
|
37
|
+
# * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
|
38
|
+
def initialize(filters = [], mask: FILTERED)
|
39
|
+
@filters = filters
|
40
|
+
@mask = mask
|
41
|
+
end
|
42
|
+
|
43
|
+
# Mask value of +params+ if key matches one of filters.
|
44
|
+
def filter(params)
|
45
|
+
compiled_filter.call(params)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
|
49
|
+
def filter_param(key, value)
|
50
|
+
@filters.empty? ? value : compiled_filter.value_for_key(key, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def compiled_filter
|
55
|
+
@compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
|
56
|
+
end
|
57
|
+
|
58
|
+
class CompiledFilter # :nodoc:
|
59
|
+
def self.compile(filters, mask:)
|
60
|
+
return lambda { |params| params.dup } if filters.empty?
|
61
|
+
|
62
|
+
strings, regexps, blocks = [], [], []
|
63
|
+
|
64
|
+
filters.each do |item|
|
65
|
+
case item
|
66
|
+
when Proc
|
67
|
+
blocks << item
|
68
|
+
when Regexp
|
69
|
+
regexps << item
|
70
|
+
else
|
71
|
+
strings << Regexp.escape(item.to_s)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
|
76
|
+
deep_strings = strings.extract! { |s| s.include?("\\.") }
|
77
|
+
|
78
|
+
regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
|
79
|
+
deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
|
80
|
+
|
81
|
+
new regexps, deep_regexps, blocks, mask: mask
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :regexps, :deep_regexps, :blocks
|
85
|
+
|
86
|
+
def initialize(regexps, deep_regexps, blocks, mask:)
|
87
|
+
@regexps = regexps
|
88
|
+
@deep_regexps = deep_regexps.any? ? deep_regexps : nil
|
89
|
+
@blocks = blocks
|
90
|
+
@mask = mask
|
91
|
+
end
|
92
|
+
|
93
|
+
def call(params, parents = [], original_params = params)
|
94
|
+
filtered_params = params.class.new
|
95
|
+
|
96
|
+
params.each do |key, value|
|
97
|
+
filtered_params[key] = value_for_key(key, value, parents, original_params)
|
98
|
+
end
|
99
|
+
|
100
|
+
filtered_params
|
101
|
+
end
|
102
|
+
|
103
|
+
def value_for_key(key, value, parents = [], original_params = nil)
|
104
|
+
parents.push(key) if deep_regexps
|
105
|
+
if regexps.any? { |r| r.match?(key.to_s) }
|
106
|
+
value = @mask
|
107
|
+
elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
|
108
|
+
value = @mask
|
109
|
+
elsif value.is_a?(Hash)
|
110
|
+
value = call(value, parents, original_params)
|
111
|
+
elsif value.is_a?(Array)
|
112
|
+
# If we don't pop the current parent it will be duplicated as we
|
113
|
+
# process each array value.
|
114
|
+
parents.pop if deep_regexps
|
115
|
+
value = value.map { |v| value_for_key(key, v, parents, original_params) }
|
116
|
+
# Restore the parent stack after processing the array.
|
117
|
+
parents.push(key) if deep_regexps
|
118
|
+
elsif blocks.any?
|
119
|
+
key = key.dup if key.duplicable?
|
120
|
+
value = value.dup if value.duplicable?
|
121
|
+
blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
|
122
|
+
end
|
123
|
+
parents.pop if deep_regexps
|
124
|
+
value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/lib/active_support/rails.rb
CHANGED
@@ -27,9 +27,3 @@ require "active_support/core_ext/module/delegation"
|
|
27
27
|
|
28
28
|
# Defines ActiveSupport::Deprecation.
|
29
29
|
require "active_support/deprecation"
|
30
|
-
|
31
|
-
# Defines Regexp#match?.
|
32
|
-
#
|
33
|
-
# This should be removed when Rails needs Ruby 2.4 or later, and the require
|
34
|
-
# added where other Regexp extensions are being used (easy to grep).
|
35
|
-
require "active_support/core_ext/regexp"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/execution_wrapper"
|
4
|
+
require "active_support/executor"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
#--
|
@@ -49,11 +50,9 @@ module ActiveSupport
|
|
49
50
|
def self.reload!
|
50
51
|
executor.wrap do
|
51
52
|
new.tap do |instance|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
instance.complete!
|
56
|
-
end
|
53
|
+
instance.run!
|
54
|
+
ensure
|
55
|
+
instance.complete!
|
57
56
|
end
|
58
57
|
end
|
59
58
|
prepare!
|