Implementing Abstract Base Model Class, the Rails Way™
Solution 1
By declaring a model as abstract you are actually saying that there's no underlying table and you want to allow subclassing. That means:
- You don't need the
downloadable_resources
table - Book.table_name prints
books
instead ofdownloadable_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.
Solution 2
There shouldn't be a downloadable_resources
table in this case. Both your books and downloads tables should declare all of the fields they need.
Marius Butuc
Ruby & Rails aficionado keen on big data, cloud computing, usable web & photography.
Updated on June 05, 2022Comments
-
Marius Butuc about 2 years
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?
-
-
Marius Butuc over 11 yearsWhy? What makes this case unsuited for inheritance? And how to decide when it's worth inheriting and when it's not?