rails / rails

Ruby on Rails

Home Page:https://rubyonrails.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

jrochkind opened this issue · comments

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

Works for me on main. git bisect shows, that it was fixed by #42650, but it was not backported to 7-0-stable.

@jonathanhefner Should the backport be done?

Oh nice, thank you for discovering and linking to that, @fatkodima!

While I would love to see a backport into Rails 7.0, I do not know what the considerations are for that.

Either way, I think this is a dup of #41195, so I'll close it.

Thank you for figuring out what was going on and linking to the relevant code, @fatkodima, that was super helpful!