Skip to content

Commit 8c7754d

Browse files
authored
Merge pull request rails#52703 from jonathanhefner/fix-52607
Resolve model attribute types on schema load
2 parents 10924d3 + 49a65a8 commit 8c7754d

File tree

4 files changed

+71
-18
lines changed

4 files changed

+71
-18
lines changed

activerecord/lib/active_record/enum.rb

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,6 @@ def self.extended(base) # :nodoc:
167167
base.class_attribute(:defined_enums, instance_writer: false, default: {})
168168
end
169169

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-
179170
class EnumType < Type::Value # :nodoc:
180171
delegate :type, to: :subtype
181172

@@ -264,7 +255,13 @@ def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods
264255

265256
attribute(name, **options)
266257

267-
decorate_attributes([name]) do |name, subtype|
258+
decorate_attributes([name]) do |_name, subtype|
259+
if subtype == ActiveModel::Type.default_value
260+
raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
261+
" backed by a database column or declared with an explicit type" \
262+
" via `attribute`."
263+
end
264+
268265
subtype = subtype.subtype if EnumType === subtype
269266
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
270267
end

activerecord/lib/active_record/model_schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ def load_schema!
595595
columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
596596
@columns_hash = columns_hash.freeze
597597

598-
super
598+
_default_attributes # Precompute to cache DB-dependent attribute types
599599
end
600600

601601
# Guesses the table name, but does not decorate it with prefix and suffix information.

activerecord/test/cases/enum_test.rb

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,15 +1157,28 @@ def self.name
11571157
end
11581158

11591159
test "raises for attributes with undeclared type" do
1160-
klass = Class.new(Book) do
1161-
def self.name; "Book"; end
1162-
enum :typeless_genre, [:adventure, :comic]
1160+
klass = assert_deprecated(ActiveRecord.deprecator) do
1161+
Class.new(Book) do
1162+
def self.name; "Book"; end
1163+
enum typeless_genre: [:adventure, :comic]
1164+
end
11631165
end
11641166

11651167
error = assert_raises(RuntimeError) do
1166-
klass.type_for_attribute(:typeless_genre) # load schema
1168+
klass.type_for_attribute(:typeless_genre)
1169+
end
1170+
assert_match "Undeclared attribute type for enum 'typeless_genre' in Book", error.message
1171+
end
1172+
1173+
test "supports attributes declared with a explicit type" do
1174+
klass = assert_deprecated(ActiveRecord.deprecator) do
1175+
Class.new(Book) do
1176+
attribute :my_genre, :integer
1177+
enum my_genre: [:adventure, :comic]
1178+
end
11671179
end
1168-
assert_match "Unknown enum attribute 'typeless_genre'", error.message
1180+
1181+
assert_equal :integer, klass.type_for_attribute(:my_genre).type
11691182
end
11701183

11711184
test "default methods can be disabled by :_instance_methods" do

railties/test/application/test_test.rb

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,54 @@ class UserTest < ActiveSupport::TestCase
335335

336336
app_file "app/models/user.rb", <<-RUBY
337337
class User < ApplicationRecord
338-
enum :type, [:admin, :user]
338+
def self.load_schema!
339+
super
340+
raise "SCHEMA LOADED!"
341+
end
342+
end
343+
RUBY
344+
345+
assert_unsuccessful_run "models/user_test.rb", "SCHEMA LOADED!"
346+
end
347+
348+
test "database-dependent attribute types are resolved when parallel tests are run in eager load context" do
349+
use_postgresql
350+
rails "db:drop", "db:create"
351+
352+
output = rails("generate", "model", "user")
353+
version = output.match(/(\d+)_create_users\.rb/)[1]
354+
355+
app_file "db/schema.rb", <<~RUBY
356+
ActiveRecord::Schema.define(version: #{version}) do
357+
create_enum "user_favorite_color", ["red", "green", "blue"]
358+
359+
create_table :users do |t|
360+
t.enum :favorite_color, enum_type: :user_favorite_color
361+
end
362+
end
363+
RUBY
364+
365+
app_file "config/initializers/enable_eager_load.rb", <<~RUBY
366+
Rails.application.config.eager_load = true
367+
RUBY
368+
369+
app_file "test/models/user_test.rb", <<~RUBY
370+
require "test_helper"
371+
class UserTest < ActiveSupport::TestCase
372+
ENV.delete("PARALLEL_WORKERS")
373+
parallelize threshold: 1, workers: 2
374+
375+
2.times do |i|
376+
test "favorite_color uses database type (worker \#{i})" do
377+
assert_instance_of ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Enum, User.type_for_attribute("favorite_color")
378+
end
379+
end
339380
end
340381
RUBY
341382

342-
assert_unsuccessful_run "models/user_test.rb", "Unknown enum attribute 'type' for User"
383+
assert_successful_test_run "models/user_test.rb"
384+
ensure
385+
rails "db:drop" rescue nil
343386
end
344387

345388
private

0 commit comments

Comments
 (0)