Skip to content

Commit 3d65e25

Browse files
nganclaude
andcommitted
Skip virtual columns when generating cached fixture INSERTs
Models with generated/virtual columns reject INSERTs into those columns ("cannot INSERT into generated column"). The coder was including every column from model.column_names, so any model with a t.virtual column would crash mount. Filter columns by adapter capability + Column#virtual?, mirroring how Rails' build_fixture_sql filters them in connection_adapters/abstract/ database_statements.rb. Adds a computed_widgets table with a stored generated column to the dummy app and a regression spec exercising the round-trip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7c5fcb5 commit 3d65e25

4 files changed

Lines changed: 42 additions & 3 deletions

File tree

lib/fixture_kit/coders/active_record_coder.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,28 @@ def base_table_model(model)
6060

6161
def generate_statements(models)
6262
models.each_with_object({}) do |model, statements|
63-
columns = model.column_names
63+
columns = insertable_columns(model)
64+
column_names = columns.map(&:name)
6465

6566
rows = []
6667
model.unscoped.order(:id).find_each do |record|
67-
row_values = columns.map do |col|
68+
row_values = column_names.map do |col|
6869
value = record.read_attribute_before_type_cast(col)
6970
model.connection.quote(value)
7071
end
7172
rows << "(#{row_values.join(", ")})"
7273
end
7374

74-
sql = rows.empty? ? nil : build_insert_sql(model.table_name, columns, rows, model.connection)
75+
sql = rows.empty? ? nil : build_insert_sql(model.table_name, column_names, rows, model.connection)
7576
statements[model] = sql
7677
end
7778
end
7879

80+
def insertable_columns(model)
81+
supports_virtual = model.connection.supports_virtual_columns?
82+
model.columns.reject { |c| supports_virtual && c.virtual? }
83+
end
84+
7985
def build_delete_sql(connection, table_name)
8086
"DELETE FROM #{connection.quote_table_name(table_name)}"
8187
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# frozen_string_literal: true
2+
3+
class ComputedWidget < ApplicationRecord
4+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
require "spec_helper"
4+
5+
# Models with generated/virtual columns can't accept INSERTs into those
6+
# columns. The coder must filter them out when building cached statements.
7+
RSpec.describe "Fixture round-trip with virtual columns" do
8+
fixture do
9+
ComputedWidget.create!(name: "alpha", quantity: 1)
10+
ComputedWidget.create!(name: "beta", quantity: 2)
11+
end
12+
13+
after { ComputedWidget.delete_all }
14+
15+
it "loads records with their generated column values" do
16+
widgets = ComputedWidget.order(:id).to_a
17+
18+
expect(widgets.map(&:name)).to eq(["alpha", "beta"])
19+
expect(widgets.map(&:name_upper)).to eq(["ALPHA", "BETA"])
20+
end
21+
end

spec/support/dummy_rails_helper.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def setup_databases
3434
ActiveRecord::Base.connection.drop_table(:users, if_exists: true)
3535
ActiveRecord::Base.connection.drop_table(:vehicles, if_exists: true)
3636
ActiveRecord::Base.connection.drop_table(:gadgets, if_exists: true)
37+
ActiveRecord::Base.connection.drop_table(:computed_widgets, if_exists: true)
3738
end
3839

3940
AnalyticsRecord.connection.disable_referential_integrity do
@@ -88,6 +89,13 @@ def setup_databases
8889
t.timestamps
8990
end
9091

92+
ActiveRecord::Base.connection.create_table :computed_widgets, force: true do |t|
93+
t.string :name, null: false
94+
t.integer :quantity, null: false, default: 0
95+
t.virtual :name_upper, type: :string, as: "UPPER(name)", stored: true
96+
t.timestamps
97+
end
98+
9199
# Analytics database schema
92100
AnalyticsRecord.connection.create_table :activity_logs, force: true do |t|
93101
t.integer :external_user_id, null: false

0 commit comments

Comments
 (0)