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
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
class DateTime
|
6
|
+
NOT_SET = Object.new # :nodoc:
|
7
|
+
def to_s(format = NOT_SET) # :nodoc:
|
8
|
+
if formatter = ::Time::DATE_FORMATS[format]
|
9
|
+
ActiveSupport::Deprecation.warn(
|
10
|
+
"DateTime#to_s(#{format.inspect}) is deprecated. Please use DateTime#to_fs(#{format.inspect}) instead."
|
11
|
+
)
|
12
|
+
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
|
13
|
+
elsif format == NOT_SET
|
14
|
+
to_default_s
|
15
|
+
else
|
16
|
+
ActiveSupport::Deprecation.warn(
|
17
|
+
"DateTime#to_s(#{format.inspect}) is deprecated. Please use DateTime#to_fs(#{format.inspect}) instead."
|
18
|
+
)
|
19
|
+
to_default_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -5,3 +5,4 @@ require "active_support/core_ext/date_time/blank"
|
|
5
5
|
require "active_support/core_ext/date_time/calculations"
|
6
6
|
require "active_support/core_ext/date_time/compatibility"
|
7
7
|
require "active_support/core_ext/date_time/conversions"
|
8
|
+
require "active_support/core_ext/date_time/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
|
@@ -1,29 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "securerandom"
|
4
|
+
require "openssl"
|
4
5
|
|
5
6
|
module Digest
|
6
7
|
module UUID
|
7
|
-
DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8"
|
8
|
-
URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8"
|
9
|
-
OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8"
|
10
|
-
X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8"
|
8
|
+
DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
9
|
+
URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
10
|
+
OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
11
|
+
X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
12
|
+
|
13
|
+
mattr_accessor :use_rfc4122_namespaced_uuids, instance_accessor: false, default: false
|
11
14
|
|
12
15
|
# Generates a v5 non-random UUID (Universally Unique IDentifier).
|
13
16
|
#
|
14
|
-
# Using Digest::MD5 generates version 3 UUIDs; Digest::SHA1 generates version 5 UUIDs.
|
17
|
+
# Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs.
|
15
18
|
# uuid_from_hash always generates the same UUID for a given name and namespace combination.
|
16
19
|
#
|
17
20
|
# See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
|
18
|
-
def self.uuid_from_hash(hash_class,
|
19
|
-
if hash_class == Digest::MD5
|
21
|
+
def self.uuid_from_hash(hash_class, namespace, name)
|
22
|
+
if hash_class == Digest::MD5 || hash_class == OpenSSL::Digest::MD5
|
20
23
|
version = 3
|
21
|
-
elsif hash_class == Digest::SHA1
|
24
|
+
elsif hash_class == Digest::SHA1 || hash_class == OpenSSL::Digest::SHA1
|
22
25
|
version = 5
|
23
26
|
else
|
24
|
-
raise ArgumentError, "Expected Digest::SHA1 or Digest::MD5, got #{hash_class.name}."
|
27
|
+
raise ArgumentError, "Expected OpenSSL::Digest::SHA1 or OpenSSL::Digest::MD5, got #{hash_class.name}."
|
25
28
|
end
|
26
29
|
|
30
|
+
uuid_namespace = pack_uuid_namespace(namespace)
|
31
|
+
|
27
32
|
hash = hash_class.new
|
28
33
|
hash.update(uuid_namespace)
|
29
34
|
hash.update(name)
|
@@ -35,19 +40,40 @@ module Digest
|
|
35
40
|
"%08x-%04x-%04x-%04x-%04x%08x" % ary
|
36
41
|
end
|
37
42
|
|
38
|
-
# Convenience method for uuid_from_hash using Digest::MD5.
|
43
|
+
# Convenience method for uuid_from_hash using OpenSSL::Digest::MD5.
|
39
44
|
def self.uuid_v3(uuid_namespace, name)
|
40
|
-
uuid_from_hash(Digest::MD5, uuid_namespace, name)
|
45
|
+
uuid_from_hash(OpenSSL::Digest::MD5, uuid_namespace, name)
|
41
46
|
end
|
42
47
|
|
43
|
-
# Convenience method for uuid_from_hash using Digest::SHA1.
|
48
|
+
# Convenience method for uuid_from_hash using OpenSSL::Digest::SHA1.
|
44
49
|
def self.uuid_v5(uuid_namespace, name)
|
45
|
-
uuid_from_hash(Digest::SHA1, uuid_namespace, name)
|
50
|
+
uuid_from_hash(OpenSSL::Digest::SHA1, uuid_namespace, name)
|
46
51
|
end
|
47
52
|
|
48
53
|
# Convenience method for SecureRandom.uuid.
|
49
54
|
def self.uuid_v4
|
50
55
|
SecureRandom.uuid
|
51
56
|
end
|
57
|
+
|
58
|
+
def self.pack_uuid_namespace(namespace)
|
59
|
+
if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
|
60
|
+
namespace
|
61
|
+
elsif use_rfc4122_namespaced_uuids == true
|
62
|
+
match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
|
63
|
+
|
64
|
+
raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?
|
65
|
+
|
66
|
+
match_data.captures.map { |s| s.to_i(16) }.pack("NnnnnN")
|
67
|
+
else
|
68
|
+
ActiveSupport::Deprecation.warn <<~WARNING.squish
|
69
|
+
Providing a namespace ID that is not one of the constants defined on Digest::UUID generates an incorrect UUID value according to RFC 4122.
|
70
|
+
To enable the correct behavior, set the Rails.application.config.active_support.use_rfc4122_namespaced_uuids configuration option to true.
|
71
|
+
WARNING
|
72
|
+
|
73
|
+
namespace
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private_class_method :pack_uuid_namespace
|
52
78
|
end
|
53
79
|
end
|
@@ -1,64 +1,103 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module ActiveSupport
|
4
|
+
module EnumerableCoreExt # :nodoc:
|
5
|
+
module Constants
|
6
|
+
private
|
7
|
+
def const_missing(name)
|
8
|
+
if name == :SoleItemExpectedError
|
9
|
+
::ActiveSupport::EnumerableCoreExt::SoleItemExpectedError
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
3
18
|
module Enumerable
|
19
|
+
# Error generated by +sole+ when called on an enumerable that doesn't have
|
20
|
+
# exactly one item.
|
21
|
+
class SoleItemExpectedError < StandardError; end
|
22
|
+
|
23
|
+
# HACK: For performance reasons, Enumerable shouldn't have any constants of its own.
|
24
|
+
# So we move SoleItemExpectedError into ActiveSupport::EnumerableCoreExt.
|
25
|
+
ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError)
|
26
|
+
singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants)
|
27
|
+
|
4
28
|
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
5
29
|
# when we omit an identity.
|
30
|
+
|
31
|
+
# :stopdoc:
|
32
|
+
|
33
|
+
# We can't use Refinements here because Refinements with Module which will be prepended
|
34
|
+
# doesn't work well https://bugs.ruby-lang.org/issues/13446
|
35
|
+
alias :_original_sum_with_required_identity :sum
|
36
|
+
private :_original_sum_with_required_identity
|
37
|
+
|
38
|
+
# :startdoc:
|
39
|
+
|
40
|
+
# Calculates the minimum from the extracted elements.
|
6
41
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
sum
|
54
|
-
|
42
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
43
|
+
# payments.minimum(:price) # => 5
|
44
|
+
def minimum(key)
|
45
|
+
map(&key).min
|
46
|
+
end
|
47
|
+
|
48
|
+
# Calculates the maximum from the extracted elements.
|
49
|
+
#
|
50
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
51
|
+
# payments.maximum(:price) # => 15
|
52
|
+
def maximum(key)
|
53
|
+
map(&key).max
|
54
|
+
end
|
55
|
+
|
56
|
+
# Calculates a sum from the elements.
|
57
|
+
#
|
58
|
+
# payments.sum { |p| p.price * p.tax_rate }
|
59
|
+
# payments.sum(&:price)
|
60
|
+
#
|
61
|
+
# The latter is a shortcut for:
|
62
|
+
#
|
63
|
+
# payments.inject(0) { |sum, p| sum + p.price }
|
64
|
+
#
|
65
|
+
# It can also calculate the sum without the use of a block.
|
66
|
+
#
|
67
|
+
# [5, 15, 10].sum # => 30
|
68
|
+
# ['foo', 'bar'].sum('') # => "foobar"
|
69
|
+
# [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
|
70
|
+
#
|
71
|
+
# The default sum of an empty list is zero. You can override this default:
|
72
|
+
#
|
73
|
+
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
74
|
+
def sum(identity = nil, &block)
|
75
|
+
if identity
|
76
|
+
_original_sum_with_required_identity(identity, &block)
|
77
|
+
elsif block_given?
|
78
|
+
map(&block).sum
|
79
|
+
# we check `first(1) == []` to check if we have an
|
80
|
+
# empty Enumerable; checking `empty?` would return
|
81
|
+
# true for `[nil]`, which we want to deprecate to
|
82
|
+
# keep consistent with Ruby
|
83
|
+
elsif first.is_a?(Numeric) || first(1) == []
|
84
|
+
identity ||= 0
|
85
|
+
_original_sum_with_required_identity(identity, &block)
|
86
|
+
else
|
87
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
88
|
+
Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
|
89
|
+
Sum of non-numeric elements requires an initial argument.
|
90
|
+
MSG
|
91
|
+
inject(:+) || 0
|
55
92
|
end
|
56
93
|
end
|
57
94
|
|
58
|
-
# Convert an enumerable to a hash
|
95
|
+
# Convert an enumerable to a hash, using the block result as the key and the
|
96
|
+
# element as the value.
|
59
97
|
#
|
60
98
|
# people.index_by(&:login)
|
61
99
|
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
100
|
+
#
|
62
101
|
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
|
63
102
|
# # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
|
64
103
|
def index_by
|
@@ -71,6 +110,33 @@ module Enumerable
|
|
71
110
|
end
|
72
111
|
end
|
73
112
|
|
113
|
+
# Convert an enumerable to a hash, using the element as the key and the block
|
114
|
+
# result as the value.
|
115
|
+
#
|
116
|
+
# post = Post.new(title: "hey there", body: "what's up?")
|
117
|
+
#
|
118
|
+
# %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
|
119
|
+
# # => { title: "hey there", body: "what's up?" }
|
120
|
+
#
|
121
|
+
# If an argument is passed instead of a block, it will be used as the value
|
122
|
+
# for all elements:
|
123
|
+
#
|
124
|
+
# %i( created_at updated_at ).index_with(Time.now)
|
125
|
+
# # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
|
126
|
+
def index_with(default = (no_default = true))
|
127
|
+
if block_given?
|
128
|
+
result = {}
|
129
|
+
each { |elem| result[elem] = yield(elem) }
|
130
|
+
result
|
131
|
+
elsif no_default
|
132
|
+
to_enum(:index_with) { size if respond_to?(:size) }
|
133
|
+
else
|
134
|
+
result = {}
|
135
|
+
each { |elem| result[elem] = default }
|
136
|
+
result
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
74
140
|
# Returns +true+ if the enumerable has more than 1 element. Functionally
|
75
141
|
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
|
76
142
|
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
|
@@ -87,24 +153,40 @@ module Enumerable
|
|
87
153
|
end
|
88
154
|
end
|
89
155
|
|
156
|
+
# Returns a new array that includes the passed elements.
|
157
|
+
#
|
158
|
+
# [ 1, 2, 3 ].including(4, 5)
|
159
|
+
# # => [ 1, 2, 3, 4, 5 ]
|
160
|
+
#
|
161
|
+
# ["David", "Rafael"].including %w[ Aaron Todd ]
|
162
|
+
# # => ["David", "Rafael", "Aaron", "Todd"]
|
163
|
+
def including(*elements)
|
164
|
+
to_a.including(*elements)
|
165
|
+
end
|
166
|
+
|
90
167
|
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
|
91
168
|
# collection does not include the object.
|
92
169
|
def exclude?(object)
|
93
170
|
!include?(object)
|
94
171
|
end
|
95
172
|
|
96
|
-
# Returns a copy of the enumerable
|
173
|
+
# Returns a copy of the enumerable excluding the specified elements.
|
97
174
|
#
|
98
|
-
# ["David", "Rafael", "Aaron", "Todd"].
|
175
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
|
99
176
|
# # => ["David", "Rafael"]
|
100
177
|
#
|
101
|
-
#
|
178
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
|
179
|
+
# # => ["David", "Rafael"]
|
180
|
+
#
|
181
|
+
# {foo: 1, bar: 2, baz: 3}.excluding :bar
|
102
182
|
# # => {foo: 1, baz: 3}
|
103
|
-
def
|
183
|
+
def excluding(*elements)
|
184
|
+
elements.flatten!(1)
|
104
185
|
reject { |element| elements.include?(element) }
|
105
186
|
end
|
187
|
+
alias :without :excluding
|
106
188
|
|
107
|
-
#
|
189
|
+
# Extract the given key from each element in the enumerable.
|
108
190
|
#
|
109
191
|
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
|
110
192
|
# # => ["David", "Rafael", "Aaron"]
|
@@ -115,12 +197,91 @@ module Enumerable
|
|
115
197
|
if keys.many?
|
116
198
|
map { |element| keys.map { |key| element[key] } }
|
117
199
|
else
|
118
|
-
|
200
|
+
key = keys.first
|
201
|
+
map { |element| element[key] }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Extract the given key from the first element in the enumerable.
|
206
|
+
#
|
207
|
+
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
|
208
|
+
# # => "David"
|
209
|
+
#
|
210
|
+
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
|
211
|
+
# # => [1, "David"]
|
212
|
+
def pick(*keys)
|
213
|
+
return if none?
|
214
|
+
|
215
|
+
if keys.many?
|
216
|
+
keys.map { |key| first[key] }
|
217
|
+
else
|
218
|
+
first[keys.first]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Returns a new +Array+ without the blank items.
|
223
|
+
# Uses Object#blank? for determining if an item is blank.
|
224
|
+
#
|
225
|
+
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
|
226
|
+
# # => [1, 2, true]
|
227
|
+
#
|
228
|
+
# Set.new([nil, "", 1, 2])
|
229
|
+
# # => [2, 1] (or [1, 2])
|
230
|
+
#
|
231
|
+
# When called on a +Hash+, returns a new +Hash+ without the blank values.
|
232
|
+
#
|
233
|
+
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
|
234
|
+
# # => { b: 1, f: true }
|
235
|
+
def compact_blank
|
236
|
+
reject(&:blank?)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
|
240
|
+
# objects in the original enumerable.
|
241
|
+
#
|
242
|
+
# [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
|
243
|
+
# # => [ Person.find(1), Person.find(5), Person.find(3) ]
|
244
|
+
#
|
245
|
+
# If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
|
246
|
+
# If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
|
247
|
+
def in_order_of(key, series)
|
248
|
+
index_by(&key).values_at(*series).compact
|
249
|
+
end
|
250
|
+
|
251
|
+
# Returns the sole item in the enumerable. If there are no items, or more
|
252
|
+
# than one item, raises +Enumerable::SoleItemExpectedError+.
|
253
|
+
#
|
254
|
+
# ["x"].sole # => "x"
|
255
|
+
# Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
|
256
|
+
# { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
|
257
|
+
def sole
|
258
|
+
case count
|
259
|
+
when 1 then return first # rubocop:disable Style/RedundantReturn
|
260
|
+
when 0 then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found"
|
261
|
+
when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found"
|
119
262
|
end
|
120
263
|
end
|
121
264
|
end
|
122
265
|
|
123
|
-
class
|
266
|
+
class Hash
|
267
|
+
# Hash#reject has its own definition, so this needs one too.
|
268
|
+
def compact_blank # :nodoc:
|
269
|
+
reject { |_k, v| v.blank? }
|
270
|
+
end
|
271
|
+
|
272
|
+
# Removes all blank values from the +Hash+ in place and returns self.
|
273
|
+
# Uses Object#blank? for determining if a value is blank.
|
274
|
+
#
|
275
|
+
# h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
|
276
|
+
# h.compact_blank!
|
277
|
+
# # => { b: 1, f: true }
|
278
|
+
def compact_blank!
|
279
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
280
|
+
delete_if { |_k, v| v.blank? }
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class Range # :nodoc:
|
124
285
|
# Optimize range sum to use arithmetic progression if a block is not given and
|
125
286
|
# we have a range of numeric values.
|
126
287
|
def sum(identity = nil)
|
@@ -138,27 +299,31 @@ class Range #:nodoc:
|
|
138
299
|
end
|
139
300
|
end
|
140
301
|
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
# Using Refinements here in order not to expose our internal method
|
148
|
-
using Module.new {
|
149
|
-
refine Array do
|
150
|
-
alias :orig_sum :sum
|
151
|
-
end
|
152
|
-
}
|
302
|
+
# Using Refinements here in order not to expose our internal method
|
303
|
+
using Module.new {
|
304
|
+
refine Array do
|
305
|
+
alias :orig_sum :sum
|
306
|
+
end
|
307
|
+
}
|
153
308
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
end
|
309
|
+
class Array # :nodoc:
|
310
|
+
def sum(init = nil, &block)
|
311
|
+
if init.is_a?(Numeric) || first.is_a?(Numeric)
|
312
|
+
init ||= 0
|
313
|
+
orig_sum(init, &block)
|
314
|
+
else
|
315
|
+
super
|
162
316
|
end
|
163
317
|
end
|
318
|
+
|
319
|
+
# Removes all blank elements from the +Array+ in place and returns self.
|
320
|
+
# Uses Object#blank? for determining if an item is blank.
|
321
|
+
#
|
322
|
+
# a = [1, "", nil, 2, " ", [], {}, false, true]
|
323
|
+
# a.compact_blank!
|
324
|
+
# # => [1, 2, true]
|
325
|
+
def compact_blank!
|
326
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
327
|
+
delete_if(&:blank?)
|
328
|
+
end
|
164
329
|
end
|
@@ -53,7 +53,7 @@ class File
|
|
53
53
|
end
|
54
54
|
|
55
55
|
# Private utility method.
|
56
|
-
def self.probe_stat_in(dir)
|
56
|
+
def self.probe_stat_in(dir) # :nodoc:
|
57
57
|
basename = [
|
58
58
|
".permissions_check",
|
59
59
|
Thread.current.object_id,
|
@@ -64,6 +64,8 @@ class File
|
|
64
64
|
file_name = join(dir, basename)
|
65
65
|
FileUtils.touch(file_name)
|
66
66
|
stat(file_name)
|
67
|
+
rescue Errno::ENOENT
|
68
|
+
file_name = nil
|
67
69
|
ensure
|
68
70
|
FileUtils.rm_f(file_name) if file_name
|
69
71
|
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/xml_mini"
|
4
|
-
require "active_support/time"
|
5
3
|
require "active_support/core_ext/object/blank"
|
6
4
|
require "active_support/core_ext/object/to_param"
|
7
5
|
require "active_support/core_ext/object/to_query"
|
6
|
+
require "active_support/core_ext/object/try"
|
8
7
|
require "active_support/core_ext/array/wrap"
|
9
8
|
require "active_support/core_ext/hash/reverse_merge"
|
10
9
|
require "active_support/core_ext/string/inflections"
|
@@ -73,7 +72,7 @@ class Hash
|
|
73
72
|
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
|
74
73
|
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
|
75
74
|
def to_xml(options = {})
|
76
|
-
require "active_support/builder" unless defined?(Builder)
|
75
|
+
require "active_support/builder" unless defined?(Builder::XmlMarkup)
|
77
76
|
|
78
77
|
options = options.dup
|
79
78
|
options[:indent] ||= 2
|
@@ -208,7 +207,7 @@ module ActiveSupport
|
|
208
207
|
elsif become_empty_string?(value)
|
209
208
|
""
|
210
209
|
elsif become_hash?(value)
|
211
|
-
xml_value =
|
210
|
+
xml_value = value.transform_values { |v| deep_to_h(v) }
|
212
211
|
|
213
212
|
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
|
214
213
|
# how multipart uploaded files from HTML appear
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
# Returns a new hash with all values converted by the block operation.
|
5
|
+
# This includes the values from the root hash and from all
|
6
|
+
# nested hashes and arrays.
|
7
|
+
#
|
8
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
9
|
+
#
|
10
|
+
# hash.deep_transform_values{ |value| value.to_s.upcase }
|
11
|
+
# # => {person: {name: "ROB", age: "28"}}
|
12
|
+
def deep_transform_values(&block)
|
13
|
+
_deep_transform_values_in_object(self, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Destructively converts all values by using the block operation.
|
17
|
+
# This includes the values from the root hash and from all
|
18
|
+
# nested hashes and arrays.
|
19
|
+
def deep_transform_values!(&block)
|
20
|
+
_deep_transform_values_in_object!(self, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# Support methods for deep transforming nested hashes and arrays.
|
25
|
+
def _deep_transform_values_in_object(object, &block)
|
26
|
+
case object
|
27
|
+
when Hash
|
28
|
+
object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
|
29
|
+
when Array
|
30
|
+
object.map { |e| _deep_transform_values_in_object(e, &block) }
|
31
|
+
else
|
32
|
+
yield(object)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def _deep_transform_values_in_object!(object, &block)
|
37
|
+
case object
|
38
|
+
when Hash
|
39
|
+
object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
|
40
|
+
when Array
|
41
|
+
object.map! { |e| _deep_transform_values_in_object!(e, &block) }
|
42
|
+
else
|
43
|
+
yield(object)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -10,8 +10,8 @@ class Hash
|
|
10
10
|
# This is useful for limiting a set of parameters to everything but a few known toggles:
|
11
11
|
# @person.update(params[:person].except(:admin))
|
12
12
|
def except(*keys)
|
13
|
-
|
14
|
-
end
|
13
|
+
slice(*self.keys - keys)
|
14
|
+
end unless method_defined?(:except)
|
15
15
|
|
16
16
|
# Removes the given keys from hash and returns it.
|
17
17
|
# hash = { a: true, b: false, c: nil }
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/hash_with_indifferent_access"
|
4
4
|
|
5
5
|
class Hash
|
6
|
-
# Returns an
|
6
|
+
# Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
|
7
7
|
#
|
8
8
|
# { a: 1 }.with_indifferent_access['a'] # => 1
|
9
9
|
def with_indifferent_access
|
@@ -13,8 +13,8 @@ class Hash
|
|
13
13
|
# Called when object is nested under an object that receives
|
14
14
|
# #with_indifferent_access. This method will be called on the current object
|
15
15
|
# by the enclosing object and is aliased to #with_indifferent_access by
|
16
|
-
# default. Subclasses of Hash may
|
17
|
-
# converting to an
|
16
|
+
# default. Subclasses of Hash may override this method to return +self+ if
|
17
|
+
# converting to an ActiveSupport::HashWithIndifferentAccess would not be
|
18
18
|
# desirable.
|
19
19
|
#
|
20
20
|
# b = { b: 1 }
|
@@ -1,35 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Hash
|
4
|
-
# Returns a new hash with all keys converted using the +block+ operation.
|
5
|
-
#
|
6
|
-
# hash = { name: 'Rob', age: '28' }
|
7
|
-
#
|
8
|
-
# hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
|
9
|
-
#
|
10
|
-
# If you do not provide a +block+, it will return an Enumerator
|
11
|
-
# for chaining with other methods:
|
12
|
-
#
|
13
|
-
# hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"}
|
14
|
-
def transform_keys
|
15
|
-
return enum_for(:transform_keys) { size } unless block_given?
|
16
|
-
result = {}
|
17
|
-
each_key do |key|
|
18
|
-
result[yield(key)] = self[key]
|
19
|
-
end
|
20
|
-
result
|
21
|
-
end unless method_defined? :transform_keys
|
22
|
-
|
23
|
-
# Destructively converts all keys using the +block+ operations.
|
24
|
-
# Same as +transform_keys+ but modifies +self+.
|
25
|
-
def transform_keys!
|
26
|
-
return enum_for(:transform_keys!) { size } unless block_given?
|
27
|
-
keys.each do |key|
|
28
|
-
self[yield(key)] = delete(key)
|
29
|
-
end
|
30
|
-
self
|
31
|
-
end unless method_defined? :transform_keys!
|
32
|
-
|
33
4
|
# Returns a new hash with all keys converted to strings.
|
34
5
|
#
|
35
6
|
# hash = { name: 'Rob', age: '28' }
|
@@ -141,11 +112,11 @@ class Hash
|
|
141
112
|
end
|
142
113
|
|
143
114
|
private
|
144
|
-
#
|
115
|
+
# Support methods for deep transforming nested hashes and arrays.
|
145
116
|
def _deep_transform_keys_in_object(object, &block)
|
146
117
|
case object
|
147
118
|
when Hash
|
148
|
-
object.each_with_object(
|
119
|
+
object.each_with_object(self.class.new) do |(key, value), result|
|
149
120
|
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
150
121
|
end
|
151
122
|
when Array
|