activesupport 7.2.3 → 8.0.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +191 -213
- data/lib/active_support/backtrace_cleaner.rb +1 -1
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/cache/file_store.rb +12 -2
- data/lib/active_support/cache/mem_cache_store.rb +4 -1
- data/lib/active_support/cache/memory_store.rb +6 -2
- data/lib/active_support/cache/redis_cache_store.rb +6 -3
- data/lib/active_support/cache.rb +16 -11
- data/lib/active_support/callbacks.rb +5 -3
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/core_ext/array/conversions.rb +3 -3
- data/lib/active_support/core_ext/benchmark.rb +6 -9
- data/lib/active_support/core_ext/class/attribute.rb +24 -18
- data/lib/active_support/core_ext/date/conversions.rb +2 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +2 -2
- data/lib/active_support/core_ext/enumerable.rb +8 -3
- data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
- data/lib/active_support/core_ext/object/json.rb +16 -10
- data/lib/active_support/core_ext/object/to_query.rb +2 -1
- data/lib/active_support/core_ext/range/overlap.rb +3 -3
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +3 -1
- data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
- data/lib/active_support/core_ext/time/calculations.rb +14 -2
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/delegation.rb +25 -44
- data/lib/active_support/dependencies.rb +0 -1
- data/lib/active_support/deprecation/reporting.rb +0 -19
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/duration.rb +14 -10
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/error_reporter.rb +31 -1
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +15 -16
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/inflector/inflections.rb +2 -1
- data/lib/active_support/inflector/methods.rb +3 -3
- data/lib/active_support/isolated_execution_state.rb +4 -4
- data/lib/active_support/json/decoding.rb +3 -1
- data/lib/active_support/json/encoding.rb +2 -2
- data/lib/active_support/logger_thread_safe_level.rb +6 -3
- data/lib/active_support/message_pack/extensions.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +0 -1
- data/lib/active_support/notifications/instrumenter.rb +1 -1
- data/lib/active_support/number_helper/number_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +17 -2
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/railtie.rb +6 -0
- data/lib/active_support/tagged_logging.rb +5 -0
- data/lib/active_support/test_case.rb +6 -0
- data/lib/active_support/testing/assertions.rb +84 -21
- data/lib/active_support/testing/autorun.rb +5 -0
- data/lib/active_support/testing/isolation.rb +0 -2
- data/lib/active_support/testing/parallelization/worker.rb +5 -1
- data/lib/active_support/testing/time_helpers.rb +2 -1
- data/lib/active_support/time_with_zone.rb +22 -13
- data/lib/active_support/values/time_zone.rb +11 -9
- data/lib/active_support.rb +10 -3
- metadata +22 -7
- data/lib/active_support/proxy_object.rb +0 -20
|
@@ -150,7 +150,7 @@ module ActiveSupport
|
|
|
150
150
|
universal_options = redis_options.extract!(*UNIVERSAL_OPTIONS)
|
|
151
151
|
|
|
152
152
|
if pool_options = self.class.send(:retrieve_pool_options, redis_options)
|
|
153
|
-
@redis = ::ConnectionPool.new(pool_options) { self.class.build_redis(**redis_options) }
|
|
153
|
+
@redis = ::ConnectionPool.new(**pool_options) { self.class.build_redis(**redis_options) }
|
|
154
154
|
else
|
|
155
155
|
@redis = self.class.build_redis(**redis_options)
|
|
156
156
|
end
|
|
@@ -173,9 +173,12 @@ module ActiveSupport
|
|
|
173
173
|
return {} if names.empty?
|
|
174
174
|
|
|
175
175
|
options = names.extract_options!
|
|
176
|
-
|
|
176
|
+
options = merged_options(options)
|
|
177
|
+
keys = names.map { |name| normalize_key(name, options) }
|
|
178
|
+
|
|
179
|
+
instrument_multi(:read_multi, keys, options) do |payload|
|
|
177
180
|
read_multi_entries(names, **options).tap do |results|
|
|
178
|
-
payload[:hits] = results.keys
|
|
181
|
+
payload[:hits] = results.keys.map { |name| normalize_key(name, options) }
|
|
179
182
|
end
|
|
180
183
|
end
|
|
181
184
|
end
|
data/lib/active_support/cache.rb
CHANGED
|
@@ -287,7 +287,7 @@ module ActiveSupport
|
|
|
287
287
|
# <tt>coder: nil</tt> to avoid the overhead of safeguarding against
|
|
288
288
|
# mutation.
|
|
289
289
|
#
|
|
290
|
-
# The +:coder+ option is
|
|
290
|
+
# The +:coder+ option is mutually exclusive with the +:serializer+ and
|
|
291
291
|
# +:compressor+ options. Specifying them together will raise an
|
|
292
292
|
# +ArgumentError+.
|
|
293
293
|
#
|
|
@@ -420,7 +420,7 @@ module ActiveSupport
|
|
|
420
420
|
# t1.join
|
|
421
421
|
#
|
|
422
422
|
# p val_1 # => "new value 1"
|
|
423
|
-
# p val_2 # => "
|
|
423
|
+
# p val_2 # => "original value"
|
|
424
424
|
# p cache.fetch("foo") # => "new value 1"
|
|
425
425
|
#
|
|
426
426
|
# # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
|
|
@@ -539,10 +539,11 @@ module ActiveSupport
|
|
|
539
539
|
|
|
540
540
|
options = names.extract_options!
|
|
541
541
|
options = merged_options(options)
|
|
542
|
+
keys = names.map { |name| normalize_key(name, options) }
|
|
542
543
|
|
|
543
|
-
instrument_multi :read_multi,
|
|
544
|
+
instrument_multi :read_multi, keys, options do |payload|
|
|
544
545
|
read_multi_entries(names, **options, event: payload).tap do |results|
|
|
545
|
-
payload[:hits] = results.keys
|
|
546
|
+
payload[:hits] = results.keys.map { |name| normalize_key(name, options) }
|
|
546
547
|
end
|
|
547
548
|
end
|
|
548
549
|
end
|
|
@@ -552,8 +553,9 @@ module ActiveSupport
|
|
|
552
553
|
return hash if hash.empty?
|
|
553
554
|
|
|
554
555
|
options = merged_options(options)
|
|
556
|
+
normalized_hash = hash.transform_keys { |key| normalize_key(key, options) }
|
|
555
557
|
|
|
556
|
-
instrument_multi :write_multi,
|
|
558
|
+
instrument_multi :write_multi, normalized_hash, options do |payload|
|
|
557
559
|
entries = hash.each_with_object({}) do |(name, value), memo|
|
|
558
560
|
memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
|
559
561
|
end
|
|
@@ -597,9 +599,9 @@ module ActiveSupport
|
|
|
597
599
|
|
|
598
600
|
options = names.extract_options!
|
|
599
601
|
options = merged_options(options)
|
|
600
|
-
|
|
602
|
+
keys = names.map { |name| normalize_key(name, options) }
|
|
601
603
|
writes = {}
|
|
602
|
-
ordered = instrument_multi :read_multi,
|
|
604
|
+
ordered = instrument_multi :read_multi, keys, options do |payload|
|
|
603
605
|
if options[:force]
|
|
604
606
|
reads = {}
|
|
605
607
|
else
|
|
@@ -611,7 +613,7 @@ module ActiveSupport
|
|
|
611
613
|
end
|
|
612
614
|
writes.compact! if options[:skip_nil]
|
|
613
615
|
|
|
614
|
-
payload[:hits] = reads.keys
|
|
616
|
+
payload[:hits] = reads.keys.map { |name| normalize_key(name, options) }
|
|
615
617
|
payload[:super_operation] = :fetch_multi
|
|
616
618
|
|
|
617
619
|
ordered
|
|
@@ -944,9 +946,12 @@ module ActiveSupport
|
|
|
944
946
|
#
|
|
945
947
|
# namespace_key 'foo', namespace: -> { 'cache' }
|
|
946
948
|
# # => 'cache:foo'
|
|
947
|
-
def namespace_key(key,
|
|
948
|
-
|
|
949
|
-
|
|
949
|
+
def namespace_key(key, call_options = nil)
|
|
950
|
+
namespace = if call_options&.key?(:namespace)
|
|
951
|
+
call_options[:namespace]
|
|
952
|
+
else
|
|
953
|
+
options[:namespace]
|
|
954
|
+
end
|
|
950
955
|
|
|
951
956
|
if namespace.respond_to?(:call)
|
|
952
957
|
namespace = namespace.call
|
|
@@ -6,7 +6,6 @@ require "active_support/core_ext/array/extract_options"
|
|
|
6
6
|
require "active_support/core_ext/class/attribute"
|
|
7
7
|
require "active_support/core_ext/string/filters"
|
|
8
8
|
require "active_support/core_ext/object/blank"
|
|
9
|
-
require "thread"
|
|
10
9
|
|
|
11
10
|
module ActiveSupport
|
|
12
11
|
# = Active Support \Callbacks
|
|
@@ -67,7 +66,7 @@ module ActiveSupport
|
|
|
67
66
|
|
|
68
67
|
included do
|
|
69
68
|
extend ActiveSupport::DescendantsTracker
|
|
70
|
-
class_attribute :__callbacks, instance_writer: false, default: {}
|
|
69
|
+
class_attribute :__callbacks, instance_writer: false, instance_predicate: false, default: {}
|
|
71
70
|
end
|
|
72
71
|
|
|
73
72
|
CALLBACK_FILTER_TYPES = [:before, :after, :around].freeze
|
|
@@ -935,7 +934,10 @@ module ActiveSupport
|
|
|
935
934
|
end
|
|
936
935
|
|
|
937
936
|
def set_callbacks(name, callbacks) # :nodoc:
|
|
938
|
-
|
|
937
|
+
# HACK: We're making assumption on how `class_attribute` is implemented
|
|
938
|
+
# to save constantly duping the callback hash. If this desync with class_attribute
|
|
939
|
+
# we'll lose the optimization, but won't cause an actual behavior bug.
|
|
940
|
+
unless singleton_class.private_method_defined?(:__class_attr__callbacks, false)
|
|
939
941
|
self.__callbacks = __callbacks.dup
|
|
940
942
|
end
|
|
941
943
|
self.__callbacks[name.to_sym] = callbacks
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module ClassAttribute # :nodoc:
|
|
5
|
+
class << self
|
|
6
|
+
def redefine(owner, name, namespaced_name, value)
|
|
7
|
+
if owner.singleton_class?
|
|
8
|
+
if owner.attached_object.is_a?(Module)
|
|
9
|
+
redefine_method(owner, namespaced_name, private: true) { value }
|
|
10
|
+
else
|
|
11
|
+
redefine_method(owner, name) { value }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
redefine_method(owner.singleton_class, namespaced_name, private: true) { value }
|
|
16
|
+
|
|
17
|
+
redefine_method(owner.singleton_class, "#{namespaced_name}=", private: true) do |new_value|
|
|
18
|
+
if owner.equal?(self)
|
|
19
|
+
value = new_value
|
|
20
|
+
else
|
|
21
|
+
::ActiveSupport::ClassAttribute.redefine(self, name, namespaced_name, new_value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def redefine_method(owner, name, private: false, &block)
|
|
27
|
+
owner.silence_redefinition_of_method(name)
|
|
28
|
+
owner.define_method(name, &block)
|
|
29
|
+
owner.send(:private, name) if private
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -55,6 +55,11 @@ module ActiveSupport
|
|
|
55
55
|
@path = path
|
|
56
56
|
@line = line
|
|
57
57
|
@namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
|
|
58
|
+
@sources = []
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def class_eval
|
|
62
|
+
yield @sources
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
def define_cached_method(canonical_name, namespace:, as: nil, &block)
|
|
@@ -65,6 +70,10 @@ module ActiveSupport
|
|
|
65
70
|
@namespaces.each_value do |method_set|
|
|
66
71
|
method_set.apply(@owner, @path, @line - 1)
|
|
67
72
|
end
|
|
73
|
+
|
|
74
|
+
unless @sources.empty?
|
|
75
|
+
@owner.class_eval("# frozen_string_literal: true\n" + @sources.join(";"), @path, @line - 1)
|
|
76
|
+
end
|
|
68
77
|
end
|
|
69
78
|
end
|
|
70
79
|
end
|
|
@@ -19,11 +19,20 @@ module ActiveSupport
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def parse(context: nil, **options)
|
|
22
|
-
source = render(context)
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
source = @content.include?("<%") ? render(context) : @content
|
|
23
|
+
|
|
24
|
+
if source == @content
|
|
25
|
+
if YAML.respond_to?(:unsafe_load)
|
|
26
|
+
YAML.unsafe_load_file(@content_path, **options) || {}
|
|
27
|
+
else
|
|
28
|
+
YAML.load_file(@content_path, **options) || {}
|
|
29
|
+
end
|
|
25
30
|
else
|
|
26
|
-
YAML.
|
|
31
|
+
if YAML.respond_to?(:unsafe_load)
|
|
32
|
+
YAML.unsafe_load(source, **options) || {}
|
|
33
|
+
else
|
|
34
|
+
YAML.load(source, **options) || {}
|
|
35
|
+
end
|
|
27
36
|
end
|
|
28
37
|
rescue Psych::SyntaxError => error
|
|
29
38
|
raise "YAML syntax error occurred while parsing #{@content_path}. " \
|
|
@@ -33,8 +42,7 @@ module ActiveSupport
|
|
|
33
42
|
|
|
34
43
|
private
|
|
35
44
|
def read(content_path)
|
|
36
|
-
require "yaml"
|
|
37
|
-
require "erb"
|
|
45
|
+
require "yaml" unless defined?(YAML)
|
|
38
46
|
|
|
39
47
|
File.read(content_path).tap do |content|
|
|
40
48
|
if content.include?("\u00A0")
|
|
@@ -44,6 +52,7 @@ module ActiveSupport
|
|
|
44
52
|
end
|
|
45
53
|
|
|
46
54
|
def render(context)
|
|
55
|
+
require "erb" unless defined?(ERB)
|
|
47
56
|
erb = ERB.new(@content).tap { |e| e.filename = @content_path }
|
|
48
57
|
context ? erb.result(context) : erb.result
|
|
49
58
|
end
|
|
@@ -16,11 +16,11 @@ class Array
|
|
|
16
16
|
# ==== Options
|
|
17
17
|
#
|
|
18
18
|
# * <tt>:words_connector</tt> - The sign or word used to join all but the last
|
|
19
|
-
# element in arrays with three or more elements (default: ", ").
|
|
19
|
+
# element in arrays with three or more elements (default: <tt>", "</tt>).
|
|
20
20
|
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element
|
|
21
|
-
# in arrays with three or more elements (default: ", and ").
|
|
21
|
+
# in arrays with three or more elements (default: <tt>", and "</tt>).
|
|
22
22
|
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements
|
|
23
|
-
# in arrays with two elements (default: " and ").
|
|
23
|
+
# in arrays with two elements (default: <tt>" and "</tt>).
|
|
24
24
|
# * <tt>:locale</tt> - If +i18n+ is available, you can set a locale and use
|
|
25
25
|
# the connector options defined on the 'support.array' namespace in the
|
|
26
26
|
# corresponding dictionary file.
|
|
@@ -4,14 +4,11 @@ require "benchmark"
|
|
|
4
4
|
return if Benchmark.respond_to?(:ms)
|
|
5
5
|
|
|
6
6
|
class << Benchmark
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# # => 0.074
|
|
14
|
-
def ms(&block)
|
|
15
|
-
1000 * realtime(&block)
|
|
7
|
+
def ms(&block) # :nodoc
|
|
8
|
+
# NOTE: Please also remove the Active Support `benchmark` dependency when removing this
|
|
9
|
+
ActiveSupport.deprecator.warn <<~TEXT
|
|
10
|
+
`Benchmark.ms` is deprecated and will be removed in Rails 8.1 without replacement.
|
|
11
|
+
TEXT
|
|
12
|
+
ActiveSupport::Benchmark.realtime(:float_millisecond, &block)
|
|
16
13
|
end
|
|
17
14
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_support/core_ext/module/redefine_method"
|
|
4
|
+
require "active_support/class_attribute"
|
|
4
5
|
|
|
5
6
|
class Class
|
|
6
7
|
# Declare a class-level attribute whose value is inheritable by subclasses.
|
|
@@ -91,24 +92,31 @@ class Class
|
|
|
91
92
|
raise TypeError, "#{name.inspect} is not a symbol nor a string"
|
|
92
93
|
end
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
RUBY
|
|
95
|
+
name = name.to_sym
|
|
96
|
+
namespaced_name = :"__class_attr_#{name}"
|
|
97
|
+
::ActiveSupport::ClassAttribute.redefine(self, name, namespaced_name, default)
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
defined?(@#{name}) ? @#{name} : self.class.#{name}
|
|
102
|
-
end
|
|
103
|
-
RUBY
|
|
99
|
+
class_methods << "def #{name}; #{namespaced_name}; end"
|
|
100
|
+
class_methods << "def #{name}=(value); self.#{namespaced_name} = value; end"
|
|
104
101
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
if singleton_class?
|
|
103
|
+
methods << <<~RUBY if instance_reader
|
|
104
|
+
silence_redefinition_of_method(:#{name})
|
|
105
|
+
def #{name}
|
|
106
|
+
self.singleton_class.#{name}
|
|
107
|
+
end
|
|
108
|
+
RUBY
|
|
109
|
+
else
|
|
110
|
+
methods << <<~RUBY if instance_reader
|
|
111
|
+
silence_redefinition_of_method def #{name}
|
|
112
|
+
if defined?(@#{name})
|
|
113
|
+
@#{name}
|
|
114
|
+
else
|
|
115
|
+
self.class.#{name}
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
RUBY
|
|
119
|
+
end
|
|
112
120
|
|
|
113
121
|
methods << <<~RUBY if instance_writer
|
|
114
122
|
silence_redefinition_of_method(:#{name}=)
|
|
@@ -125,7 +133,5 @@ class Class
|
|
|
125
133
|
|
|
126
134
|
location = caller_locations(1, 1).first
|
|
127
135
|
class_eval(["class << self", *class_methods, "end", *methods].join(";").tr("\n", ";"), location.path, location.lineno)
|
|
128
|
-
|
|
129
|
-
attrs.each { |name| public_send("#{name}=", default) }
|
|
130
136
|
end
|
|
131
137
|
end
|
|
@@ -17,6 +17,7 @@ class Date
|
|
|
17
17
|
date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
|
|
18
18
|
},
|
|
19
19
|
rfc822: "%d %b %Y",
|
|
20
|
+
rfc2822: "%d %b %Y",
|
|
20
21
|
iso8601: lambda { |date| date.iso8601 }
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -34,6 +35,7 @@ class Date
|
|
|
34
35
|
# date.to_fs(:long) # => "November 10, 2007"
|
|
35
36
|
# date.to_fs(:long_ordinal) # => "November 10th, 2007"
|
|
36
37
|
# date.to_fs(:rfc822) # => "10 Nov 2007"
|
|
38
|
+
# date.to_fs(:rfc2822) # => "10 Nov 2007"
|
|
37
39
|
# date.to_fs(:iso8601) # => "2007-11-10"
|
|
38
40
|
#
|
|
39
41
|
# == Adding your own date formats to to_fs
|
|
@@ -26,8 +26,8 @@ module DateAndTime
|
|
|
26
26
|
# Only warn once, the first time the value is used (which should
|
|
27
27
|
# be the first time #to_time is called).
|
|
28
28
|
ActiveSupport.deprecator.warn(
|
|
29
|
-
"to_time will always preserve the timezone
|
|
30
|
-
"To opt in to the new behavior, set `
|
|
29
|
+
"`to_time` will always preserve the receiver timezone rather than system local time in Rails 8.1." \
|
|
30
|
+
"To opt in to the new behavior, set `config.active_support.to_time_preserves_timezone = :zone`."
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
@@preserve_timezone = false
|
|
@@ -192,9 +192,14 @@ module Enumerable
|
|
|
192
192
|
# # => [ Person.find(1), Person.find(5), Person.find(3) ]
|
|
193
193
|
#
|
|
194
194
|
# If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
|
|
195
|
-
# If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
# If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result, unless
|
|
196
|
+
# the +filter+ option is set to +false+.
|
|
197
|
+
def in_order_of(key, series, filter: true)
|
|
198
|
+
if filter
|
|
199
|
+
group_by(&key).values_at(*series).flatten(1).compact
|
|
200
|
+
else
|
|
201
|
+
sort_by { |v| series.index(v.public_send(key)) || series.size }.compact
|
|
202
|
+
end
|
|
198
203
|
end
|
|
199
204
|
|
|
200
205
|
# Returns the sole item in the enumerable. If there are no items, or more
|
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Hash
|
|
4
|
-
# Returns a hash that includes everything except given keys.
|
|
5
|
-
# hash = { a: true, b: false, c: nil }
|
|
6
|
-
# hash.except(:c) # => { a: true, b: false }
|
|
7
|
-
# hash.except(:a, :b) # => { c: nil }
|
|
8
|
-
# hash # => { a: true, b: false, c: nil }
|
|
9
|
-
#
|
|
10
|
-
# This is useful for limiting a set of parameters to everything but a few known toggles:
|
|
11
|
-
# @person.update(params[:person].except(:admin))
|
|
12
|
-
def except(*keys)
|
|
13
|
-
slice(*self.keys - keys)
|
|
14
|
-
end unless method_defined?(:except)
|
|
15
|
-
|
|
16
4
|
# Removes the given keys from hash and returns it.
|
|
17
5
|
# hash = { a: true, b: false, c: nil }
|
|
18
6
|
# hash.except!(:c) # => { a: true, b: false }
|
|
@@ -24,14 +24,13 @@ class Module
|
|
|
24
24
|
|
|
25
25
|
def attr_internal_naming_format=(format)
|
|
26
26
|
if format.start_with?("@")
|
|
27
|
-
|
|
28
|
-
Setting `attr_internal_naming_format` with a `@` prefix is
|
|
27
|
+
raise ArgumentError, <<~MESSAGE.squish
|
|
28
|
+
Setting `attr_internal_naming_format` with a `@` prefix is not supported.
|
|
29
29
|
|
|
30
30
|
You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
|
|
31
31
|
MESSAGE
|
|
32
|
-
|
|
33
|
-
format = format.delete_prefix("@")
|
|
34
32
|
end
|
|
33
|
+
|
|
35
34
|
@attr_internal_naming_format = format
|
|
36
35
|
end
|
|
37
36
|
end
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
require "json"
|
|
5
5
|
require "bigdecimal"
|
|
6
6
|
require "ipaddr"
|
|
7
|
-
require "uri
|
|
7
|
+
require "uri"
|
|
8
8
|
require "pathname"
|
|
9
9
|
require "active_support/core_ext/big_decimal/conversions" # for #to_s
|
|
10
10
|
require "active_support/core_ext/hash/except"
|
|
@@ -65,11 +65,9 @@ class Object
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
to_h.as_json(options)
|
|
72
|
-
end
|
|
68
|
+
class Data # :nodoc:
|
|
69
|
+
def as_json(options = nil)
|
|
70
|
+
to_h.as_json(options)
|
|
73
71
|
end
|
|
74
72
|
end
|
|
75
73
|
|
|
@@ -105,7 +103,7 @@ end
|
|
|
105
103
|
|
|
106
104
|
class Symbol
|
|
107
105
|
def as_json(options = nil) # :nodoc:
|
|
108
|
-
|
|
106
|
+
name
|
|
109
107
|
end
|
|
110
108
|
end
|
|
111
109
|
|
|
@@ -164,7 +162,12 @@ end
|
|
|
164
162
|
|
|
165
163
|
class Array
|
|
166
164
|
def as_json(options = nil) # :nodoc:
|
|
167
|
-
|
|
165
|
+
if options
|
|
166
|
+
options = options.dup.freeze unless options.frozen?
|
|
167
|
+
map { |v| v.as_json(options) }
|
|
168
|
+
else
|
|
169
|
+
map { |v| v.as_json }
|
|
170
|
+
end
|
|
168
171
|
end
|
|
169
172
|
end
|
|
170
173
|
|
|
@@ -184,8 +187,11 @@ class Hash
|
|
|
184
187
|
end
|
|
185
188
|
|
|
186
189
|
result = {}
|
|
187
|
-
|
|
188
|
-
|
|
190
|
+
if options
|
|
191
|
+
options = options.dup.freeze unless options.frozen?
|
|
192
|
+
subset.each { |k, v| result[k.to_s] = v.as_json(options) }
|
|
193
|
+
else
|
|
194
|
+
subset.each { |k, v| result[k.to_s] = v.as_json }
|
|
189
195
|
end
|
|
190
196
|
result
|
|
191
197
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Range
|
|
4
|
-
# Compare two ranges and see if they overlap each other
|
|
5
|
-
# (1..5).overlap?(4..6) # => true
|
|
6
|
-
# (1..5).overlap?(7..9) # => false
|
|
7
4
|
unless Range.method_defined?(:overlap?) # Ruby 3.3+
|
|
5
|
+
# Compare two ranges and see if they overlap each other
|
|
6
|
+
# (1..5).overlap?(4..6) # => true
|
|
7
|
+
# (1..5).overlap?(7..9) # => false
|
|
8
8
|
def overlap?(other)
|
|
9
9
|
raise TypeError unless other.is_a? Range
|
|
10
10
|
|
|
@@ -248,7 +248,7 @@ class String
|
|
|
248
248
|
# optional parameter +capitalize+ to false.
|
|
249
249
|
# By default, this parameter is true.
|
|
250
250
|
#
|
|
251
|
-
# The trailing '_id' can be kept
|
|
251
|
+
# The trailing '_id' can be kept by setting the
|
|
252
252
|
# optional parameter +keep_id_suffix+ to true.
|
|
253
253
|
# By default, this parameter is false.
|
|
254
254
|
#
|
|
@@ -128,7 +128,9 @@ module ActiveSupport # :nodoc:
|
|
|
128
128
|
escaped_args = Array(args).map { |arg| explicit_html_escape_interpolated_argument(arg) }
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
self.class.new(super(escaped_args))
|
|
131
|
+
new_safe_buffer = self.class.new(super(escaped_args))
|
|
132
|
+
new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
|
|
133
|
+
new_safe_buffer
|
|
132
134
|
end
|
|
133
135
|
|
|
134
136
|
attr_reader :html_safe
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Thread::Backtrace::Location # :nodoc:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ErrorHighlight.spot(ex, backtrace_location: self)
|
|
7
|
-
end
|
|
8
|
-
else
|
|
9
|
-
def spot(ex)
|
|
10
|
-
end
|
|
4
|
+
def spot(ex)
|
|
5
|
+
ErrorHighlight.spot(ex, backtrace_location: self)
|
|
11
6
|
end
|
|
12
7
|
end
|
|
@@ -147,6 +147,13 @@ class Time
|
|
|
147
147
|
elsif zone.respond_to?(:utc_to_local)
|
|
148
148
|
new_time = ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone)
|
|
149
149
|
|
|
150
|
+
# Some versions of Ruby have a bug where Time.new with a zone object and
|
|
151
|
+
# fractional seconds will end up with a broken utc_offset.
|
|
152
|
+
# This is fixed in Ruby 3.3.1 and 3.2.4
|
|
153
|
+
unless new_time.utc_offset.integer?
|
|
154
|
+
new_time += 0
|
|
155
|
+
end
|
|
156
|
+
|
|
150
157
|
# When there are two occurrences of a nominal time due to DST ending,
|
|
151
158
|
# `Time.new` chooses the first chronological occurrence (the one with a
|
|
152
159
|
# larger UTC offset). However, for `change`, we want to choose the
|
|
@@ -217,8 +224,13 @@ class Time
|
|
|
217
224
|
# Returns a new Time representing the time a number of seconds since the instance time
|
|
218
225
|
def since(seconds)
|
|
219
226
|
self + seconds
|
|
220
|
-
rescue
|
|
221
|
-
to_datetime.since(seconds)
|
|
227
|
+
rescue TypeError
|
|
228
|
+
result = to_datetime.since(seconds)
|
|
229
|
+
ActiveSupport.deprecator.warn(
|
|
230
|
+
"Passing an instance of #{seconds.class} to #{self.class}#since is deprecated. This behavior will raise " \
|
|
231
|
+
"a `TypeError` in Rails 8.1."
|
|
232
|
+
)
|
|
233
|
+
result
|
|
222
234
|
end
|
|
223
235
|
alias :in :since
|
|
224
236
|
|
|
@@ -22,6 +22,7 @@ class Time
|
|
|
22
22
|
offset_format = time.formatted_offset(false)
|
|
23
23
|
time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
|
|
24
24
|
},
|
|
25
|
+
rfc2822: lambda { |time| time.rfc2822 },
|
|
25
26
|
iso8601: lambda { |time| time.iso8601 }
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -40,6 +41,7 @@ class Time
|
|
|
40
41
|
# time.to_fs(:long) # => "January 18, 2007 06:10"
|
|
41
42
|
# time.to_fs(:long_ordinal) # => "January 18th, 2007 06:10"
|
|
42
43
|
# time.to_fs(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
|
|
44
|
+
# time.to_fs(:rfc2822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
|
|
43
45
|
# time.to_fs(:iso8601) # => "2007-01-18T06:10:17-06:00"
|
|
44
46
|
#
|
|
45
47
|
# == Adding your own time formats to +to_fs+
|