activesupport 5.2.4.4 → 6.1.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 +353 -435
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support.rb +14 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache.rb +142 -78
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +32 -20
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +70 -43
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +70 -3
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/core_ext/array.rb +1 -1
- 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/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- 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 +171 -75
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- 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 +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- 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/marshal.rb +2 -0
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +14 -2
- 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 +34 -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_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +70 -13
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +6 -1
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/dependencies.rb +109 -34
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/deprecation/behaviors.rb +16 -3
- 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 +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +22 -4
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +64 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +49 -58
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +50 -6
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- 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 +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- 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 +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- 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 -3
- data/lib/active_support/subscriber.rb +72 -28
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +30 -9
- 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 +51 -0
- 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/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +32 -17
- data/lib/active_support/xml_mini.rb +2 -10
- 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 +10 -3
- metadata +58 -32
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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
|
|
@@ -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:
|
@@ -14,7 +16,7 @@ module ActiveSupport
|
|
14
16
|
@number = RoundingHelper.new(options).round(number)
|
15
17
|
@number = Float(number)
|
16
18
|
|
17
|
-
#
|
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"
|
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
|
-
#
|
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"
|
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:
|
@@ -18,14 +20,14 @@ module ActiveSupport
|
|
18
20
|
end
|
19
21
|
|
20
22
|
formatted_string =
|
21
|
-
if
|
23
|
+
if rounded_number.nan? || rounded_number.infinite? || rounded_number == rounded_number.to_i
|
24
|
+
"%00.#{precision}f" % rounded_number
|
25
|
+
else
|
22
26
|
s = rounded_number.to_s("F")
|
23
|
-
s << "0"
|
24
|
-
a, b = s.split("."
|
25
|
-
a << "."
|
27
|
+
s << "0" * precision
|
28
|
+
a, b = s.split(".", 2)
|
29
|
+
a << "."
|
26
30
|
a << b[0, precision]
|
27
|
-
else
|
28
|
-
"%00.#{precision}f" % rounded_number
|
29
31
|
end
|
30
32
|
else
|
31
33
|
formatted_string = 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
|
@@ -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))
|
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,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)
|
@@ -14,14 +15,32 @@ module ActiveSupport
|
|
14
15
|
|
15
16
|
private
|
16
17
|
def method_missing(method, *arguments, &block)
|
18
|
+
options = nil
|
17
19
|
if arguments.first.is_a?(Proc)
|
18
20
|
proc = arguments.pop
|
19
21
|
arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
|
22
|
+
elsif arguments.last.respond_to?(:to_hash)
|
23
|
+
options = @options.deep_merge(arguments.pop)
|
20
24
|
else
|
21
|
-
|
25
|
+
options = @options
|
22
26
|
end
|
23
27
|
|
24
|
-
|
28
|
+
invoke_method(method, arguments, options, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
if RUBY_VERSION >= "2.7"
|
32
|
+
def invoke_method(method, arguments, options, &block)
|
33
|
+
if options
|
34
|
+
@context.__send__(method, *arguments, **options, &block)
|
35
|
+
else
|
36
|
+
@context.__send__(method, *arguments, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
else
|
40
|
+
def invoke_method(method, arguments, options, &block)
|
41
|
+
arguments << options.dup if options
|
42
|
+
@context.__send__(method, *arguments, &block)
|
43
|
+
end
|
25
44
|
end
|
26
45
|
end
|
27
46
|
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
|
@@ -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'
|
@@ -39,7 +41,7 @@ module ActiveSupport
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def method_missing(name, *args)
|
42
|
-
name_string = name.to_s
|
44
|
+
name_string = +name.to_s
|
43
45
|
if name_string.chomp!("=")
|
44
46
|
self[name_string] = args.first
|
45
47
|
else
|
@@ -56,6 +58,14 @@ module ActiveSupport
|
|
56
58
|
def respond_to_missing?(name, include_private)
|
57
59
|
true
|
58
60
|
end
|
61
|
+
|
62
|
+
def extractable_options?
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
"#<#{self.class.name} #{super}>"
|
68
|
+
end
|
59
69
|
end
|
60
70
|
|
61
71
|
# +InheritableOptions+ provides a constructor to build an +OrderedOptions+
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/duplicable"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
# +ParameterFilter+ allows you to specify keys for sensitive data from
|
7
|
+
# hash-like object and replace corresponding value. Filtering only certain
|
8
|
+
# sub-keys from a hash is possible by using the dot notation:
|
9
|
+
# 'credit_card.number'. If a proc is given, each key and value of a hash and
|
10
|
+
# all sub-hashes are passed to it, where the value or the key can be replaced
|
11
|
+
# using String#replace or similar methods.
|
12
|
+
#
|
13
|
+
# ActiveSupport::ParameterFilter.new([:password])
|
14
|
+
# => replaces the value to all keys matching /password/i with "[FILTERED]"
|
15
|
+
#
|
16
|
+
# ActiveSupport::ParameterFilter.new([:foo, "bar"])
|
17
|
+
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
|
18
|
+
#
|
19
|
+
# ActiveSupport::ParameterFilter.new(["credit_card.code"])
|
20
|
+
# => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
|
21
|
+
# change { file: { code: "xxxx"} }
|
22
|
+
#
|
23
|
+
# ActiveSupport::ParameterFilter.new([-> (k, v) do
|
24
|
+
# v.reverse! if /secret/i.match?(k)
|
25
|
+
# end])
|
26
|
+
# => reverses the value to all keys matching /secret/i
|
27
|
+
class ParameterFilter
|
28
|
+
FILTERED = "[FILTERED]" # :nodoc:
|
29
|
+
|
30
|
+
# Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
|
31
|
+
# Other types of filters are treated as +String+ using +to_s+.
|
32
|
+
# For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
|
33
|
+
#
|
34
|
+
# ==== Options
|
35
|
+
#
|
36
|
+
# * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
|
37
|
+
def initialize(filters = [], mask: FILTERED)
|
38
|
+
@filters = filters
|
39
|
+
@mask = mask
|
40
|
+
end
|
41
|
+
|
42
|
+
# Mask value of +params+ if key matches one of filters.
|
43
|
+
def filter(params)
|
44
|
+
compiled_filter.call(params)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
|
48
|
+
def filter_param(key, value)
|
49
|
+
@filters.empty? ? value : compiled_filter.value_for_key(key, value)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def compiled_filter
|
54
|
+
@compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
|
55
|
+
end
|
56
|
+
|
57
|
+
class CompiledFilter # :nodoc:
|
58
|
+
def self.compile(filters, mask:)
|
59
|
+
return lambda { |params| params.dup } if filters.empty?
|
60
|
+
|
61
|
+
strings, regexps, blocks, deep_regexps, deep_strings = [], [], [], nil, nil
|
62
|
+
|
63
|
+
filters.each do |item|
|
64
|
+
case item
|
65
|
+
when Proc
|
66
|
+
blocks << item
|
67
|
+
when Regexp
|
68
|
+
if item.to_s.include?("\\.")
|
69
|
+
(deep_regexps ||= []) << item
|
70
|
+
else
|
71
|
+
regexps << item
|
72
|
+
end
|
73
|
+
else
|
74
|
+
s = Regexp.escape(item.to_s)
|
75
|
+
if s.include?("\\.")
|
76
|
+
(deep_strings ||= []) << s
|
77
|
+
else
|
78
|
+
strings << s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
|
84
|
+
(deep_regexps ||= []) << Regexp.new(deep_strings.join("|"), true) if deep_strings&.any?
|
85
|
+
|
86
|
+
new regexps, deep_regexps, blocks, mask: mask
|
87
|
+
end
|
88
|
+
|
89
|
+
attr_reader :regexps, :deep_regexps, :blocks
|
90
|
+
|
91
|
+
def initialize(regexps, deep_regexps, blocks, mask:)
|
92
|
+
@regexps = regexps
|
93
|
+
@deep_regexps = deep_regexps&.any? ? deep_regexps : nil
|
94
|
+
@blocks = blocks
|
95
|
+
@mask = mask
|
96
|
+
end
|
97
|
+
|
98
|
+
def call(params, parents = [], original_params = params)
|
99
|
+
filtered_params = params.class.new
|
100
|
+
|
101
|
+
params.each do |key, value|
|
102
|
+
filtered_params[key] = value_for_key(key, value, parents, original_params)
|
103
|
+
end
|
104
|
+
|
105
|
+
filtered_params
|
106
|
+
end
|
107
|
+
|
108
|
+
def value_for_key(key, value, parents = [], original_params = nil)
|
109
|
+
parents.push(key) if deep_regexps
|
110
|
+
if regexps.any? { |r| r.match?(key.to_s) }
|
111
|
+
value = @mask
|
112
|
+
elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
|
113
|
+
value = @mask
|
114
|
+
elsif value.is_a?(Hash)
|
115
|
+
value = call(value, parents, original_params)
|
116
|
+
elsif value.is_a?(Array)
|
117
|
+
# If we don't pop the current parent it will be duplicated as we
|
118
|
+
# process each array value.
|
119
|
+
parents.pop if deep_regexps
|
120
|
+
value = value.map { |v| value_for_key(key, v, parents, original_params) }
|
121
|
+
# Restore the parent stack after processing the array.
|
122
|
+
parents.push(key) if deep_regexps
|
123
|
+
elsif blocks.any?
|
124
|
+
key = key.dup if key.duplicable?
|
125
|
+
value = value.dup if value.duplicable?
|
126
|
+
blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
|
127
|
+
end
|
128
|
+
parents.pop if deep_regexps
|
129
|
+
value
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|