activesupport 7.0.8 → 7.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +142 -428
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +39 -7
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +49 -17
- data/lib/active_support/cache/mem_cache_store.rb +94 -128
- data/lib/active_support/cache/memory_store.rb +80 -25
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +165 -152
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +29 -14
- data/lib/active_support/cache.rb +363 -291
- data/lib/active_support/callbacks.rb +118 -134
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +10 -0
- data/lib/active_support/core_ext/array/conversions.rb +1 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/class/subclasses.rb +17 -34
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +1 -2
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -2
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +3 -75
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/duplicable.rb +24 -15
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +4 -4
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +28 -7
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +1 -5
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -5
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +34 -177
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +36 -30
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +1 -3
- data/lib/active_support/core_ext/time/zones.rb +4 -4
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +53 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +65 -42
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +3 -5
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -172
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +30 -9
- data/lib/active_support/encrypted_file.rb +9 -4
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +160 -36
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +4 -5
- data/lib/active_support/file_update_checker.rb +5 -3
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +41 -25
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +23 -11
- data/lib/active_support/inflector/transliterate.rb +3 -1
- data/lib/active_support/isolated_execution_state.rb +26 -22
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +25 -43
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +6 -4
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +74 -34
- data/lib/active_support/logger.rb +22 -60
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +197 -53
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +305 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +220 -89
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +4 -2
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +29 -28
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +379 -318
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +67 -15
- data/lib/active_support/parameter_filter.rb +84 -69
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +25 -20
- data/lib/active_support/reloader.rb +12 -4
- data/lib/active_support/rescuable.rb +2 -0
- data/lib/active_support/secure_compare_rotator.rb +16 -9
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -25
- data/lib/active_support/test_case.rb +156 -7
- data/lib/active_support/testing/assertions.rb +28 -12
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +21 -9
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +12 -18
- data/lib/active_support/values/time_zone.rb +25 -14
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +12 -3
- data/lib/active_support.rb +15 -3
- metadata +145 -24
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
@@ -1,41 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/
|
3
|
+
require "active_support/descendants_tracker"
|
4
4
|
|
5
5
|
class Class
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# C.descendants # => []
|
11
|
-
#
|
12
|
-
# class B < C; end
|
13
|
-
# C.descendants # => [B]
|
14
|
-
#
|
15
|
-
# class A < B; end
|
16
|
-
# C.descendants # => [B, A]
|
17
|
-
#
|
18
|
-
# class D < C; end
|
19
|
-
# C.descendants # => [B, A, D]
|
20
|
-
def descendants
|
21
|
-
subclasses.concat(subclasses.flat_map(&:descendants))
|
22
|
-
end
|
23
|
-
else
|
24
|
-
def descendants
|
25
|
-
ObjectSpace.each_object(singleton_class).reject do |k|
|
26
|
-
k.singleton_class? || k == self
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Returns an array with the direct children of +self+.
|
6
|
+
# Returns an array with all classes that are < than its receiver.
|
7
|
+
#
|
8
|
+
# class C; end
|
9
|
+
# C.descendants # => []
|
32
10
|
#
|
33
|
-
# class
|
34
|
-
#
|
35
|
-
# class Baz < Bar; end
|
11
|
+
# class B < C; end
|
12
|
+
# C.descendants # => [B]
|
36
13
|
#
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
|
14
|
+
# class A < B; end
|
15
|
+
# C.descendants # => [B, A]
|
16
|
+
#
|
17
|
+
# class D < C; end
|
18
|
+
# C.descendants # => [B, A, D]
|
19
|
+
def descendants
|
20
|
+
subclasses.concat(subclasses.flat_map(&:descendants))
|
21
|
+
end
|
22
|
+
|
23
|
+
prepend ActiveSupport::DescendantsTracker::ReloadedClassesFiltering
|
41
24
|
end
|
@@ -52,11 +52,10 @@ class Date
|
|
52
52
|
strftime(formatter)
|
53
53
|
end
|
54
54
|
else
|
55
|
-
|
55
|
+
to_s
|
56
56
|
end
|
57
57
|
end
|
58
58
|
alias_method :to_formatted_s, :to_fs
|
59
|
-
alias_method :to_default_s, :to_s
|
60
59
|
|
61
60
|
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
|
62
61
|
def readable_inspect
|
@@ -4,5 +4,4 @@ require "active_support/core_ext/date/acts_like"
|
|
4
4
|
require "active_support/core_ext/date/blank"
|
5
5
|
require "active_support/core_ext/date/calculations"
|
6
6
|
require "active_support/core_ext/date/conversions"
|
7
|
-
require "active_support/core_ext/date/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
|
8
7
|
require "active_support/core_ext/date/zones"
|
@@ -157,6 +157,16 @@ module DateAndTime
|
|
157
157
|
end
|
158
158
|
alias :at_end_of_quarter :end_of_quarter
|
159
159
|
|
160
|
+
# Returns the quarter for a date/time.
|
161
|
+
#
|
162
|
+
# Date.new(2010, 1, 31).quarter # => 1
|
163
|
+
# Date.new(2010, 4, 12).quarter # => 2
|
164
|
+
# Date.new(2010, 9, 15).quarter # => 3
|
165
|
+
# Date.new(2010, 12, 25).quarter # => 4
|
166
|
+
def quarter
|
167
|
+
(month / 3.0).ceil
|
168
|
+
end
|
169
|
+
|
160
170
|
# Returns a new date/time at the beginning of the year.
|
161
171
|
#
|
162
172
|
# today = Date.today # => Fri, 10 Jul 2015
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/module/attribute_accessors"
|
4
|
+
require "active_support/core_ext/module/redefine_method"
|
4
5
|
|
5
6
|
module DateAndTime
|
6
7
|
module Compatibility
|
@@ -11,7 +12,33 @@ module DateAndTime
|
|
11
12
|
# of the receiver. For backwards compatibility we're overriding
|
12
13
|
# this behavior, but new apps will have an initializer that sets
|
13
14
|
# this to true, because the new behavior is preferred.
|
14
|
-
mattr_accessor :preserve_timezone,
|
15
|
+
mattr_accessor :preserve_timezone, instance_accessor: false, default: nil
|
16
|
+
|
17
|
+
singleton_class.silence_redefinition_of_method :preserve_timezone
|
18
|
+
|
19
|
+
#--
|
20
|
+
# This re-implements the behaviour of the mattr_reader, instead
|
21
|
+
# of prepending on to it, to avoid overcomplicating a module that
|
22
|
+
# is in turn included in several places. This will all go away in
|
23
|
+
# Rails 8.0 anyway.
|
24
|
+
def self.preserve_timezone # :nodoc:
|
25
|
+
if @@preserve_timezone.nil?
|
26
|
+
# Only warn once, the first time the value is used (which should
|
27
|
+
# be the first time #to_time is called).
|
28
|
+
ActiveSupport.deprecator.warn(
|
29
|
+
"to_time will always preserve the timezone offset of the receiver in Rails 8.0. " \
|
30
|
+
"To opt in to the new behavior, set `ActiveSupport.to_time_preserves_timezone = true`."
|
31
|
+
)
|
32
|
+
|
33
|
+
@@preserve_timezone = false
|
34
|
+
end
|
35
|
+
|
36
|
+
@@preserve_timezone
|
37
|
+
end
|
38
|
+
|
39
|
+
def preserve_timezone # :nodoc:
|
40
|
+
Compatibility.preserve_timezone
|
41
|
+
end
|
15
42
|
|
16
43
|
# Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
|
17
44
|
#
|
@@ -36,11 +36,11 @@ class DateTime
|
|
36
36
|
if formatter = ::Time::DATE_FORMATS[format]
|
37
37
|
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
|
38
38
|
else
|
39
|
-
|
39
|
+
to_s
|
40
40
|
end
|
41
41
|
end
|
42
42
|
alias_method :to_formatted_s, :to_fs
|
43
|
-
|
43
|
+
|
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.
|
@@ -5,4 +5,3 @@ 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"]
|
@@ -10,8 +10,6 @@ module Digest
|
|
10
10
|
OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
11
11
|
X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
12
12
|
|
13
|
-
mattr_accessor :use_rfc4122_namespaced_uuids, instance_accessor: false, default: false
|
14
|
-
|
15
13
|
# Generates a v5 non-random UUID (Universally Unique IDentifier).
|
16
14
|
#
|
17
15
|
# Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs.
|
@@ -55,22 +53,21 @@ module Digest
|
|
55
53
|
SecureRandom.uuid
|
56
54
|
end
|
57
55
|
|
56
|
+
# Returns the nil UUID. This is a special form of UUID that is specified to
|
57
|
+
# have all 128 bits set to zero.
|
58
|
+
def self.nil_uuid
|
59
|
+
"00000000-0000-0000-0000-000000000000"
|
60
|
+
end
|
61
|
+
|
58
62
|
def self.pack_uuid_namespace(namespace)
|
59
63
|
if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
|
60
64
|
namespace
|
61
|
-
|
65
|
+
else
|
62
66
|
match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
|
63
67
|
|
64
68
|
raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?
|
65
69
|
|
66
70
|
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
71
|
end
|
75
72
|
end
|
76
73
|
|
@@ -25,18 +25,6 @@ module Enumerable
|
|
25
25
|
ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError)
|
26
26
|
singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants)
|
27
27
|
|
28
|
-
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
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
28
|
# Calculates the minimum from the extracted elements.
|
41
29
|
#
|
42
30
|
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
@@ -53,50 +41,6 @@ module Enumerable
|
|
53
41
|
map(&key).max
|
54
42
|
end
|
55
43
|
|
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
|
-
else
|
80
|
-
first = true
|
81
|
-
|
82
|
-
reduce(nil) do |sum, value|
|
83
|
-
if first
|
84
|
-
first = false
|
85
|
-
|
86
|
-
unless value.is_a?(Numeric) || value.respond_to?(:coerce)
|
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
|
-
end
|
92
|
-
value
|
93
|
-
else
|
94
|
-
sum + value
|
95
|
-
end
|
96
|
-
end || 0
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
44
|
# Convert an enumerable to a hash, using the block result as the key and the
|
101
45
|
# element as the value.
|
102
46
|
#
|
@@ -289,38 +233,22 @@ end
|
|
289
233
|
class Range # :nodoc:
|
290
234
|
# Optimize range sum to use arithmetic progression if a block is not given and
|
291
235
|
# we have a range of numeric values.
|
292
|
-
def sum(
|
236
|
+
def sum(initial_value = 0)
|
293
237
|
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
|
294
238
|
super
|
295
239
|
else
|
296
240
|
actual_last = exclude_end? ? (last - 1) : last
|
297
241
|
if actual_last >= first
|
298
|
-
sum =
|
242
|
+
sum = initial_value || 0
|
299
243
|
sum + (actual_last - first + 1) * (actual_last + first) / 2
|
300
244
|
else
|
301
|
-
|
245
|
+
initial_value || 0
|
302
246
|
end
|
303
247
|
end
|
304
248
|
end
|
305
249
|
end
|
306
250
|
|
307
|
-
# Using Refinements here in order not to expose our internal method
|
308
|
-
using Module.new {
|
309
|
-
refine Array do
|
310
|
-
alias :orig_sum :sum
|
311
|
-
end
|
312
|
-
}
|
313
|
-
|
314
251
|
class Array # :nodoc:
|
315
|
-
def sum(init = nil, &block)
|
316
|
-
if init.is_a?(Numeric) || first.is_a?(Numeric)
|
317
|
-
init ||= 0
|
318
|
-
orig_sum(init, &block)
|
319
|
-
else
|
320
|
-
super
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
252
|
# Removes all blank elements from the +Array+ in place and returns self.
|
325
253
|
# Uses Object#blank? for determining if an item is blank.
|
326
254
|
#
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
module CoreExt
|
7
|
+
module ERBUtil
|
8
|
+
# HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
|
9
|
+
# This method is not for public consumption! Seriously!
|
10
|
+
def html_escape(s) # :nodoc:
|
11
|
+
s = s.to_s
|
12
|
+
if s.html_safe?
|
13
|
+
s
|
14
|
+
else
|
15
|
+
super(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
alias :unwrapped_html_escape :html_escape # :nodoc:
|
19
|
+
|
20
|
+
# A utility method for escaping HTML tag characters.
|
21
|
+
# This method is also aliased as <tt>h</tt>.
|
22
|
+
#
|
23
|
+
# puts html_escape('is a > 0 & a < 10?')
|
24
|
+
# # => is a > 0 & a < 10?
|
25
|
+
def html_escape(s) # rubocop:disable Lint/DuplicateMethods
|
26
|
+
unwrapped_html_escape(s).html_safe
|
27
|
+
end
|
28
|
+
alias h html_escape
|
29
|
+
end
|
30
|
+
|
31
|
+
module ERBUtilPrivate
|
32
|
+
include ERBUtil
|
33
|
+
private :unwrapped_html_escape, :html_escape, :h
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ERB
|
39
|
+
module Util
|
40
|
+
HTML_ESCAPE = { "&" => "&", ">" => ">", "<" => "<", '"' => """, "'" => "'" }
|
41
|
+
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
|
42
|
+
|
43
|
+
# Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
|
44
|
+
TAG_NAME_START_CODEPOINTS = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
|
45
|
+
"\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
|
46
|
+
"\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
|
47
|
+
INVALID_TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_CODEPOINTS}]/
|
48
|
+
TAG_NAME_FOLLOWING_CODEPOINTS = "#{TAG_NAME_START_CODEPOINTS}\\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}"
|
49
|
+
INVALID_TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_FOLLOWING_CODEPOINTS}]/
|
50
|
+
SAFE_XML_TAG_NAME_REGEXP = /\A[#{TAG_NAME_START_CODEPOINTS}][#{TAG_NAME_FOLLOWING_CODEPOINTS}]*\z/
|
51
|
+
TAG_NAME_REPLACEMENT_CHAR = "_"
|
52
|
+
|
53
|
+
prepend ActiveSupport::CoreExt::ERBUtilPrivate
|
54
|
+
singleton_class.prepend ActiveSupport::CoreExt::ERBUtil
|
55
|
+
|
56
|
+
# A utility method for escaping HTML without affecting existing escaped entities.
|
57
|
+
#
|
58
|
+
# html_escape_once('1 < 2 & 3')
|
59
|
+
# # => "1 < 2 & 3"
|
60
|
+
#
|
61
|
+
# html_escape_once('<< Accept & Checkout')
|
62
|
+
# # => "<< Accept & Checkout"
|
63
|
+
def html_escape_once(s)
|
64
|
+
ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE).html_safe
|
65
|
+
end
|
66
|
+
|
67
|
+
module_function :html_escape_once
|
68
|
+
|
69
|
+
# A utility method for escaping HTML entities in JSON strings. Specifically, the
|
70
|
+
# &, > and < characters are replaced with their equivalent unicode escaped form -
|
71
|
+
# \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
|
72
|
+
# escaped as they are treated as newline characters in some JavaScript engines.
|
73
|
+
# These sequences have identical meaning as the original characters inside the
|
74
|
+
# context of a JSON string, so assuming the input is a valid and well-formed
|
75
|
+
# JSON value, the output will have equivalent meaning when parsed:
|
76
|
+
#
|
77
|
+
# json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
|
78
|
+
# # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
|
79
|
+
#
|
80
|
+
# json_escape(json)
|
81
|
+
# # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
|
82
|
+
#
|
83
|
+
# JSON.parse(json) == JSON.parse(json_escape(json))
|
84
|
+
# # => true
|
85
|
+
#
|
86
|
+
# The intended use case for this method is to escape JSON strings before including
|
87
|
+
# them inside a script tag to avoid XSS vulnerability:
|
88
|
+
#
|
89
|
+
# <script>
|
90
|
+
# var currentUser = <%= raw json_escape(current_user.to_json) %>;
|
91
|
+
# </script>
|
92
|
+
#
|
93
|
+
# It is necessary to +raw+ the result of +json_escape+, so that quotation marks
|
94
|
+
# don't get converted to <tt>"</tt> entities. +json_escape+ doesn't
|
95
|
+
# automatically flag the result as HTML safe, since the raw value is unsafe to
|
96
|
+
# use inside HTML attributes.
|
97
|
+
#
|
98
|
+
# If your JSON is being used downstream for insertion into the DOM, be aware of
|
99
|
+
# whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
|
100
|
+
# If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
|
101
|
+
# content returned by your JSON.
|
102
|
+
#
|
103
|
+
# If you need to output JSON elsewhere in your HTML, you can just do something
|
104
|
+
# like this, as any unsafe characters (including quotation marks) will be
|
105
|
+
# automatically escaped for you:
|
106
|
+
#
|
107
|
+
# <div data-user-info="<%= current_user.to_json %>">...</div>
|
108
|
+
#
|
109
|
+
# WARNING: this helper only works with valid JSON. Using this on non-JSON values
|
110
|
+
# will open up serious XSS vulnerabilities. For example, if you replace the
|
111
|
+
# +current_user.to_json+ in the example above with user input instead, the browser
|
112
|
+
# will happily <tt>eval()</tt> that string as JavaScript.
|
113
|
+
#
|
114
|
+
# The escaping performed in this method is identical to those performed in the
|
115
|
+
# Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
|
116
|
+
# set to true. Because this transformation is idempotent, this helper can be
|
117
|
+
# applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
|
118
|
+
#
|
119
|
+
# Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
|
120
|
+
# is enabled, or if you are unsure where your JSON string originated from, it
|
121
|
+
# is recommended that you always apply this helper (other libraries, such as the
|
122
|
+
# JSON gem, do not provide this kind of protection by default; also some gems
|
123
|
+
# might override +to_json+ to bypass Active Support's encoder).
|
124
|
+
def json_escape(s)
|
125
|
+
result = s.to_s.dup
|
126
|
+
result.gsub!(">", '\u003e')
|
127
|
+
result.gsub!("<", '\u003c')
|
128
|
+
result.gsub!("&", '\u0026')
|
129
|
+
result.gsub!("\u2028", '\u2028')
|
130
|
+
result.gsub!("\u2029", '\u2029')
|
131
|
+
s.html_safe? ? result.html_safe : result
|
132
|
+
end
|
133
|
+
|
134
|
+
module_function :json_escape
|
135
|
+
|
136
|
+
# A utility method for escaping XML names of tags and names of attributes.
|
137
|
+
#
|
138
|
+
# xml_name_escape('1 < 2 & 3')
|
139
|
+
# # => "1___2___3"
|
140
|
+
#
|
141
|
+
# It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
|
142
|
+
def xml_name_escape(name)
|
143
|
+
name = name.to_s
|
144
|
+
return "" if name.blank?
|
145
|
+
return name if name.match?(SAFE_XML_TAG_NAME_REGEXP)
|
146
|
+
|
147
|
+
starting_char = name[0]
|
148
|
+
starting_char.gsub!(INVALID_TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
|
149
|
+
|
150
|
+
return starting_char if name.size == 1
|
151
|
+
|
152
|
+
following_chars = name[1..-1]
|
153
|
+
following_chars.gsub!(INVALID_TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
|
154
|
+
|
155
|
+
starting_char << following_chars
|
156
|
+
end
|
157
|
+
module_function :xml_name_escape
|
158
|
+
|
159
|
+
# Tokenizes a line of ERB. This is really just for error reporting and
|
160
|
+
# nobody should use it.
|
161
|
+
def self.tokenize(source) # :nodoc:
|
162
|
+
require "strscan"
|
163
|
+
source = StringScanner.new(source.chomp)
|
164
|
+
tokens = []
|
165
|
+
|
166
|
+
start_re = /<%(?:={1,2}|-|\#|%)?/m
|
167
|
+
finish_re = /(?:[-=])?%>/m
|
168
|
+
|
169
|
+
while !source.eos?
|
170
|
+
pos = source.pos
|
171
|
+
source.scan_until(/(?:#{start_re}|#{finish_re})/)
|
172
|
+
raise NotImplementedError if source.matched.nil?
|
173
|
+
len = source.pos - source.matched.bytesize - pos
|
174
|
+
|
175
|
+
case source.matched
|
176
|
+
when start_re
|
177
|
+
tokens << [:TEXT, source.string[pos, len]] if len > 0
|
178
|
+
tokens << [:OPEN, source.matched]
|
179
|
+
if source.scan(/(.*?)(?=#{finish_re}|\z)/m)
|
180
|
+
tokens << [:CODE, source.matched] unless source.matched.empty?
|
181
|
+
tokens << [:CLOSE, source.scan(finish_re)] unless source.eos?
|
182
|
+
else
|
183
|
+
raise NotImplementedError
|
184
|
+
end
|
185
|
+
when finish_re
|
186
|
+
tokens << [:CODE, source.string[pos, len]] if len > 0
|
187
|
+
tokens << [:CLOSE, source.matched]
|
188
|
+
else
|
189
|
+
raise NotImplementedError, source.matched
|
190
|
+
end
|
191
|
+
|
192
|
+
unless source.eos? || source.exist?(start_re) || source.exist?(finish_re)
|
193
|
+
tokens << [:TEXT, source.rest]
|
194
|
+
source.terminate
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
tokens
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -68,7 +68,7 @@ class Hash
|
|
68
68
|
#
|
69
69
|
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
|
70
70
|
#
|
71
|
-
# The default XML builder is a fresh instance of
|
71
|
+
# The default XML builder is a fresh instance of +Builder::XmlMarkup+. You can
|
72
72
|
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
|
73
73
|
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
|
74
74
|
def to_xml(options = {})
|
@@ -1,6 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/deep_mergeable"
|
4
|
+
|
3
5
|
class Hash
|
6
|
+
include ActiveSupport::DeepMergeable
|
7
|
+
|
8
|
+
##
|
9
|
+
# :method: deep_merge
|
10
|
+
# :call-seq: deep_merge(other_hash, &block)
|
11
|
+
#
|
4
12
|
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
5
13
|
#
|
6
14
|
# h1 = { a: true, b: { c: [1, 2, 3] } }
|
@@ -15,20 +23,20 @@ class Hash
|
|
15
23
|
# h2 = { b: 250, c: { c1: 200 } }
|
16
24
|
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
|
17
25
|
# # => { a: 100, b: 450, c: { c1: 300 } }
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
#
|
27
|
+
#--
|
28
|
+
# Implemented by ActiveSupport::DeepMergeable#deep_merge.
|
29
|
+
|
30
|
+
##
|
31
|
+
# :method: deep_merge!
|
32
|
+
# :call-seq: deep_merge!(other_hash, &block)
|
33
|
+
#
|
34
|
+
# Same as #deep_merge, but modifies +self+.
|
35
|
+
#
|
36
|
+
#--
|
37
|
+
# Implemented by ActiveSupport::DeepMergeable#deep_merge!.
|
21
38
|
|
22
|
-
|
23
|
-
|
24
|
-
merge!(other_hash) do |key, this_val, other_val|
|
25
|
-
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
26
|
-
this_val.deep_merge(other_val, &block)
|
27
|
-
elsif block_given?
|
28
|
-
block.call(key, this_val, other_val)
|
29
|
-
else
|
30
|
-
other_val
|
31
|
-
end
|
32
|
-
end
|
39
|
+
def deep_merge?(other) # :nodoc:
|
40
|
+
other.is_a?(Hash)
|
33
41
|
end
|
34
42
|
end
|
@@ -8,13 +8,13 @@ class Hash
|
|
8
8
|
# hash.stringify_keys
|
9
9
|
# # => {"name"=>"Rob", "age"=>"28"}
|
10
10
|
def stringify_keys
|
11
|
-
transform_keys
|
11
|
+
transform_keys { |k| Symbol === k ? k.name : k.to_s }
|
12
12
|
end
|
13
13
|
|
14
14
|
# Destructively converts all keys to strings. Same as
|
15
15
|
# +stringify_keys+, but modifies +self+.
|
16
16
|
def stringify_keys!
|
17
|
-
transform_keys!
|
17
|
+
transform_keys! { |k| Symbol === k ? k.name : k.to_s }
|
18
18
|
end
|
19
19
|
|
20
20
|
# Returns a new hash with all keys converted to symbols, as long as
|
@@ -82,14 +82,14 @@ class Hash
|
|
82
82
|
# hash.deep_stringify_keys
|
83
83
|
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
|
84
84
|
def deep_stringify_keys
|
85
|
-
deep_transform_keys
|
85
|
+
deep_transform_keys { |k| Symbol === k ? k.name : k.to_s }
|
86
86
|
end
|
87
87
|
|
88
88
|
# Destructively converts all keys to strings.
|
89
89
|
# This includes the keys from the root hash and from all
|
90
90
|
# nested hashes and arrays.
|
91
91
|
def deep_stringify_keys!
|
92
|
-
deep_transform_keys!
|
92
|
+
deep_transform_keys! { |k| Symbol === k ? k.name : k.to_s }
|
93
93
|
end
|
94
94
|
|
95
95
|
# Returns a new hash with all keys converted to symbols, as long as
|
@@ -19,16 +19,27 @@ class Module
|
|
19
19
|
end
|
20
20
|
alias_method :attr_internal, :attr_internal_accessor
|
21
21
|
|
22
|
-
class << self
|
23
|
-
|
22
|
+
class << self
|
23
|
+
attr_reader :attr_internal_naming_format
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
def attr_internal_naming_format=(format)
|
26
|
+
if format.start_with?("@")
|
27
|
+
ActiveSupport.deprecator.warn <<~MESSAGE
|
28
|
+
Setting `attr_internal_naming_format` with a `@` prefix is deprecated and will be removed in Rails 8.0.
|
29
|
+
|
30
|
+
You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
|
31
|
+
MESSAGE
|
32
|
+
|
33
|
+
format = format.delete_prefix("@")
|
34
|
+
end
|
35
|
+
@attr_internal_naming_format = format
|
28
36
|
end
|
37
|
+
end
|
38
|
+
self.attr_internal_naming_format = "_%s"
|
29
39
|
|
40
|
+
private
|
30
41
|
def attr_internal_define(attr_name, type)
|
31
|
-
internal_name =
|
42
|
+
internal_name = Module.attr_internal_naming_format % attr_name
|
32
43
|
# use native attr_* methods as they are faster on some Ruby implementations
|
33
44
|
public_send("attr_#{type}", internal_name)
|
34
45
|
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
|