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/]

« Previous Post

avatar

Chris Blunt

Plymouth Software

Devon, England

twitter.com/cblunt

github.com/cblunt

 

facebook.com/cbluntuk

flickr.com/photos/cblunt