activesupport 6.0.3.7 → 7.0.0
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 +220 -533
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_support/actionable_error.rb +1 -1
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -3
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +18 -11
- data/lib/active_support/cache/mem_cache_store.rb +143 -37
- data/lib/active_support/cache/memory_store.rb +56 -28
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +63 -88
- data/lib/active_support/cache/strategy/local_cache.rb +46 -57
- data/lib/active_support/cache.rb +273 -82
- data/lib/active_support/callbacks.rb +226 -118
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +49 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +9 -6
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +9 -7
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array.rb +1 -0
- 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 +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +21 -40
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +4 -4
- data/lib/active_support/core_ext/date/conversions.rb +5 -4
- 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 +13 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
- 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 +139 -15
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +2 -2
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +2 -2
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- 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 +25 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +40 -36
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +11 -0
- data/lib/active_support/core_ext/object/json.rb +42 -26
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with_options.rb +20 -1
- 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 +6 -25
- data/lib/active_support/core_ext/range/conversions.rb +8 -8
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +39 -5
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +69 -45
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- 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 +26 -6
- data/lib/active_support/core_ext/time/conversions.rb +6 -3
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +4 -19
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -23
- 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 +39 -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 +58 -764
- data/lib/active_support/deprecation/behaviors.rb +19 -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 +6 -5
- data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +177 -64
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +24 -10
- data/lib/active_support/duration.rb +134 -55
- data/lib/active_support/encrypted_configuration.rb +11 -1
- data/lib/active_support/encrypted_file.rb +20 -3
- 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 +70 -134
- 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 +30 -4
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +51 -25
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +14 -19
- data/lib/active_support/inflector/inflections.rb +24 -9
- data/lib/active_support/inflector/methods.rb +29 -49
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/isolated_execution_state.rb +56 -0
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +8 -4
- data/lib/active_support/key_generator.rb +19 -2
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber.rb +21 -3
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +34 -21
- data/lib/active_support/message_encryptor.rb +12 -10
- data/lib/active_support/message_verifier.rb +50 -18
- data/lib/active_support/messages/metadata.rb +11 -3
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +6 -5
- data/lib/active_support/multibyte/chars.rb +13 -52
- data/lib/active_support/multibyte/unicode.rb +1 -87
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +110 -69
- data/lib/active_support/notifications/instrumenter.rb +37 -29
- data/lib/active_support/notifications.rb +47 -26
- data/lib/active_support/number_helper/number_converter.rb +2 -4
- data/lib/active_support/number_helper/number_to_currency_converter.rb +10 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +29 -16
- data/lib/active_support/option_merger.rb +9 -16
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +8 -2
- data/lib/active_support/parameter_filter.rb +21 -11
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/rescuable.rb +6 -6
- 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 -2
- data/lib/active_support/subscriber.rb +19 -25
- data/lib/active_support/tagged_logging.rb +31 -6
- data/lib/active_support/test_case.rb +9 -21
- data/lib/active_support/testing/assertions.rb +49 -12
- data/lib/active_support/testing/deprecation.rb +52 -1
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +5 -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 +16 -95
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +3 -5
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +53 -5
- data/lib/active_support/time_with_zone.rb +120 -55
- data/lib/active_support/values/time_zone.rb +49 -18
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +4 -4
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +9 -2
- data/lib/active_support/xml_mini.rb +5 -4
- data/lib/active_support.rb +29 -1
- metadata +46 -45
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/marshal.rb +0 -24
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -9,14 +9,14 @@ require "active_support/values/time_zone"
|
|
9
9
|
class DateTime
|
10
10
|
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
|
11
11
|
#
|
12
|
-
# This method is aliased to <tt>
|
12
|
+
# This method is aliased to <tt>to_fs</tt>.
|
13
13
|
#
|
14
14
|
# === Examples
|
15
15
|
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
|
16
16
|
#
|
17
17
|
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
|
18
|
-
# datetime.
|
19
|
-
# datetime.
|
18
|
+
# datetime.to_fs(:db) # => "2007-12-04 00:00:00"
|
19
|
+
# datetime.to_formatted_s(:number) # => "20071204000000"
|
20
20
|
# datetime.to_formatted_s(:short) # => "04 Dec 00:00"
|
21
21
|
# datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
|
22
22
|
# datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
|
@@ -39,8 +39,8 @@ class DateTime
|
|
39
39
|
to_default_s
|
40
40
|
end
|
41
41
|
end
|
42
|
+
alias_method :to_fs, :to_formatted_s
|
42
43
|
alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
|
43
|
-
alias_method :to_s, :to_formatted_s
|
44
44
|
|
45
45
|
# Returns a formatted string of the offset from UTC, or an alternative
|
46
46
|
# string if the time zone is already UTC.
|
@@ -54,7 +54,7 @@ class DateTime
|
|
54
54
|
|
55
55
|
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
|
56
56
|
def readable_inspect
|
57
|
-
|
57
|
+
to_formatted_s(:rfc822)
|
58
58
|
end
|
59
59
|
alias_method :default_inspect, :inspect
|
60
60
|
alias_method :inspect, :readable_inspect
|
@@ -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_formatted_s(#{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_formatted_s(#{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
|
@@ -4,6 +4,10 @@ module Enumerable
|
|
4
4
|
INDEX_WITH_DEFAULT = Object.new
|
5
5
|
private_constant :INDEX_WITH_DEFAULT
|
6
6
|
|
7
|
+
# Error generated by +sole+ when called on an enumerable that doesn't have
|
8
|
+
# exactly one item.
|
9
|
+
class SoleItemExpectedError < StandardError; end
|
10
|
+
|
7
11
|
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
8
12
|
# when we omit an identity.
|
9
13
|
|
@@ -16,6 +20,22 @@ module Enumerable
|
|
16
20
|
|
17
21
|
# :startdoc:
|
18
22
|
|
23
|
+
# Calculates the minimum from the extracted elements.
|
24
|
+
#
|
25
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
26
|
+
# payments.minimum(:price) # => 5
|
27
|
+
def minimum(key)
|
28
|
+
map(&key).min
|
29
|
+
end
|
30
|
+
|
31
|
+
# Calculates the maximum from the extracted elements.
|
32
|
+
#
|
33
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
34
|
+
# payments.maximum(:price) # => 15
|
35
|
+
def maximum(key)
|
36
|
+
map(&key).max
|
37
|
+
end
|
38
|
+
|
19
39
|
# Calculates a sum from the elements.
|
20
40
|
#
|
21
41
|
# payments.sum { |p| p.price * p.tax_rate }
|
@@ -28,8 +48,8 @@ module Enumerable
|
|
28
48
|
# It can also calculate the sum without the use of a block.
|
29
49
|
#
|
30
50
|
# [5, 15, 10].sum # => 30
|
31
|
-
# ['foo', 'bar'].sum # => "foobar"
|
32
|
-
# [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
|
51
|
+
# ['foo', 'bar'].sum('') # => "foobar"
|
52
|
+
# [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
|
33
53
|
#
|
34
54
|
# The default sum of an empty list is zero. You can override this default:
|
35
55
|
#
|
@@ -38,13 +58,25 @@ module Enumerable
|
|
38
58
|
if identity
|
39
59
|
_original_sum_with_required_identity(identity, &block)
|
40
60
|
elsif block_given?
|
41
|
-
map(&block).sum
|
61
|
+
map(&block).sum
|
62
|
+
# we check `first(1) == []` to check if we have an
|
63
|
+
# empty Enumerable; checking `empty?` would return
|
64
|
+
# true for `[nil]`, which we want to deprecate to
|
65
|
+
# keep consistent with Ruby
|
66
|
+
elsif first.is_a?(Numeric) || first(1) == []
|
67
|
+
identity ||= 0
|
68
|
+
_original_sum_with_required_identity(identity, &block)
|
42
69
|
else
|
70
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
71
|
+
Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
|
72
|
+
Sum of non-numeric elements requires an initial argument.
|
73
|
+
MSG
|
43
74
|
inject(:+) || 0
|
44
75
|
end
|
45
76
|
end
|
46
77
|
|
47
|
-
# Convert an enumerable to a hash
|
78
|
+
# Convert an enumerable to a hash, using the block result as the key and the
|
79
|
+
# element as the value.
|
48
80
|
#
|
49
81
|
# people.index_by(&:login)
|
50
82
|
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
@@ -61,12 +93,19 @@ module Enumerable
|
|
61
93
|
end
|
62
94
|
end
|
63
95
|
|
64
|
-
# Convert an enumerable to a hash
|
96
|
+
# Convert an enumerable to a hash, using the element as the key and the block
|
97
|
+
# result as the value.
|
65
98
|
#
|
66
99
|
# post = Post.new(title: "hey there", body: "what's up?")
|
67
100
|
#
|
68
101
|
# %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
|
69
102
|
# # => { title: "hey there", body: "what's up?" }
|
103
|
+
#
|
104
|
+
# If an argument is passed instead of a block, it will be used as the value
|
105
|
+
# for all elements:
|
106
|
+
#
|
107
|
+
# %i( created_at updated_at ).index_with(Time.now)
|
108
|
+
# # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
|
70
109
|
def index_with(default = INDEX_WITH_DEFAULT)
|
71
110
|
if block_given?
|
72
111
|
result = {}
|
@@ -128,13 +167,9 @@ module Enumerable
|
|
128
167
|
elements.flatten!(1)
|
129
168
|
reject { |element| elements.include?(element) }
|
130
169
|
end
|
170
|
+
alias :without :excluding
|
131
171
|
|
132
|
-
#
|
133
|
-
def without(*elements)
|
134
|
-
excluding(*elements)
|
135
|
-
end
|
136
|
-
|
137
|
-
# Convert an enumerable to an array based on the given key.
|
172
|
+
# Extract the given key from each element in the enumerable.
|
138
173
|
#
|
139
174
|
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
|
140
175
|
# # => ["David", "Rafael", "Aaron"]
|
@@ -145,12 +180,91 @@ module Enumerable
|
|
145
180
|
if keys.many?
|
146
181
|
map { |element| keys.map { |key| element[key] } }
|
147
182
|
else
|
148
|
-
|
183
|
+
key = keys.first
|
184
|
+
map { |element| element[key] }
|
149
185
|
end
|
150
186
|
end
|
187
|
+
|
188
|
+
# Extract the given key from the first element in the enumerable.
|
189
|
+
#
|
190
|
+
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
|
191
|
+
# # => "David"
|
192
|
+
#
|
193
|
+
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
|
194
|
+
# # => [1, "David"]
|
195
|
+
def pick(*keys)
|
196
|
+
return if none?
|
197
|
+
|
198
|
+
if keys.many?
|
199
|
+
keys.map { |key| first[key] }
|
200
|
+
else
|
201
|
+
first[keys.first]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Returns a new +Array+ without the blank items.
|
206
|
+
# Uses Object#blank? for determining if an item is blank.
|
207
|
+
#
|
208
|
+
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
|
209
|
+
# # => [1, 2, true]
|
210
|
+
#
|
211
|
+
# Set.new([nil, "", 1, 2])
|
212
|
+
# # => [2, 1] (or [1, 2])
|
213
|
+
#
|
214
|
+
# When called on a +Hash+, returns a new +Hash+ without the blank values.
|
215
|
+
#
|
216
|
+
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
|
217
|
+
# #=> { b: 1, f: true }
|
218
|
+
def compact_blank
|
219
|
+
reject(&:blank?)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
|
223
|
+
# objects in the original enumerable.
|
224
|
+
#
|
225
|
+
# [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
|
226
|
+
# => [ Person.find(1), Person.find(5), Person.find(3) ]
|
227
|
+
#
|
228
|
+
# If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
|
229
|
+
# If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
|
230
|
+
def in_order_of(key, series)
|
231
|
+
index_by(&key).values_at(*series).compact
|
232
|
+
end
|
233
|
+
|
234
|
+
# Returns the sole item in the enumerable. If there are no items, or more
|
235
|
+
# than one item, raises +Enumerable::SoleItemExpectedError+.
|
236
|
+
#
|
237
|
+
# ["x"].sole # => "x"
|
238
|
+
# Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
|
239
|
+
# { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
|
240
|
+
def sole
|
241
|
+
case count
|
242
|
+
when 1 then return first # rubocop:disable Style/RedundantReturn
|
243
|
+
when 0 then raise SoleItemExpectedError, "no item found"
|
244
|
+
when 2.. then raise SoleItemExpectedError, "multiple items found"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class Hash
|
250
|
+
# Hash#reject has its own definition, so this needs one too.
|
251
|
+
def compact_blank # :nodoc:
|
252
|
+
reject { |_k, v| v.blank? }
|
253
|
+
end
|
254
|
+
|
255
|
+
# Removes all blank values from the +Hash+ in place and returns self.
|
256
|
+
# Uses Object#blank? for determining if a value is blank.
|
257
|
+
#
|
258
|
+
# h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
|
259
|
+
# h.compact_blank!
|
260
|
+
# # => { b: 1, f: true }
|
261
|
+
def compact_blank!
|
262
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
263
|
+
delete_if { |_k, v| v.blank? }
|
264
|
+
end
|
151
265
|
end
|
152
266
|
|
153
|
-
class Range
|
267
|
+
class Range # :nodoc:
|
154
268
|
# Optimize range sum to use arithmetic progression if a block is not given and
|
155
269
|
# we have a range of numeric values.
|
156
270
|
def sum(identity = nil)
|
@@ -175,8 +289,7 @@ using Module.new {
|
|
175
289
|
end
|
176
290
|
}
|
177
291
|
|
178
|
-
class Array
|
179
|
-
# Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
|
292
|
+
class Array # :nodoc:
|
180
293
|
def sum(init = nil, &block)
|
181
294
|
if init.is_a?(Numeric) || first.is_a?(Numeric)
|
182
295
|
init ||= 0
|
@@ -185,4 +298,15 @@ class Array #:nodoc:
|
|
185
298
|
super
|
186
299
|
end
|
187
300
|
end
|
301
|
+
|
302
|
+
# Removes all blank elements from the +Array+ in place and returns self.
|
303
|
+
# Uses Object#blank? for determining if an item is blank.
|
304
|
+
#
|
305
|
+
# a = [1, "", nil, 2, " ", [], {}, false, true]
|
306
|
+
# a.compact_blank!
|
307
|
+
# # => [1, 2, true]
|
308
|
+
def compact_blank!
|
309
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
310
|
+
delete_if(&:blank?)
|
311
|
+
end
|
188
312
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/xml_mini"
|
4
|
-
require "active_support/time"
|
5
4
|
require "active_support/core_ext/object/blank"
|
6
5
|
require "active_support/core_ext/object/to_param"
|
7
6
|
require "active_support/core_ext/object/to_query"
|
7
|
+
require "active_support/core_ext/object/try"
|
8
8
|
require "active_support/core_ext/array/wrap"
|
9
9
|
require "active_support/core_ext/hash/reverse_merge"
|
10
10
|
require "active_support/core_ext/string/inflections"
|
@@ -208,7 +208,7 @@ module ActiveSupport
|
|
208
208
|
elsif become_empty_string?(value)
|
209
209
|
""
|
210
210
|
elsif become_hash?(value)
|
211
|
-
xml_value =
|
211
|
+
xml_value = value.transform_values { |v| deep_to_h(v) }
|
212
212
|
|
213
213
|
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
|
214
214
|
# how multipart uploaded files from HTML appear
|
@@ -112,11 +112,11 @@ class Hash
|
|
112
112
|
end
|
113
113
|
|
114
114
|
private
|
115
|
-
#
|
115
|
+
# Support methods for deep transforming nested hashes and arrays.
|
116
116
|
def _deep_transform_keys_in_object(object, &block)
|
117
117
|
case object
|
118
118
|
when Hash
|
119
|
-
object.each_with_object(
|
119
|
+
object.each_with_object(self.class.new) do |(key, value), result|
|
120
120
|
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
121
121
|
end
|
122
122
|
when Array
|
@@ -18,8 +18,9 @@ class Hash
|
|
18
18
|
|
19
19
|
# Removes and returns the key/value pairs matching the given keys.
|
20
20
|
#
|
21
|
-
# { a: 1, b: 2, c: 3, d: 4 }
|
22
|
-
#
|
21
|
+
# hash = { a: 1, b: 2, c: 3, d: 4 }
|
22
|
+
# hash.extract!(:a, :b) # => {:a=>1, :b=>2}
|
23
|
+
# hash # => {:c=>3, :d=>4}
|
23
24
|
def extract!(*keys)
|
24
25
|
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
|
25
26
|
end
|
@@ -11,14 +11,14 @@ module Kernel
|
|
11
11
|
# end
|
12
12
|
#
|
13
13
|
# noisy_call # warning voiced
|
14
|
-
def silence_warnings
|
15
|
-
with_warnings(nil)
|
14
|
+
def silence_warnings(&block)
|
15
|
+
with_warnings(nil, &block)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Sets $VERBOSE to +true+ for the duration of the block and back to its
|
19
19
|
# original value afterwards.
|
20
|
-
def enable_warnings
|
21
|
-
with_warnings(true)
|
20
|
+
def enable_warnings(&block)
|
21
|
+
with_warnings(true, &block)
|
22
22
|
end
|
23
23
|
|
24
24
|
# Sets $VERBOSE for the duration of the block and back to its original
|
@@ -4,6 +4,6 @@ class LoadError
|
|
4
4
|
# Returns true if the given path name (except perhaps for the ".rb"
|
5
5
|
# extension) is the missing file which caused the exception to be raised.
|
6
6
|
def is_missing?(location)
|
7
|
-
location.
|
7
|
+
location.delete_suffix(".rb") == path.to_s.delete_suffix(".rb")
|
8
8
|
end
|
9
9
|
end
|
@@ -28,9 +28,9 @@ class Module
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def attr_internal_define(attr_name, type)
|
31
|
-
internal_name = attr_internal_ivar_name(attr_name).
|
31
|
+
internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
|
32
32
|
# use native attr_* methods as they are faster on some Ruby implementations
|
33
|
-
|
33
|
+
public_send("attr_#{type}", internal_name)
|
34
34
|
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
|
35
35
|
alias_method attr_name, internal_name
|
36
36
|
remove_method internal_name
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# == Attribute Accessors
|
4
|
+
#
|
3
5
|
# Extends the module object with class/module and instance accessors for
|
4
6
|
# class/module attributes, just like the native attr* accessors for instance
|
5
7
|
# attributes.
|
@@ -48,28 +50,25 @@ class Module
|
|
48
50
|
# end
|
49
51
|
#
|
50
52
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
51
|
-
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
|
53
|
+
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
|
54
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
55
|
+
location ||= caller_locations(1, 1).first
|
56
|
+
|
57
|
+
definition = []
|
52
58
|
syms.each do |sym|
|
53
59
|
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
|
54
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
55
|
-
@@#{sym} = nil unless defined? @@#{sym}
|
56
60
|
|
57
|
-
|
58
|
-
@@#{sym}
|
59
|
-
end
|
60
|
-
EOS
|
61
|
+
definition << "def self.#{sym}; @@#{sym}; end"
|
61
62
|
|
62
63
|
if instance_reader && instance_accessor
|
63
|
-
|
64
|
-
def #{sym}
|
65
|
-
@@#{sym}
|
66
|
-
end
|
67
|
-
EOS
|
64
|
+
definition << "def #{sym}; @@#{sym}; end"
|
68
65
|
end
|
69
66
|
|
70
67
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
71
|
-
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
|
68
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
72
69
|
end
|
70
|
+
|
71
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
73
72
|
end
|
74
73
|
alias :cattr_reader :mattr_reader
|
75
74
|
|
@@ -115,28 +114,24 @@ class Module
|
|
115
114
|
# end
|
116
115
|
#
|
117
116
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
118
|
-
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
|
117
|
+
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
|
118
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
119
|
+
location ||= caller_locations(1, 1).first
|
120
|
+
|
121
|
+
definition = []
|
119
122
|
syms.each do |sym|
|
120
123
|
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
|
121
|
-
|
122
|
-
@@#{sym} = nil unless defined? @@#{sym}
|
123
|
-
|
124
|
-
def self.#{sym}=(obj)
|
125
|
-
@@#{sym} = obj
|
126
|
-
end
|
127
|
-
EOS
|
124
|
+
definition << "def self.#{sym}=(val); @@#{sym} = val; end"
|
128
125
|
|
129
126
|
if instance_writer && instance_accessor
|
130
|
-
|
131
|
-
def #{sym}=(obj)
|
132
|
-
@@#{sym} = obj
|
133
|
-
end
|
134
|
-
EOS
|
127
|
+
definition << "def #{sym}=(val); @@#{sym} = val; end"
|
135
128
|
end
|
136
129
|
|
137
130
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
138
|
-
|
131
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
139
132
|
end
|
133
|
+
|
134
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
140
135
|
end
|
141
136
|
alias :cattr_writer :mattr_writer
|
142
137
|
|
@@ -205,8 +200,9 @@ class Module
|
|
205
200
|
#
|
206
201
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
207
202
|
def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
|
208
|
-
|
209
|
-
|
203
|
+
location = caller_locations(1, 1).first
|
204
|
+
mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
|
205
|
+
mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
|
210
206
|
end
|
211
207
|
alias :cattr_accessor :mattr_accessor
|
212
208
|
end
|