I created a mixin "Database::Table" that defines the methods "#before_create" and "#before_save" to have the desired behaviour. Now, in your ActiveRecord classes, include this mixin, and you're done. Here's the code.
module Database
# This classes updates the timestamps on the database tables, and forces
# all subclassed tables to have created_at and last_updated_at tables
module Table
def before_create
self.created_at = Time.now
self.last_updated_at = Time.now
end
def before_save
self.last_updated_at = Time.now
end
end
end
And the code that uses it:
require "rubygems"
require "activerecord"
require "database/table"
class User < ActiveRecord::Base
include Database::Table
# code...
end
The second technique is to use tables that have the "id" field to be something other than an integer -- the default type. We use another mixin for this: "Database::UuidBase."
require 'database/table'
module Database
# This class allows subclasses to use a guid/uuid based id
# instead of the sequential id used otherwise
module UuidBase
include Database::Table
def before_create
super
self.id = uuid if self.id.nil?
end
def uuid
# user your favorite library to generate a UUID
end
end
end
You now include this mixin into your class to get both the behaviours. There are several reasons why one may want to do this.
Often there is a unique id for a table that we need to be a string, and not an integer. If it is a part of all or most of your search queries and is unique for every row, you can avoid having an additional index by replacing the default "id" column with this string.
If you use an integer field that gets auto_incremented, and you need access to this field after an insert, you have to typically load the row from the database again. This means you need to do a search, and you cannot search on the primary key -- "id." You therefore have to rely on a secondary index -- assuming you have a secondary index. Howevery, by populating a unique number _before_ it is saved into the database, you have access to the "id" field after the insert without the need to reload it from the database.