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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ec33405aa24cc15f0f8a4092bc3a04acafc8191a4df5b41896ce648a5937687
4
- data.tar.gz: 89efd888d4ed72e2d3ff6e75297c1dc4d0dcb19c15ed32c39fd53a2947a51b0b
3
+ metadata.gz: 4b72f5d48ed3098cb5d20e0f33ed2b082cccdb963338accb19f89c982a790ff1
4
+ data.tar.gz: 313c950f13b265ab63f2b5882f8fe9b20642e4f8702f1862f55abd030862a450
5
5
  SHA512:
6
- metadata.gz: 84757d9eab7d9709fcd86d0e39d111f6fabf6dc09f15167c432c1ce097d87524e8839be9843eaf7b0be0acc53f92c92dafdae3e21df3c996a34eb01ca6f5703c
7
- data.tar.gz: 83c46935ce453cec3e536ba27caa6b76fd5cdf59d8ca762e6eba91d2137a616d3a1e01cbfe049c945363130e55998e2afde0a8ff1d975ee2359ad38b560980ed
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.
@@ -61,7 +61,7 @@ module ActiveRecord
61
61
  when Hash
62
62
  associations.each do |k, v|
63
63
  cache = hash[k] ||= {}
64
- walk_tree v, cache
64
+ walk_tree v, cache if v
65
65
  end
66
66
  else
67
67
  raise ConfigurationError, associations.inspect
@@ -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. See the examples below for more
29
- # information about providing custom type objects.
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 previous default value (if any) will be used.
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 = WeakKeyMap.new
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 = WeakKeyMap.new
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
- @thread_query_caches = Concurrent::Map.new(initial_capacity: @size)
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
- @thread_query_caches.each_value(&:clear)
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
@@ -161,7 +161,7 @@ module ActiveRecord
161
161
  end
162
162
 
163
163
  def valid_primary_key_options
164
- super + [:unsigned]
164
+ super + [:unsigned, :auto_increment]
165
165
  end
166
166
 
167
167
  def create_table_definition(name, **options)
@@ -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}.lease_connection' to establish a connection)"
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})"
@@ -123,7 +123,7 @@ module ActiveRecord
123
123
  end)
124
124
  end
125
125
 
126
- def load_schema!
126
+ def load_schema! # :nodoc:
127
127
  super
128
128
 
129
129
  add_length_validation_for_encrypted_columns if ActiveRecord::Encryption.config.validate_column_size
@@ -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 < ::ActiveRecord::Type::Text
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 a +clean_text+ and returns the result as clean text
49
+ # Decrypts an +encrypted_text+ and returns the result as clean text
50
50
  #
51
51
  # === Options
52
52
  #
@@ -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 |_name, subtype|
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 " \
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 2
12
- TINY = 0
12
+ TINY = 1
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -531,7 +531,9 @@ module ActiveRecord
531
531
  initialize_find_by_cache
532
532
  end
533
533
 
534
- def load_schema # :nodoc:
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 ||= compute_class(compute_name(class_name))
431
+ @klass ||= _klass(class_name)
432
432
  end
433
433
 
434
- def compute_class(name)
435
- name.constantize
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 compute_name(name) # :nodoc:
439
- active_record.name.demodulize == name ? "::#{name}" : name
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.compute_class(compute_name(class_name))
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
@@ -190,6 +190,7 @@ module ActiveRecord
190
190
 
191
191
  private
192
192
  def singleton_method_added(name)
193
+ super
193
194
  generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
194
195
  end
195
196
  end
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.0
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-09 00:00:00.000000000 Z
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.0
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.0
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.0
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.0
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.0/activerecord/CHANGELOG.md
480
- documentation_uri: https://api.rubyonrails.org/v7.2.0/
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.0/activerecord
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: