activesupport 6.0.6.1 → 6.1.0.rc1
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 +337 -573
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +2 -2
- data/lib/active_support/cache/mem_cache_store.rb +20 -14
- data/lib/active_support/cache/memory_store.rb +38 -26
- data/lib/active_support/cache/redis_cache_store.rb +25 -25
- data/lib/active_support/cache/strategy/local_cache.rb +13 -4
- data/lib/active_support/cache.rb +75 -34
- data/lib/active_support/callbacks.rb +65 -56
- data/lib/active_support/concern.rb +46 -2
- data/lib/active_support/configurable.rb +3 -3
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- 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/enumerable.rb +76 -4
- 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/keys.rb +1 -1
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +38 -28
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +5 -1
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range/compare_range.rb +9 -3
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/inflections.rb +38 -4
- 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 +8 -38
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +5 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +7 -2
- data/lib/active_support/dependencies.rb +38 -24
- data/lib/active_support/deprecation/behaviors.rb +15 -2
- 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 +3 -2
- data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +6 -2
- data/lib/active_support/duration/iso8601_serializer.rb +15 -9
- data/lib/active_support/duration.rb +71 -22
- data/lib/active_support/encrypted_file.rb +19 -2
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +69 -133
- data/lib/active_support/execution_wrapper.rb +13 -16
- data/lib/active_support/fork_tracker.rb +58 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +35 -22
- data/lib/active_support/i18n_railtie.rb +14 -19
- data/lib/active_support/inflector/inflections.rb +1 -2
- data/lib/active_support/inflector/methods.rb +35 -31
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +5 -1
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +8 -0
- 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 -12
- data/lib/active_support/message_encryptor.rb +4 -7
- data/lib/active_support/message_verifier.rb +5 -5
- 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 +4 -42
- data/lib/active_support/multibyte/unicode.rb +9 -83
- data/lib/active_support/notifications/fanout.rb +23 -8
- data/lib/active_support/notifications/instrumenter.rb +6 -15
- data/lib/active_support/notifications.rb +31 -4
- data/lib/active_support/number_helper/number_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 +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/number_helper.rb +29 -14
- data/lib/active_support/option_merger.rb +2 -1
- data/lib/active_support/ordered_options.rb +8 -2
- data/lib/active_support/parameter_filter.rb +15 -10
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +1 -1
- 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 +4 -2
- data/lib/active_support/subscriber.rb +12 -7
- data/lib/active_support/tagged_logging.rb +29 -4
- data/lib/active_support/testing/assertions.rb +18 -11
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/parallelization.rb +12 -95
- data/lib/active_support/testing/time_helpers.rb +40 -3
- data/lib/active_support/time_with_zone.rb +66 -42
- data/lib/active_support/values/time_zone.rb +20 -10
- data/lib/active_support/xml_mini/rexml.rb +8 -1
- data/lib/active_support.rb +13 -1
- metadata +39 -42
- 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/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
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/kernel/singleton_class"
|
4
3
|
require "active_support/core_ext/module/redefine_method"
|
5
|
-
require "active_support/core_ext/array/extract_options"
|
6
4
|
|
7
5
|
class Class
|
8
6
|
# Declare a class-level attribute whose value is inheritable by subclasses.
|
@@ -84,58 +82,50 @@ class Class
|
|
84
82
|
# To set a default value for the attribute, pass <tt>default:</tt>, like so:
|
85
83
|
#
|
86
84
|
# class_attribute :settings, default: {}
|
87
|
-
def class_attribute(
|
88
|
-
|
89
|
-
instance_accessor: true,
|
90
|
-
instance_reader: instance_accessor,
|
91
|
-
instance_writer: instance_accessor,
|
92
|
-
instance_predicate: true,
|
93
|
-
default: nil
|
94
|
-
)
|
95
|
-
attrs.each do |name|
|
96
|
-
singleton_class.silence_redefinition_of_method(name)
|
97
|
-
define_singleton_method(name) { default }
|
98
|
-
|
99
|
-
singleton_class.silence_redefinition_of_method("#{name}?")
|
100
|
-
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
85
|
+
def class_attribute(*attrs, instance_accessor: true,
|
86
|
+
instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil)
|
101
87
|
|
102
|
-
|
88
|
+
class_methods, methods = [], []
|
89
|
+
attrs.each do |name|
|
90
|
+
unless name.is_a?(Symbol) || name.is_a?(String)
|
91
|
+
raise TypeError, "#{name.inspect} is not a symbol nor a string"
|
92
|
+
end
|
103
93
|
|
104
|
-
|
105
|
-
|
106
|
-
|
94
|
+
class_methods << <<~RUBY # In case the method exists and is not public
|
95
|
+
silence_redefinition_of_method def #{name}
|
96
|
+
end
|
97
|
+
RUBY
|
107
98
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
if instance_variable_defined? ivar
|
112
|
-
instance_variable_get ivar
|
113
|
-
else
|
114
|
-
singleton_class.send name
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
99
|
+
methods << <<~RUBY if instance_reader
|
100
|
+
silence_redefinition_of_method def #{name}
|
101
|
+
defined?(@#{name}) ? @#{name} : self.class.#{name}
|
118
102
|
end
|
119
|
-
|
120
|
-
end
|
103
|
+
RUBY
|
121
104
|
|
122
|
-
|
123
|
-
|
124
|
-
if
|
125
|
-
|
126
|
-
|
127
|
-
self.class.public_send name
|
128
|
-
end
|
105
|
+
class_methods << <<~RUBY
|
106
|
+
silence_redefinition_of_method def #{name}=(value)
|
107
|
+
redefine_method(:#{name}) { value } if singleton_class?
|
108
|
+
redefine_singleton_method(:#{name}) { value }
|
109
|
+
value
|
129
110
|
end
|
111
|
+
RUBY
|
130
112
|
|
131
|
-
|
132
|
-
|
113
|
+
methods << <<~RUBY if instance_writer
|
114
|
+
silence_redefinition_of_method(:#{name}=)
|
115
|
+
attr_writer :#{name}
|
116
|
+
RUBY
|
133
117
|
|
134
|
-
if
|
135
|
-
|
136
|
-
|
118
|
+
if instance_predicate
|
119
|
+
class_methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end"
|
120
|
+
if instance_reader
|
121
|
+
methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end"
|
137
122
|
end
|
138
123
|
end
|
139
124
|
end
|
125
|
+
|
126
|
+
location = caller_locations(1, 1).first
|
127
|
+
class_eval(["class << self", *class_methods, "end", *methods].join(";").tr("\n", ";"), location.path, location.lineno)
|
128
|
+
|
129
|
+
attrs.each { |name| public_send("#{name}=", default) }
|
140
130
|
end
|
141
131
|
end
|
@@ -1,39 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Class
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# C.descendants # => [B, A, D]
|
21
|
-
def descendants
|
22
|
-
descendants = []
|
23
|
-
ObjectSpace.each_object(singleton_class) do |k|
|
24
|
-
next if k.singleton_class?
|
25
|
-
descendants.unshift k unless k == self
|
26
|
-
end
|
27
|
-
descendants
|
28
|
-
end
|
29
|
-
rescue StandardError # JRuby 9.0.4.0 and earlier
|
30
|
-
def descendants
|
31
|
-
descendants = []
|
32
|
-
ObjectSpace.each_object(Class) do |k|
|
33
|
-
descendants.unshift k if k < self
|
34
|
-
end
|
35
|
-
descendants.uniq!
|
36
|
-
descendants
|
4
|
+
# Returns an array with all classes that are < than its receiver.
|
5
|
+
#
|
6
|
+
# class C; end
|
7
|
+
# C.descendants # => []
|
8
|
+
#
|
9
|
+
# class B < C; end
|
10
|
+
# C.descendants # => [B]
|
11
|
+
#
|
12
|
+
# class A < B; end
|
13
|
+
# C.descendants # => [B, A]
|
14
|
+
#
|
15
|
+
# class D < C; end
|
16
|
+
# C.descendants # => [B, A, D]
|
17
|
+
def descendants
|
18
|
+
ObjectSpace.each_object(singleton_class).reject do |k|
|
19
|
+
k.singleton_class? || k == self
|
37
20
|
end
|
38
21
|
end
|
39
22
|
|
@@ -45,10 +28,6 @@ class Class
|
|
45
28
|
#
|
46
29
|
# Foo.subclasses # => [Bar]
|
47
30
|
def subclasses
|
48
|
-
|
49
|
-
chain.each do |k|
|
50
|
-
subclasses << k unless chain.any? { |c| c > k }
|
51
|
-
end
|
52
|
-
subclasses
|
31
|
+
descendants.select { |descendant| descendant.superclass == self }
|
53
32
|
end
|
54
33
|
end
|
@@ -10,6 +10,7 @@ class Date
|
|
10
10
|
short: "%d %b",
|
11
11
|
long: "%B %d, %Y",
|
12
12
|
db: "%Y-%m-%d",
|
13
|
+
inspect: "%Y-%m-%d",
|
13
14
|
number: "%Y%m%d",
|
14
15
|
long_ordinal: lambda { |date|
|
15
16
|
day_format = ActiveSupport::Inflector.ordinalize(date.day)
|
@@ -80,7 +81,7 @@ class Date
|
|
80
81
|
# If the *application's* timezone is needed, then use +in_time_zone+ instead.
|
81
82
|
def to_time(form = :local)
|
82
83
|
raise ArgumentError, "Expected :local or :utc, got #{form.inspect}." unless [:local, :utc].include?(form)
|
83
|
-
::Time.
|
84
|
+
::Time.public_send(form, year, month, day)
|
84
85
|
end
|
85
86
|
|
86
87
|
silence_redefinition_of_method :xmlschema
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/object/try"
|
4
|
+
require "active_support/core_ext/date_time/conversions"
|
4
5
|
|
5
6
|
module DateAndTime
|
6
7
|
module Calculations
|
@@ -30,6 +31,18 @@ module DateAndTime
|
|
30
31
|
to_date == ::Date.current
|
31
32
|
end
|
32
33
|
|
34
|
+
# Returns true if the date/time is tomorrow.
|
35
|
+
def tomorrow?
|
36
|
+
to_date == ::Date.current.tomorrow
|
37
|
+
end
|
38
|
+
alias :next_day? :tomorrow?
|
39
|
+
|
40
|
+
# Returns true if the date/time is yesterday.
|
41
|
+
def yesterday?
|
42
|
+
to_date == ::Date.current.yesterday
|
43
|
+
end
|
44
|
+
alias :prev_day? :yesterday?
|
45
|
+
|
33
46
|
# Returns true if the date/time is in the past.
|
34
47
|
def past?
|
35
48
|
self < self.class.current
|
@@ -12,5 +12,20 @@ module DateAndTime
|
|
12
12
|
# this behavior, but new apps will have an initializer that sets
|
13
13
|
# this to true, because the new behavior is preferred.
|
14
14
|
mattr_accessor :preserve_timezone, instance_writer: false, default: false
|
15
|
+
|
16
|
+
# Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
|
17
|
+
#
|
18
|
+
# When `true`, it returns local times with an UTC offset, with `false` local
|
19
|
+
# times are returned as UTC.
|
20
|
+
#
|
21
|
+
# # Given this zone:
|
22
|
+
# zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
|
23
|
+
#
|
24
|
+
# # With `utc_to_local_returns_utc_offset_times = false`, local time is converted to UTC:
|
25
|
+
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 UTC
|
26
|
+
#
|
27
|
+
# # With `utc_to_local_returns_utc_offset_times = true`, local time is returned with UTC offset:
|
28
|
+
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 -0500
|
29
|
+
mattr_accessor :utc_to_local_returns_utc_offset_times, instance_writer: false, default: false
|
15
30
|
end
|
16
31
|
end
|
@@ -44,7 +44,8 @@ module Enumerable
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
# Convert an enumerable to a hash
|
47
|
+
# Convert an enumerable to a hash, using the block result as the key and the
|
48
|
+
# element as the value.
|
48
49
|
#
|
49
50
|
# people.index_by(&:login)
|
50
51
|
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
@@ -61,12 +62,19 @@ module Enumerable
|
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
64
|
-
# Convert an enumerable to a hash
|
65
|
+
# Convert an enumerable to a hash, using the element as the key and the block
|
66
|
+
# result as the value.
|
65
67
|
#
|
66
68
|
# post = Post.new(title: "hey there", body: "what's up?")
|
67
69
|
#
|
68
70
|
# %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
|
69
71
|
# # => { title: "hey there", body: "what's up?" }
|
72
|
+
#
|
73
|
+
# If an argument is passed instead of a block, it will be used as the value
|
74
|
+
# for all elements:
|
75
|
+
#
|
76
|
+
# %i( created_at updated_at ).index_with(Time.now)
|
77
|
+
# # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
|
70
78
|
def index_with(default = INDEX_WITH_DEFAULT)
|
71
79
|
if block_given?
|
72
80
|
result = {}
|
@@ -134,7 +142,7 @@ module Enumerable
|
|
134
142
|
excluding(*elements)
|
135
143
|
end
|
136
144
|
|
137
|
-
#
|
145
|
+
# Extract the given key from each element in the enumerable.
|
138
146
|
#
|
139
147
|
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
|
140
148
|
# # => ["David", "Rafael", "Aaron"]
|
@@ -145,9 +153,62 @@ module Enumerable
|
|
145
153
|
if keys.many?
|
146
154
|
map { |element| keys.map { |key| element[key] } }
|
147
155
|
else
|
148
|
-
|
156
|
+
key = keys.first
|
157
|
+
map { |element| element[key] }
|
149
158
|
end
|
150
159
|
end
|
160
|
+
|
161
|
+
# Extract the given key from the first element in the enumerable.
|
162
|
+
#
|
163
|
+
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
|
164
|
+
# # => "David"
|
165
|
+
#
|
166
|
+
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
|
167
|
+
# # => [1, "David"]
|
168
|
+
def pick(*keys)
|
169
|
+
return if none?
|
170
|
+
|
171
|
+
if keys.many?
|
172
|
+
keys.map { |key| first[key] }
|
173
|
+
else
|
174
|
+
first[keys.first]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns a new +Array+ without the blank items.
|
179
|
+
# Uses Object#blank? for determining if an item is blank.
|
180
|
+
#
|
181
|
+
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
|
182
|
+
# # => [1, 2, true]
|
183
|
+
#
|
184
|
+
# Set.new([nil, "", 1, 2])
|
185
|
+
# # => [2, 1] (or [1, 2])
|
186
|
+
#
|
187
|
+
# When called on a +Hash+, returns a new +Hash+ without the blank values.
|
188
|
+
#
|
189
|
+
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
|
190
|
+
# #=> { b: 1, f: true }
|
191
|
+
def compact_blank
|
192
|
+
reject(&:blank?)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class Hash
|
197
|
+
# Hash#reject has its own definition, so this needs one too.
|
198
|
+
def compact_blank #:nodoc:
|
199
|
+
reject { |_k, v| v.blank? }
|
200
|
+
end
|
201
|
+
|
202
|
+
# Removes all blank values from the +Hash+ in place and returns self.
|
203
|
+
# Uses Object#blank? for determining if a value is blank.
|
204
|
+
#
|
205
|
+
# h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
|
206
|
+
# h.compact_blank!
|
207
|
+
# # => { b: 1, f: true }
|
208
|
+
def compact_blank!
|
209
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
210
|
+
delete_if { |_k, v| v.blank? }
|
211
|
+
end
|
151
212
|
end
|
152
213
|
|
153
214
|
class Range #:nodoc:
|
@@ -185,4 +246,15 @@ class Array #:nodoc:
|
|
185
246
|
super
|
186
247
|
end
|
187
248
|
end
|
249
|
+
|
250
|
+
# Removes all blank elements from the +Array+ in place and returns self.
|
251
|
+
# Uses Object#blank? for determining if an item is blank.
|
252
|
+
#
|
253
|
+
# a = [1, "", nil, 2, " ", [], {}, false, true]
|
254
|
+
# a.compact_blank!
|
255
|
+
# # => [1, 2, true]
|
256
|
+
def compact_blank!
|
257
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
258
|
+
delete_if(&:blank?)
|
259
|
+
end
|
188
260
|
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,7 +112,7 @@ 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
|
@@ -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
|
@@ -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
|
@@ -48,28 +48,25 @@ class Module
|
|
48
48
|
# end
|
49
49
|
#
|
50
50
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
51
|
-
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
|
51
|
+
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
|
52
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
53
|
+
location ||= caller_locations(1, 1).first
|
54
|
+
|
55
|
+
definition = []
|
52
56
|
syms.each do |sym|
|
53
57
|
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
58
|
|
57
|
-
|
58
|
-
@@#{sym}
|
59
|
-
end
|
60
|
-
EOS
|
59
|
+
definition << "def self.#{sym}; @@#{sym}; end"
|
61
60
|
|
62
61
|
if instance_reader && instance_accessor
|
63
|
-
|
64
|
-
def #{sym}
|
65
|
-
@@#{sym}
|
66
|
-
end
|
67
|
-
EOS
|
62
|
+
definition << "def #{sym}; @@#{sym}; end"
|
68
63
|
end
|
69
64
|
|
70
65
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
71
|
-
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
|
66
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
72
67
|
end
|
68
|
+
|
69
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
73
70
|
end
|
74
71
|
alias :cattr_reader :mattr_reader
|
75
72
|
|
@@ -115,28 +112,24 @@ class Module
|
|
115
112
|
# end
|
116
113
|
#
|
117
114
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
118
|
-
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
|
115
|
+
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
|
116
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
117
|
+
location ||= caller_locations(1, 1).first
|
118
|
+
|
119
|
+
definition = []
|
119
120
|
syms.each do |sym|
|
120
121
|
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
|
122
|
+
definition << "def self.#{sym}=(val); @@#{sym} = val; end"
|
128
123
|
|
129
124
|
if instance_writer && instance_accessor
|
130
|
-
|
131
|
-
def #{sym}=(obj)
|
132
|
-
@@#{sym} = obj
|
133
|
-
end
|
134
|
-
EOS
|
125
|
+
definition << "def #{sym}=(val); @@#{sym} = val; end"
|
135
126
|
end
|
136
127
|
|
137
128
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
138
|
-
|
129
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
139
130
|
end
|
131
|
+
|
132
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
140
133
|
end
|
141
134
|
alias :cattr_writer :mattr_writer
|
142
135
|
|
@@ -205,8 +198,9 @@ class Module
|
|
205
198
|
#
|
206
199
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
207
200
|
def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
|
208
|
-
|
209
|
-
|
201
|
+
location = caller_locations(1, 1).first
|
202
|
+
mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
|
203
|
+
mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
|
210
204
|
end
|
211
205
|
alias :cattr_accessor :mattr_accessor
|
212
206
|
end
|
@@ -33,7 +33,7 @@ class Module
|
|
33
33
|
# end
|
34
34
|
#
|
35
35
|
# Current.new.user # => NoMethodError
|
36
|
-
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true) # :nodoc:
|
36
|
+
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
|
37
37
|
syms.each do |sym|
|
38
38
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
39
39
|
|
@@ -52,6 +52,8 @@ class Module
|
|
52
52
|
end
|
53
53
|
EOS
|
54
54
|
end
|
55
|
+
|
56
|
+
Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
|
55
57
|
end
|
56
58
|
end
|
57
59
|
alias :thread_cattr_reader :thread_mattr_reader
|
@@ -74,7 +76,7 @@ class Module
|
|
74
76
|
# end
|
75
77
|
#
|
76
78
|
# Current.new.user = "DHH" # => NoMethodError
|
77
|
-
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
|
79
|
+
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
|
78
80
|
syms.each do |sym|
|
79
81
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
80
82
|
|
@@ -93,6 +95,8 @@ class Module
|
|
93
95
|
end
|
94
96
|
EOS
|
95
97
|
end
|
98
|
+
|
99
|
+
public_send("#{sym}=", default) unless default.nil?
|
96
100
|
end
|
97
101
|
end
|
98
102
|
alias :thread_cattr_writer :thread_mattr_writer
|
@@ -136,8 +140,8 @@ class Module
|
|
136
140
|
#
|
137
141
|
# Current.new.user = "DHH" # => NoMethodError
|
138
142
|
# Current.new.user # => NoMethodError
|
139
|
-
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true)
|
140
|
-
thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor)
|
143
|
+
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
|
144
|
+
thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
|
141
145
|
thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
|
142
146
|
end
|
143
147
|
alias :thread_cattr_accessor :thread_mattr_accessor
|
@@ -104,10 +104,16 @@ class Module
|
|
104
104
|
# * grok the behavior of our class in one glance,
|
105
105
|
# * clean up monolithic junk-drawer classes by separating their concerns, and
|
106
106
|
# * stop leaning on protected/private for crude "this is internal stuff" modularity.
|
107
|
+
#
|
108
|
+
# === Prepending `concerning`
|
109
|
+
#
|
110
|
+
# `concerning` supports a `prepend: true` argument which will `prepend` the
|
111
|
+
# concern instead of using `include` for it.
|
107
112
|
module Concerning
|
108
113
|
# Define a new concern and mix it in.
|
109
|
-
def concerning(topic, &block)
|
110
|
-
|
114
|
+
def concerning(topic, prepend: false, &block)
|
115
|
+
method = prepend ? :prepend : :include
|
116
|
+
__send__(method, concern(topic, &block))
|
111
117
|
end
|
112
118
|
|
113
119
|
# A low-cruft shortcut to define a concern.
|