activerecord 7.2.0 → 7.2.1
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 +42 -0
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/attributes.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +23 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +41 -14
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- data/lib/active_record/core.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +1 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +1 -1
- data/lib/active_record/enum.rb +18 -7
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/model_schema.rb +5 -1
- data/lib/active_record/reflection.rb +10 -6
- data/lib/active_record/relation/batches.rb +4 -4
- data/lib/active_record/scoping/named.rb +1 -0
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b72f5d48ed3098cb5d20e0f33ed2b082cccdb963338accb19f89c982a790ff1
|
4
|
+
data.tar.gz: 313c950f13b265ab63f2b5882f8fe9b20642e4f8702f1862f55abd030862a450
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99f658cc51d0fc5d92b0869e6479f7569a6ffb74c075c514abefa2af4e248a9a3bfda7e01a4270c09c2aca91b99b01337939b1b40bc727a8c556d3aa47e90e99
|
7
|
+
data.tar.gz: 95a70341ef2dde997393b143c2beb7d25c74494f76159a888c591978c229e6c9de567183fbc4824290bd466bca3ea50e6f323c00596971af10eb53f21c3eb906
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,45 @@
|
|
1
|
+
## Rails 7.2.1 (August 22, 2024) ##
|
2
|
+
|
3
|
+
* Fix detection for `enum` columns with parallelized tests and PostgreSQL.
|
4
|
+
|
5
|
+
*Rafael Mendonça França*
|
6
|
+
|
7
|
+
* Allow to eager load nested nil associations.
|
8
|
+
|
9
|
+
*fatkodima*
|
10
|
+
|
11
|
+
* Fix swallowing ignore order warning when batching using `BatchEnumerator`.
|
12
|
+
|
13
|
+
*fatkodima*
|
14
|
+
|
15
|
+
* Fix memory bloat on the connection pool when using the Fiber `IsolatedExecutionState`.
|
16
|
+
|
17
|
+
*Jean Boussier*
|
18
|
+
|
19
|
+
* Restore inferred association class with the same modularized name.
|
20
|
+
|
21
|
+
*Justin Ko*
|
22
|
+
|
23
|
+
* Fix `ActiveRecord::Base.inspect` to properly explain how to load schema information.
|
24
|
+
|
25
|
+
*Jean Boussier*
|
26
|
+
|
27
|
+
* Check invalid `enum` options for the new syntax.
|
28
|
+
|
29
|
+
The options using `_` prefix in the old syntax are invalid in the new syntax.
|
30
|
+
|
31
|
+
*Rafael Mendonça França*
|
32
|
+
|
33
|
+
* Fix `ActiveRecord::Encryption::EncryptedAttributeType#type` to return
|
34
|
+
actual cast type.
|
35
|
+
|
36
|
+
*Vasiliy Ermolovich*
|
37
|
+
|
38
|
+
* Fix `create_table` with `:auto_increment` option for MySQL adapter.
|
39
|
+
|
40
|
+
*fatkodima*
|
41
|
+
|
42
|
+
|
1
43
|
## Rails 7.2.0 (August 09, 2024) ##
|
2
44
|
|
3
45
|
* Handle commas in Sqlite3 default function definitions.
|
@@ -25,15 +25,17 @@ module ActiveRecord
|
|
25
25
|
# column which this will persist to.
|
26
26
|
#
|
27
27
|
# +cast_type+ A symbol such as +:string+ or +:integer+, or a type object
|
28
|
-
# to be used for this attribute.
|
29
|
-
#
|
28
|
+
# to be used for this attribute. If this parameter is not passed, the previously
|
29
|
+
# defined type (if any) will be used.
|
30
|
+
# Otherwise, the type will be ActiveModel::Type::Value.
|
31
|
+
# See the examples below for more information about providing custom type objects.
|
30
32
|
#
|
31
33
|
# ==== Options
|
32
34
|
#
|
33
35
|
# The following options are accepted:
|
34
36
|
#
|
35
37
|
# +default+ The default value to use when no value is provided. If this option
|
36
|
-
# is not passed, the
|
38
|
+
# is not passed, the previously defined default value (if any) on the superclass or in the schema will be used.
|
37
39
|
# Otherwise, the default will be +nil+.
|
38
40
|
#
|
39
41
|
# +array+ (PostgreSQL only) specifies that the type should be an array (see the
|
@@ -118,6 +118,27 @@ module ActiveRecord
|
|
118
118
|
# * private methods that require being called in a +synchronize+ blocks
|
119
119
|
# are now explicitly documented
|
120
120
|
class ConnectionPool
|
121
|
+
class WeakThreadKeyMap # :nodoc:
|
122
|
+
# FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
|
123
|
+
# but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
|
124
|
+
def initialize
|
125
|
+
@map = {}
|
126
|
+
end
|
127
|
+
|
128
|
+
def clear
|
129
|
+
@map.clear
|
130
|
+
end
|
131
|
+
|
132
|
+
def [](key)
|
133
|
+
@map[key]
|
134
|
+
end
|
135
|
+
|
136
|
+
def []=(key, value)
|
137
|
+
@map.select! { |c, _| c.alive? }
|
138
|
+
@map[key] = value
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
121
142
|
class Lease # :nodoc:
|
122
143
|
attr_accessor :connection, :sticky
|
123
144
|
|
@@ -145,48 +166,9 @@ module ActiveRecord
|
|
145
166
|
end
|
146
167
|
|
147
168
|
class LeaseRegistry # :nodoc:
|
148
|
-
if ObjectSpace.const_defined?(:WeakKeyMap) # RUBY_VERSION >= 3.3
|
149
|
-
WeakKeyMap = ::ObjectSpace::WeakKeyMap # :nodoc:
|
150
|
-
else
|
151
|
-
class WeakKeyMap # :nodoc:
|
152
|
-
def initialize
|
153
|
-
@map = ObjectSpace::WeakMap.new
|
154
|
-
@values = nil
|
155
|
-
@size = 0
|
156
|
-
end
|
157
|
-
|
158
|
-
alias_method :clear, :initialize
|
159
|
-
|
160
|
-
def [](key)
|
161
|
-
prune if @map.size != @size
|
162
|
-
@map[key]
|
163
|
-
end
|
164
|
-
|
165
|
-
def []=(key, value)
|
166
|
-
@map[key] = value
|
167
|
-
prune if @map.size != @size
|
168
|
-
value
|
169
|
-
end
|
170
|
-
|
171
|
-
def delete(key)
|
172
|
-
if value = self[key]
|
173
|
-
self[key] = nil
|
174
|
-
prune
|
175
|
-
end
|
176
|
-
value
|
177
|
-
end
|
178
|
-
|
179
|
-
private
|
180
|
-
def prune(force = false)
|
181
|
-
@values = @map.values
|
182
|
-
@size = @map.size
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
169
|
def initialize
|
188
170
|
@mutex = Mutex.new
|
189
|
-
@map =
|
171
|
+
@map = WeakThreadKeyMap.new
|
190
172
|
end
|
191
173
|
|
192
174
|
def [](context)
|
@@ -197,7 +179,7 @@ module ActiveRecord
|
|
197
179
|
|
198
180
|
def clear
|
199
181
|
@mutex.synchronize do
|
200
|
-
@map
|
182
|
+
@map.clear
|
201
183
|
end
|
202
184
|
end
|
203
185
|
end
|
@@ -630,8 +612,6 @@ module ActiveRecord
|
|
630
612
|
remove conn
|
631
613
|
end
|
632
614
|
end
|
633
|
-
|
634
|
-
prune_thread_cache
|
635
615
|
end
|
636
616
|
|
637
617
|
# Disconnect all connections that have been idle for at least
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "concurrent/map"
|
4
|
+
require "concurrent/atomic/atomic_fixnum"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module ConnectionAdapters # :nodoc:
|
@@ -35,7 +36,9 @@ module ActiveRecord
|
|
35
36
|
alias_method :enabled?, :enabled
|
36
37
|
alias_method :dirties?, :dirties
|
37
38
|
|
38
|
-
def initialize(max_size)
|
39
|
+
def initialize(version, max_size)
|
40
|
+
@version = version
|
41
|
+
@current_version = version.value
|
39
42
|
@map = {}
|
40
43
|
@max_size = max_size
|
41
44
|
@enabled = false
|
@@ -43,14 +46,17 @@ module ActiveRecord
|
|
43
46
|
end
|
44
47
|
|
45
48
|
def size
|
49
|
+
check_version
|
46
50
|
@map.size
|
47
51
|
end
|
48
52
|
|
49
53
|
def empty?
|
54
|
+
check_version
|
50
55
|
@map.empty?
|
51
56
|
end
|
52
57
|
|
53
58
|
def [](key)
|
59
|
+
check_version
|
54
60
|
return unless @enabled
|
55
61
|
|
56
62
|
if entry = @map.delete(key)
|
@@ -59,6 +65,8 @@ module ActiveRecord
|
|
59
65
|
end
|
60
66
|
|
61
67
|
def compute_if_absent(key)
|
68
|
+
check_version
|
69
|
+
|
62
70
|
return yield unless @enabled
|
63
71
|
|
64
72
|
if entry = @map.delete(key)
|
@@ -76,12 +84,40 @@ module ActiveRecord
|
|
76
84
|
@map.clear
|
77
85
|
self
|
78
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def check_version
|
90
|
+
if @current_version != @version.value
|
91
|
+
@map.clear
|
92
|
+
@current_version = @version.value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class QueryCacheRegistry # :nodoc:
|
98
|
+
def initialize
|
99
|
+
@mutex = Mutex.new
|
100
|
+
@map = ConnectionPool::WeakThreadKeyMap.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def compute_if_absent(context)
|
104
|
+
@map[context] || @mutex.synchronize do
|
105
|
+
@map[context] ||= yield
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear
|
110
|
+
@map.synchronize do
|
111
|
+
@map.clear
|
112
|
+
end
|
113
|
+
end
|
79
114
|
end
|
80
115
|
|
81
116
|
module ConnectionPoolConfiguration # :nodoc:
|
82
117
|
def initialize(...)
|
83
118
|
super
|
84
|
-
@
|
119
|
+
@query_cache_version = Concurrent::AtomicFixnum.new
|
120
|
+
@thread_query_caches = QueryCacheRegistry.new
|
85
121
|
@query_cache_max_size = \
|
86
122
|
case query_cache = db_config&.query_cache
|
87
123
|
when 0, false
|
@@ -141,25 +177,16 @@ module ActiveRecord
|
|
141
177
|
# With transactional fixtures, and especially systems test
|
142
178
|
# another thread may use the same connection, but with a different
|
143
179
|
# query cache. So we must clear them all.
|
144
|
-
@
|
145
|
-
else
|
146
|
-
query_cache.clear
|
180
|
+
@query_cache_version.increment
|
147
181
|
end
|
182
|
+
query_cache.clear
|
148
183
|
end
|
149
184
|
|
150
185
|
def query_cache
|
151
186
|
@thread_query_caches.compute_if_absent(ActiveSupport::IsolatedExecutionState.context) do
|
152
|
-
Store.new(@query_cache_max_size)
|
187
|
+
Store.new(@query_cache_version, @query_cache_max_size)
|
153
188
|
end
|
154
189
|
end
|
155
|
-
|
156
|
-
private
|
157
|
-
def prune_thread_cache
|
158
|
-
dead_threads = @thread_query_caches.keys.reject(&:alive?)
|
159
|
-
dead_threads.each do |dead_thread|
|
160
|
-
@thread_query_caches.delete(dead_thread)
|
161
|
-
end
|
162
|
-
end
|
163
190
|
end
|
164
191
|
|
165
192
|
attr_accessor :query_cache
|
data/lib/active_record/core.rb
CHANGED
@@ -353,8 +353,8 @@ module ActiveRecord
|
|
353
353
|
super
|
354
354
|
elsif abstract_class?
|
355
355
|
"#{super}(abstract)"
|
356
|
-
elsif !connected?
|
357
|
-
"#{super} (call '#{super}.
|
356
|
+
elsif !schema_loaded? && !connected?
|
357
|
+
"#{super} (call '#{super}.load_schema' to load schema informations)"
|
358
358
|
elsif table_exists?
|
359
359
|
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
360
360
|
"#{super}(#{attr_list})"
|
@@ -7,13 +7,13 @@ module ActiveRecord
|
|
7
7
|
# This is the central piece that connects the encryption system with +encrypts+ declarations in the
|
8
8
|
# model classes. Whenever you declare an attribute as encrypted, it configures an +EncryptedAttributeType+
|
9
9
|
# for that attribute.
|
10
|
-
class EncryptedAttributeType < ::
|
10
|
+
class EncryptedAttributeType < ::ActiveModel::Type::Value
|
11
11
|
include ActiveModel::Type::Helpers::Mutable
|
12
12
|
|
13
13
|
attr_reader :scheme, :cast_type
|
14
14
|
|
15
15
|
delegate :key_provider, :downcase?, :deterministic?, :previous_schemes, :with_context, :fixed?, to: :scheme
|
16
|
-
delegate :accessor, to: :cast_type
|
16
|
+
delegate :accessor, :type, to: :cast_type
|
17
17
|
|
18
18
|
# === Options
|
19
19
|
#
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
serialize_message build_encrypted_message(clear_text, key_provider: key_provider, cipher_options: cipher_options)
|
47
47
|
end
|
48
48
|
|
49
|
-
# Decrypts
|
49
|
+
# Decrypts an +encrypted_text+ and returns the result as clean text
|
50
50
|
#
|
51
51
|
# === Options
|
52
52
|
#
|
data/lib/active_record/enum.rb
CHANGED
@@ -167,6 +167,15 @@ module ActiveRecord
|
|
167
167
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
168
168
|
end
|
169
169
|
|
170
|
+
def load_schema! # :nodoc:
|
171
|
+
defined_enums.each_key do |name|
|
172
|
+
unless columns_hash.key?(resolve_attribute_name(name))
|
173
|
+
raise "Unknown enum attribute '#{name}' for #{self.name}. Enums must be" \
|
174
|
+
" backed by a database column."
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
170
179
|
class EnumType < Type::Value # :nodoc:
|
171
180
|
delegate :type, to: :subtype
|
172
181
|
|
@@ -240,6 +249,7 @@ module ActiveRecord
|
|
240
249
|
|
241
250
|
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
242
251
|
assert_valid_enum_definition_values(values)
|
252
|
+
assert_valid_enum_options(options)
|
243
253
|
# statuses = { }
|
244
254
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
245
255
|
name = name.to_s
|
@@ -254,13 +264,7 @@ module ActiveRecord
|
|
254
264
|
|
255
265
|
attribute(name, **options)
|
256
266
|
|
257
|
-
decorate_attributes([name]) do |
|
258
|
-
if subtype == ActiveModel::Type.default_value
|
259
|
-
raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
|
260
|
-
" backed by a database column or declared with an explicit type" \
|
261
|
-
" via `attribute`."
|
262
|
-
end
|
263
|
-
|
267
|
+
decorate_attributes([name]) do |name, subtype|
|
264
268
|
subtype = subtype.subtype if EnumType === subtype
|
265
269
|
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
266
270
|
end
|
@@ -370,6 +374,13 @@ module ActiveRecord
|
|
370
374
|
end
|
371
375
|
end
|
372
376
|
|
377
|
+
def assert_valid_enum_options(options)
|
378
|
+
invalid_keys = options.keys & %i[_prefix _suffix _scopes _default _instance_methods]
|
379
|
+
unless invalid_keys.empty?
|
380
|
+
raise ArgumentError, "invalid option(s): #{invalid_keys.map(&:inspect).join(", ")}. Valid options are: :prefix, :suffix, :scopes, :default, :instance_methods, and :validate."
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
373
384
|
ENUM_CONFLICT_MESSAGE = \
|
374
385
|
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
375
386
|
"this will generate a %{type} method \"%{method}\", which is already defined " \
|
@@ -531,7 +531,9 @@ module ActiveRecord
|
|
531
531
|
initialize_find_by_cache
|
532
532
|
end
|
533
533
|
|
534
|
-
|
534
|
+
# Load the model's schema information either from the schema cache
|
535
|
+
# or directly from the database.
|
536
|
+
def load_schema
|
535
537
|
return if schema_loaded?
|
536
538
|
@load_schema_monitor.synchronize do
|
537
539
|
return if schema_loaded?
|
@@ -592,6 +594,8 @@ module ActiveRecord
|
|
592
594
|
columns_hash = schema_cache.columns_hash(table_name)
|
593
595
|
columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
|
594
596
|
@columns_hash = columns_hash.freeze
|
597
|
+
|
598
|
+
super
|
595
599
|
end
|
596
600
|
|
597
601
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
@@ -428,15 +428,19 @@ module ActiveRecord
|
|
428
428
|
# a new association object. Use +build_association+ or +create_association+
|
429
429
|
# instead. This allows plugins to hook into association object creation.
|
430
430
|
def klass
|
431
|
-
@klass ||=
|
431
|
+
@klass ||= _klass(class_name)
|
432
432
|
end
|
433
433
|
|
434
|
-
def
|
435
|
-
name.
|
434
|
+
def _klass(class_name) # :nodoc:
|
435
|
+
if active_record.name.demodulize == class_name
|
436
|
+
return compute_class("::#{class_name}") rescue NameError
|
437
|
+
end
|
438
|
+
|
439
|
+
compute_class(class_name)
|
436
440
|
end
|
437
441
|
|
438
|
-
def
|
439
|
-
|
442
|
+
def compute_class(name)
|
443
|
+
name.constantize
|
440
444
|
end
|
441
445
|
|
442
446
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
@@ -986,7 +990,7 @@ module ActiveRecord
|
|
986
990
|
end
|
987
991
|
|
988
992
|
def klass
|
989
|
-
@klass ||= delegate_reflection.
|
993
|
+
@klass ||= delegate_reflection._klass(class_name)
|
990
994
|
end
|
991
995
|
|
992
996
|
# Returns the source of the through reflection. It checks both a singularized
|
@@ -241,14 +241,14 @@ module ActiveRecord
|
|
241
241
|
raise ArgumentError, ":order must be :asc or :desc or an array consisting of :asc or :desc, got #{order.inspect}"
|
242
242
|
end
|
243
243
|
|
244
|
-
unless block
|
245
|
-
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self, order: order, use_ranges: use_ranges)
|
246
|
-
end
|
247
|
-
|
248
244
|
if arel.orders.present?
|
249
245
|
act_on_ignored_order(error_on_ignore)
|
250
246
|
end
|
251
247
|
|
248
|
+
unless block
|
249
|
+
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self, order: order, use_ranges: use_ranges)
|
250
|
+
end
|
251
|
+
|
252
252
|
batch_limit = of
|
253
253
|
|
254
254
|
if limit_value
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.2.
|
4
|
+
version: 7.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 7.2.
|
19
|
+
version: 7.2.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 7.2.
|
26
|
+
version: 7.2.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 7.2.
|
33
|
+
version: 7.2.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 7.2.
|
40
|
+
version: 7.2.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: timeout
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -476,10 +476,10 @@ licenses:
|
|
476
476
|
- MIT
|
477
477
|
metadata:
|
478
478
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
479
|
-
changelog_uri: https://github.com/rails/rails/blob/v7.2.
|
480
|
-
documentation_uri: https://api.rubyonrails.org/v7.2.
|
479
|
+
changelog_uri: https://github.com/rails/rails/blob/v7.2.1/activerecord/CHANGELOG.md
|
480
|
+
documentation_uri: https://api.rubyonrails.org/v7.2.1/
|
481
481
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
482
|
-
source_code_uri: https://github.com/rails/rails/tree/v7.2.
|
482
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.2.1/activerecord
|
483
483
|
rubygems_mfa_required: 'true'
|
484
484
|
post_install_message:
|
485
485
|
rdoc_options:
|