activesupport 5.2.4.3 → 7.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +244 -459
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +31 -5
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +47 -41
- data/lib/active_support/cache/mem_cache_store.rb +151 -40
- data/lib/active_support/cache/memory_store.rb +68 -34
- data/lib/active_support/cache/null_store.rb +16 -3
- data/lib/active_support/cache/redis_cache_store.rb +103 -101
- data/lib/active_support/cache/strategy/local_cache.rb +56 -64
- data/lib/active_support/cache.rb +333 -116
- data/lib/active_support/callbacks.rb +244 -128
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +72 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -3
- data/lib/active_support/configurable.rb +15 -16
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +15 -7
- data/lib/active_support/core_ext/array/conversions.rb +18 -17
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/array.rb +2 -1
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +9 -22
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +15 -14
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
- 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/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +241 -76
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +3 -4
- 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/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +2 -31
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- 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/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.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/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +70 -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/module.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -1
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +3 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +14 -110
- data/lib/active_support/core_ext/object/json.rb +44 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +24 -14
- data/lib/active_support/core_ext/object/with_options.rb +21 -2
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +23 -27
- data/lib/active_support/core_ext/range/conversions.rb +32 -30
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -2
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/range.rb +1 -1
- 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 +3 -2
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +46 -7
- data/lib/active_support/core_ext/string/inquiry.rb +2 -1
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +129 -20
- 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/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +59 -10
- data/lib/active_support/core_ext/time/conversions.rb +15 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +7 -22
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -22
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +47 -16
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +60 -715
- data/lib/active_support/deprecation/behaviors.rb +21 -5
- 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 +31 -8
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +7 -2
- data/lib/active_support/descendants_tracker.rb +190 -34
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +5 -7
- data/lib/active_support/duration/iso8601_serializer.rb +27 -15
- data/lib/active_support/duration.rb +149 -67
- data/lib/active_support/encrypted_configuration.rb +12 -5
- data/lib/active_support/encrypted_file.rb +23 -5
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +85 -122
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +44 -21
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +5 -5
- data/lib/active_support/hash_with_indifferent_access.rb +73 -43
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +2 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +25 -14
- data/lib/active_support/inflector/methods.rb +38 -71
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/isolated_execution_state.rb +72 -0
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +14 -6
- data/lib/active_support/key_generator.rb +23 -38
- data/lib/active_support/lazy_load_hooks.rb +19 -5
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +51 -11
- data/lib/active_support/logger.rb +6 -22
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +45 -10
- data/lib/active_support/message_encryptor.rb +20 -19
- data/lib/active_support/message_verifier.rb +53 -21
- data/lib/active_support/messages/metadata.rb +13 -4
- 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 +17 -76
- data/lib/active_support/multibyte/unicode.rb +7 -331
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +163 -37
- data/lib/active_support/notifications/instrumenter.rb +90 -11
- data/lib/active_support/notifications.rb +88 -30
- data/lib/active_support/number_helper/number_converter.rb +6 -9
- data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
- 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 +5 -4
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +36 -12
- data/lib/active_support/option_merger.rb +15 -4
- data/lib/active_support/ordered_hash.rb +2 -2
- data/lib/active_support/ordered_options.rb +14 -4
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/reloader.rb +5 -6
- data/lib/active_support/rescuable.rb +8 -8
- data/lib/active_support/ruby_features.rb +7 -0
- 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 +2 -3
- data/lib/active_support/subscriber.rb +79 -46
- data/lib/active_support/tagged_logging.rb +58 -9
- data/lib/active_support/test_case.rb +79 -0
- data/lib/active_support/testing/assertions.rb +62 -11
- data/lib/active_support/testing/deprecation.rb +52 -2
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +4 -4
- data/lib/active_support/testing/method_call_assertions.rb +32 -5
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +55 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +4 -7
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +60 -14
- data/lib/active_support/time_with_zone.rb +139 -64
- data/lib/active_support/values/time_zone.rb +66 -30
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -4
- data/lib/active_support/xml_mini/libxml.rb +7 -7
- data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
- data/lib/active_support/xml_mini/nokogiri.rb +6 -6
- data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
- data/lib/active_support/xml_mini/rexml.rb +11 -4
- data/lib/active_support/xml_mini.rb +7 -14
- data/lib/active_support.rb +30 -1
- metadata +64 -35
- 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/marshal.rb +0 -24
- 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
@@ -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
|
@@ -8,24 +8,24 @@ module ActiveSupport
|
|
8
8
|
self.namespace = :currency
|
9
9
|
|
10
10
|
def convert
|
11
|
-
number = self.number.to_s.strip
|
12
11
|
format = options[:format]
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
number_f = valid_float?
|
14
|
+
if number_f
|
15
|
+
if number_f.negative?
|
16
|
+
number_f = number_f.abs
|
17
|
+
format = options[:negative_format] if (number_f * 10**options[:precision]) >= 0.5
|
18
|
+
end
|
19
|
+
number_s = NumberToRoundedConverter.convert(number_f, options)
|
20
|
+
else
|
21
|
+
number_s = number.to_s.strip
|
22
|
+
format = options[:negative_format] if number_s.sub!(/^-/, "")
|
17
23
|
end
|
18
24
|
|
19
|
-
|
20
|
-
format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
|
25
|
+
format.gsub("%n", number_s).gsub("%u", options[:unit])
|
21
26
|
end
|
22
27
|
|
23
28
|
private
|
24
|
-
|
25
|
-
def absolute_value(number)
|
26
|
-
number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "")
|
27
|
-
end
|
28
|
-
|
29
29
|
def options
|
30
30
|
@options ||= begin
|
31
31
|
defaults = default_format_options.merge(i18n_opts)
|
@@ -1,8 +1,10 @@
|
|
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
|
-
class NumberToDelimitedConverter < NumberConverter
|
7
|
+
class NumberToDelimitedConverter < NumberConverter # :nodoc:
|
6
8
|
self.validate_float = true
|
7
9
|
|
8
10
|
DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
|
@@ -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,8 +1,10 @@
|
|
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
|
-
class NumberToHumanSizeConverter < NumberConverter
|
7
|
+
class NumberToHumanSizeConverter < NumberConverter # :nodoc:
|
6
8
|
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb]
|
7
9
|
|
8
10
|
self.namespace = :human
|
@@ -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,8 +1,10 @@
|
|
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
|
-
class NumberToPhoneConverter < NumberConverter
|
7
|
+
class NumberToPhoneConverter < NumberConverter # :nodoc:
|
6
8
|
def convert
|
7
9
|
str = country_code(opts[:country_code]).dup
|
8
10
|
str << convert_to_phone_number(number.to_s.strip)
|
@@ -10,7 +12,6 @@ module ActiveSupport
|
|
10
12
|
end
|
11
13
|
|
12
14
|
private
|
13
|
-
|
14
15
|
def convert_to_phone_number(number)
|
15
16
|
if opts[:area_code]
|
16
17
|
convert_with_area_code(number)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/number_helper/number_converter"
|
4
|
+
|
3
5
|
module ActiveSupport
|
4
6
|
module NumberHelper
|
5
7
|
class NumberToRoundedConverter < NumberConverter # :nodoc:
|
@@ -18,14 +20,18 @@ module ActiveSupport
|
|
18
20
|
end
|
19
21
|
|
20
22
|
formatted_string =
|
21
|
-
if
|
23
|
+
if rounded_number.finite?
|
22
24
|
s = rounded_number.to_s("F")
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
a, b = s.split(".", 2)
|
26
|
+
if precision != 0
|
27
|
+
b << "0" * precision
|
28
|
+
a << "."
|
29
|
+
a << b[0, precision]
|
30
|
+
end
|
31
|
+
a
|
27
32
|
else
|
28
|
-
|
33
|
+
# Infinity/NaN
|
34
|
+
"%f" % rounded_number
|
29
35
|
end
|
30
36
|
else
|
31
37
|
formatted_string = rounded_number
|
@@ -36,7 +42,6 @@ module ActiveSupport
|
|
36
42
|
end
|
37
43
|
|
38
44
|
private
|
39
|
-
|
40
45
|
def strip_insignificant_zeros
|
41
46
|
options[:strip_insignificant_zeros]
|
42
47
|
end
|
@@ -10,56 +10,36 @@ 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]
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
def absolute_number(number)
|
62
|
-
number.respond_to?(:abs) ? number.abs : number.to_d.abs
|
37
|
+
def absolute_precision(number)
|
38
|
+
if options[:significant] && options[:precision] > 0
|
39
|
+
options[:precision] - digit_count(convert_to_decimal(number))
|
40
|
+
else
|
41
|
+
options[:precision]
|
42
|
+
end
|
63
43
|
end
|
64
44
|
end
|
65
45
|
end
|
@@ -71,6 +71,8 @@ module ActiveSupport
|
|
71
71
|
# (defaults to current locale).
|
72
72
|
# * <tt>:precision</tt> - Sets the level of precision (defaults
|
73
73
|
# to 2).
|
74
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
75
|
+
# (defaults to :default. See BigDecimal::mode)
|
74
76
|
# * <tt>:unit</tt> - Sets the denomination of the currency
|
75
77
|
# (defaults to "$").
|
76
78
|
# * <tt>:separator</tt> - Sets the separator between the units
|
@@ -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(-0.456789, precision: 0)
|
103
|
+
# # => "$0"
|
97
104
|
# number_to_currency(-1234567890.50, negative_format: '(%u%n)')
|
98
105
|
# # => "($1,234,567,890.50)"
|
99
106
|
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '')
|
100
107
|
# # => "£1234567890,50"
|
101
108
|
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '', format: '%n %u')
|
102
109
|
# # => "1234567890,50 £"
|
110
|
+
# number_to_currency(1234567890.50, strip_insignificant_zeros: true)
|
111
|
+
# # => "$1,234,567,890.5"
|
112
|
+
# number_to_currency(1234567890.50, precision: 0, round_mode: :up)
|
113
|
+
# # => "$1,234,567,891"
|
103
114
|
def number_to_currency(number, options = {})
|
104
115
|
NumberToCurrencyConverter.convert(number, options)
|
105
116
|
end
|
@@ -113,6 +124,8 @@ module ActiveSupport
|
|
113
124
|
# (defaults to current locale).
|
114
125
|
# * <tt>:precision</tt> - Sets the precision of the number
|
115
126
|
# (defaults to 3). Keeps the number's precision if +nil+.
|
127
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
128
|
+
# (defaults to :default. See BigDecimal::mode)
|
116
129
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
117
130
|
# of significant_digits. If +false+, the number of fractional
|
118
131
|
# digits (defaults to +false+).
|
@@ -128,15 +141,16 @@ module ActiveSupport
|
|
128
141
|
#
|
129
142
|
# ==== Examples
|
130
143
|
#
|
131
|
-
# number_to_percentage(100)
|
132
|
-
# number_to_percentage('98')
|
133
|
-
# number_to_percentage(100, precision: 0)
|
134
|
-
# number_to_percentage(1000, delimiter: '.', separator: ',')
|
135
|
-
# number_to_percentage(302.24398923423, precision: 5)
|
136
|
-
# number_to_percentage(1000, locale: :fr)
|
137
|
-
# number_to_percentage(1000, precision: nil)
|
138
|
-
# number_to_percentage('98a')
|
139
|
-
# number_to_percentage(100, format: '%n %')
|
144
|
+
# number_to_percentage(100) # => "100.000%"
|
145
|
+
# number_to_percentage('98') # => "98.000%"
|
146
|
+
# number_to_percentage(100, precision: 0) # => "100%"
|
147
|
+
# number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
|
148
|
+
# number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
|
149
|
+
# number_to_percentage(1000, locale: :fr) # => "1000,000%"
|
150
|
+
# number_to_percentage(1000, precision: nil) # => "1000%"
|
151
|
+
# number_to_percentage('98a') # => "98a%"
|
152
|
+
# number_to_percentage(100, format: '%n %') # => "100.000 %"
|
153
|
+
# number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%"
|
140
154
|
def number_to_percentage(number, options = {})
|
141
155
|
NumberToPercentageConverter.convert(number, options)
|
142
156
|
end
|
@@ -187,6 +201,8 @@ module ActiveSupport
|
|
187
201
|
# (defaults to current locale).
|
188
202
|
# * <tt>:precision</tt> - Sets the precision of the number
|
189
203
|
# (defaults to 3). Keeps the number's precision if +nil+.
|
204
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
205
|
+
# (defaults to :default. See BigDecimal::mode)
|
190
206
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
191
207
|
# of significant_digits. If +false+, the number of fractional
|
192
208
|
# digits (defaults to +false+).
|
@@ -208,6 +224,7 @@ module ActiveSupport
|
|
208
224
|
# number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
|
209
225
|
# number_to_rounded(13, precision: 5, significant: true) # => "13.000"
|
210
226
|
# number_to_rounded(13, precision: nil) # => "13"
|
227
|
+
# number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
|
211
228
|
# number_to_rounded(111.234, locale: :fr) # => "111,234"
|
212
229
|
#
|
213
230
|
# number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
|
@@ -221,7 +238,7 @@ module ActiveSupport
|
|
221
238
|
end
|
222
239
|
|
223
240
|
# Formats the bytes in +number+ into a more understandable
|
224
|
-
# representation (e.g., giving it 1500 yields 1.
|
241
|
+
# representation (e.g., giving it 1500 yields 1.46 KB). This
|
225
242
|
# method is useful for reporting file sizes to users. You can
|
226
243
|
# customize the format in the +options+ hash.
|
227
244
|
#
|
@@ -234,6 +251,8 @@ module ActiveSupport
|
|
234
251
|
# (defaults to current locale).
|
235
252
|
# * <tt>:precision</tt> - Sets the precision of the number
|
236
253
|
# (defaults to 3).
|
254
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
255
|
+
# (defaults to :default. See BigDecimal::mode)
|
237
256
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
238
257
|
# of significant_digits. If +false+, the number of fractional
|
239
258
|
# digits (defaults to +true+)
|
@@ -257,6 +276,7 @@ module ActiveSupport
|
|
257
276
|
# number_to_human_size(1234567890123456789) # => "1.07 EB"
|
258
277
|
# number_to_human_size(1234567, precision: 2) # => "1.2 MB"
|
259
278
|
# number_to_human_size(483989, precision: 2) # => "470 KB"
|
279
|
+
# number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
|
260
280
|
# number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
|
261
281
|
# number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
|
262
282
|
# number_to_human_size(524288000, precision: 5) # => "500 MB"
|
@@ -265,7 +285,7 @@ module ActiveSupport
|
|
265
285
|
end
|
266
286
|
|
267
287
|
# Pretty prints (formats and approximates) a number in a way it
|
268
|
-
# is more readable by humans (
|
288
|
+
# is more readable by humans (e.g.: 1200000000 becomes "1.2
|
269
289
|
# Billion"). This is useful for numbers that can get very large
|
270
290
|
# (and too hard to read).
|
271
291
|
#
|
@@ -273,7 +293,7 @@ module ActiveSupport
|
|
273
293
|
# size.
|
274
294
|
#
|
275
295
|
# You can also define your own unit-quantifier names if you want
|
276
|
-
# to use other decimal units (
|
296
|
+
# to use other decimal units (e.g.: 1500 becomes "1.5
|
277
297
|
# kilometers", 0.150 becomes "150 milliliters", etc). You may
|
278
298
|
# define a wide range of unit quantifiers, even fractional ones
|
279
299
|
# (centi, deci, mili, etc).
|
@@ -284,6 +304,8 @@ module ActiveSupport
|
|
284
304
|
# (defaults to current locale).
|
285
305
|
# * <tt>:precision</tt> - Sets the precision of the number
|
286
306
|
# (defaults to 3).
|
307
|
+
# * <tt>:round_mode</tt> - Determine how rounding is performed
|
308
|
+
# (defaults to :default. See BigDecimal::mode)
|
287
309
|
# * <tt>:significant</tt> - If +true+, precision will be the number
|
288
310
|
# of significant_digits. If +false+, the number of fractional
|
289
311
|
# digits (defaults to +true+)
|
@@ -321,6 +343,8 @@ module ActiveSupport
|
|
321
343
|
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
|
322
344
|
# number_to_human(489939, precision: 2) # => "490 Thousand"
|
323
345
|
# number_to_human(489939, precision: 4) # => "489.9 Thousand"
|
346
|
+
# number_to_human(489939, precision: 2
|
347
|
+
# , round_mode: :down) # => "480 Thousand"
|
324
348
|
# number_to_human(1234567, precision: 4,
|
325
349
|
# significant: false) # => "1.2346 Million"
|
326
350
|
# number_to_human(1234567, precision: 1,
|
@@ -3,9 +3,9 @@
|
|
3
3
|
require "active_support/core_ext/hash/deep_merge"
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
|
-
class OptionMerger
|
6
|
+
class OptionMerger # :nodoc:
|
7
7
|
instance_methods.each do |method|
|
8
|
-
undef_method(method)
|
8
|
+
undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(context, options)
|
@@ -14,14 +14,25 @@ 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
|
+
if options
|
28
|
+
@context.__send__(method, *arguments, **options, &block)
|
29
|
+
else
|
30
|
+
@context.__send__(method, *arguments, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to_missing?(*arguments)
|
35
|
+
@context.respond_to?(*arguments)
|
25
36
|
end
|
26
37
|
end
|
27
38
|
end
|
@@ -16,12 +16,12 @@ 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
|
23
23
|
# with other implementations.
|
24
|
-
class OrderedHash < ::Hash
|
24
|
+
class OrderedHash < ::Hash # :nodoc:
|
25
25
|
def to_yaml_type
|
26
26
|
"!tag:yaml.org,2002:omap"
|
27
27
|
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'
|
@@ -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,9 +58,17 @@ 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
|
-
# +InheritableOptions+ provides a constructor to build an
|
71
|
+
# +InheritableOptions+ provides a constructor to build an OrderedOptions
|
62
72
|
# hash inherited from another hash.
|
63
73
|
#
|
64
74
|
# Use this if you already have some hash and you want to create a new one based on it.
|
@@ -0,0 +1,138 @@
|
|
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([/\Apin\z/i, /\Apin_/i])
|
20
|
+
# => replaces the value for the exact (case-insensitive) key 'pin' and all
|
21
|
+
# (case-insensitive) keys beginning with 'pin_', with "[FILTERED]".
|
22
|
+
# Does not match keys with 'pin' as a substring, such as 'shipping_id'.
|
23
|
+
#
|
24
|
+
# ActiveSupport::ParameterFilter.new(["credit_card.code"])
|
25
|
+
# => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
|
26
|
+
# change { file: { code: "xxxx"} }
|
27
|
+
#
|
28
|
+
# ActiveSupport::ParameterFilter.new([-> (k, v) do
|
29
|
+
# v.reverse! if /secret/i.match?(k)
|
30
|
+
# end])
|
31
|
+
# => reverses the value to all keys matching /secret/i
|
32
|
+
class ParameterFilter
|
33
|
+
FILTERED = "[FILTERED]" # :nodoc:
|
34
|
+
|
35
|
+
# Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
|
36
|
+
# Other types of filters are treated as +String+ using +to_s+.
|
37
|
+
# For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
|
38
|
+
#
|
39
|
+
# ==== Options
|
40
|
+
#
|
41
|
+
# * <tt>:mask</tt> - A replaced object when filtered. Defaults to <tt>"[FILTERED]"</tt>.
|
42
|
+
def initialize(filters = [], mask: FILTERED)
|
43
|
+
@filters = filters
|
44
|
+
@mask = mask
|
45
|
+
end
|
46
|
+
|
47
|
+
# Mask value of +params+ if key matches one of filters.
|
48
|
+
def filter(params)
|
49
|
+
compiled_filter.call(params)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
|
53
|
+
def filter_param(key, value)
|
54
|
+
@filters.empty? ? value : compiled_filter.value_for_key(key, value)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def compiled_filter
|
59
|
+
@compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
|
60
|
+
end
|
61
|
+
|
62
|
+
class CompiledFilter # :nodoc:
|
63
|
+
def self.compile(filters, mask:)
|
64
|
+
return lambda { |params| params.dup } if filters.empty?
|
65
|
+
|
66
|
+
strings, regexps, blocks, deep_regexps, deep_strings = [], [], [], nil, nil
|
67
|
+
|
68
|
+
filters.each do |item|
|
69
|
+
case item
|
70
|
+
when Proc
|
71
|
+
blocks << item
|
72
|
+
when Regexp
|
73
|
+
if item.to_s.include?("\\.")
|
74
|
+
(deep_regexps ||= []) << item
|
75
|
+
else
|
76
|
+
regexps << item
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s = Regexp.escape(item.to_s)
|
80
|
+
if s.include?("\\.")
|
81
|
+
(deep_strings ||= []) << s
|
82
|
+
else
|
83
|
+
strings << s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
|
89
|
+
(deep_regexps ||= []) << Regexp.new(deep_strings.join("|"), true) if deep_strings&.any?
|
90
|
+
|
91
|
+
new regexps, deep_regexps, blocks, mask: mask
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_reader :regexps, :deep_regexps, :blocks
|
95
|
+
|
96
|
+
def initialize(regexps, deep_regexps, blocks, mask:)
|
97
|
+
@regexps = regexps
|
98
|
+
@deep_regexps = deep_regexps&.any? ? deep_regexps : nil
|
99
|
+
@blocks = blocks
|
100
|
+
@mask = mask
|
101
|
+
end
|
102
|
+
|
103
|
+
def call(params, parents = [], original_params = params)
|
104
|
+
filtered_params = params.class.new
|
105
|
+
|
106
|
+
params.each do |key, value|
|
107
|
+
filtered_params[key] = value_for_key(key, value, parents, original_params)
|
108
|
+
end
|
109
|
+
|
110
|
+
filtered_params
|
111
|
+
end
|
112
|
+
|
113
|
+
def value_for_key(key, value, parents = [], original_params = nil)
|
114
|
+
parents.push(key) if deep_regexps
|
115
|
+
if regexps.any? { |r| r.match?(key.to_s) }
|
116
|
+
value = @mask
|
117
|
+
elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
|
118
|
+
value = @mask
|
119
|
+
elsif value.is_a?(Hash)
|
120
|
+
value = call(value, parents, original_params)
|
121
|
+
elsif value.is_a?(Array)
|
122
|
+
# If we don't pop the current parent it will be duplicated as we
|
123
|
+
# process each array value.
|
124
|
+
parents.pop if deep_regexps
|
125
|
+
value = value.map { |v| value_for_key(key, v, parents, original_params) }
|
126
|
+
# Restore the parent stack after processing the array.
|
127
|
+
parents.push(key) if deep_regexps
|
128
|
+
elsif blocks.any?
|
129
|
+
key = key.dup if key.duplicable?
|
130
|
+
value = value.dup if value.duplicable?
|
131
|
+
blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
|
132
|
+
end
|
133
|
+
parents.pop if deep_regexps
|
134
|
+
value
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|