Rails: Abstract Models - ActiveRecord Without Tables
I recently hit a situation where I needed an ActiveRecord model with no corresponding database table. The model had to represent a collection of other event-type models - for example: Birthdays, Weddings, Anniversaries, etc.
As an Event model would be a completely abstract representation of another model, it didn't make sense to store them in the database. Unfortunately, ActiveRecord doesn't appear to support this natively. Luckily, I came across this post by Micha? Szajbe which demonstrates ActiveRecord model without a corresponding table. I've used Micha?'s code to build the example model below:
Event-Model Example
First, create a class to represent your abstract model using the code from the post above:
# app/models/event.rb
class Event < ActiveRecord::Base
def self.columns
@columns ||= []
end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
# Manually define the columns used by this model
column :name, :string
column :event_at, :datetime
column :description, :string
column :event_type, :string
end
(Note that I've since found that ActiveRecord doesn't like any column called id)
In my example, I gave each of the real models a to_event method to capture the common attributes and return an Event model:
# app/models/birthday.rb
def to_event
Event.new(
:name => [self.person.name, "'s birthday"].join,
:event_at self.birthday,
:event_type => 'birthday'
)
end
Now we can build a normal RESTful controller with index and show actions to display the Events. Note that because the models are just an abstract view on other models, the CRUD RESTful routes (create, edit, etc.) are redundant.
script/generate rspec_controller events index
In the controller, build an array of events by calling to_event on your other models:
# app/controllers/events_controller.rb
def index
@events = Wedding.find(:all).collect { |w| w.to_event }
@events += Birthdays.find(:all).collect { |b| b.to_event }
end
Finally, add a RESTful route for your events into routes.rb:
# config/routes.rb
map.resources :events
Now, the abstract event models can be accessed using the normal RESTful URL:
http://localhost/events
References
Tableless Models in Rails [Micha? Szajbe, http://codetunes.com/]