I have a Book and Download model that share many attributes, so my goal is to inherit the common attributes from a DownloadableResource model.
Had a look at STI, but I went the abstract base model class way instead:
models:
class DownloadableResource < ActiveRecord::Base
self.abstract_class = true
attr_accessible :title, :url, :description, :active, :position
validates :title, :url, :description, presence: true
scope :active, where(active: true).order(:position)
end
class Book < DownloadableResource
attr_accessible :cover_url, :authors
validates :cover_url, :authors, presence: true
end
class Download < DownloadableResource
attr_accessible :icon_url
validates :icon_url, presence: true
end
migrations:
class CreateDownloadableResources < ActiveRecord::Migration
def change
create_table :downloadable_resources do |t|
t.string :title
t.string :url
t.text :description
t.boolean :active, default: false
t.integer :position
t.timestamps
end
end
end
class CreateBooks < ActiveRecord::Migration
def change
create_table :books do |t|
t.string :cover_url
t.string :authors
t.timestamps
end
end
end
class CreateDownloads < ActiveRecord::Migration
def change
create_table :downloads do |t|
t.string :icon_url
t.timestamps
end
end
end
After migration, when I create a new Book the result is far from expected:
> Book.new
=> #<Book id: nil, cover_url: nil, authors: nil, created_at: nil, updated_at: nil>
Can somebody please shed some light on how to implement the Abstract Base Model Class technique so ActiveRecord models can share common code via inheritance yet be persisted to different database tables?
By declaring a model as abstract you are actually saying that there's no underlying table and you want to allow subclassing. That means:
downloadable_resources
tablebooks
instead of downloadable_resources
As @Finbarr already mentioned, this also means that both Book
and Download
models need to have all of the attributes in their tables.
What is this actually useful for then? In my opinion not for much. You can share validations, scopes etc. but you can achieve all of that more easily by including custom modules.
To solve your problem I would probably go with a different approach. I would create another model called DownloadableContent
that would be self contained. It would include validations and the table would have all of the attributes. And finally models Book
and Download
would have a polymorphic has_one
relation to the DownloadableContent
model.
You could go with the STI approach but I generally don't like mixing all of the custom attributes together.