Skip to content

Single-Table Inheritance with attribute on sub-class, raises after becomes sub-class #47538

Description

@jrochkind

Steps to reproduce

  • When using ActiveRecord Single-Table Inheritance, where a sub-class has declared a "virtual" attribute with ActiveModel attribute method.
  • And using ActiveRecord becomes to change a superclass instance to a subclass instance
  • You can not read or write the virtual attribute in the sub-class, after "becomes"

This is an isolated reproduction of an issue that was affecting me in more complex use of Rails attributes API for implementing json-backed attributes, at jrochkind/attr_json#189

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", ">= 7.0.4.2"
  gem "sqlite3"
end

require "active_record"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :vehicles, force: true do |t|
    t.string :type
    t.string :name
  end
end

class Vehicle < ActiveRecord::Base
end

class Car < Vehicle
  # virtual attribute
  attribute :virtual_string, :string
end

class BugTest < Minitest::Test
  # without `becomes`, works fine
  def test_virtual_attribute
    car = Car.create!
    car.virtual_string = "my value"
    assert_equal car.virtual_string, "my value"
  end

  # FAILS
  def test_virtual_attribute_after_becomes
    became_car = Vehicle.new.becomes(Car)

    became_car.virtual_string = "my value"
    # RAISES
    # ActiveModel::MissingAttributeError: can't write unknown attribute `virtual_string`

    assert_equal car.virtual_string, "my value"
  end
end

Expected behavior

I expect became_car.virtual_string = "my value" to properly set the attribute value.

Actual behavior

Raises: ActiveModel::MissingAttributeError: can't write unknown attribute virtual_string

System configuration

Rails version: 7.0.4.2

Ruby version: 3.2.0

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions