April 2008

Rails by default uses a system-generated primary key called “id” for all persisted objects. I personally like this (for the very reasons Dave Thomas outlines in the Rails book on pp. 286-287), but if you don’t, you can change it. Like Dave, I don’t recommend it, but here’s how if you must.

class Friend < ActiveRecord::Base
self.primary_key = "email"

Now the primary key of the Friend class is “email” and you must set this value before you can save the object. This is one caveat of being able to change the primary_key from the convention (but don’t complain, at least you can do it).

I chose this example to point out why I like using system-generated, non-model-related primary keys. Email address seems at first like a reasonable field to use for the primary key because they must be unique between people* and they don’t change (often). But they do change (for example if someone use’s their ISP for email and they change their ISP). If you use it as the primary key and your best friend moves from Boston to Idaho and gets a new email address, you have to update all of the places that it’s used as a foreign key (and in the case of your best friend, this may be quite a few places).

Stick to the default system-generated ID and you’ll be better off. But at least you can change the default if you must.

Composite Keys

Rails doesn’t support composite keys natively, but there are plugins available that accommodate this, such as Nic Williams CompositeKeys.
* In the case of a married couple who shares the same email address, you would not be able to include both the man and the woman (who might both be your friends) in your contact list.

Have you ever wish you could tell Rails the name of the table that mapped to your model class?  Perhaps you want a model to represent a legacy table (which could be any table that already exists which is either too painful to recreate or over which you have no control).  Or perhaps you just want to be strange and call your class “This” and your table “that” (not something I’d recommend, but if you must satisfy that urge, you can).  Here’s how:

class Customer < ActiveRecord::Base
set_table_name "customp"

Now Rails will look for the table called “customp” when persisting or reading instances of Customer.  You can also use the more direct form:

class Customer < ActiveRecord::Base
self.table_name = "customp"

This tip comes from page 282 of the Rails book.

I have found as I’ve been working on my project that Rails has many very useful little features that may not be used all that often but are sometimes needed. As I come across them I hope to note them here in the hopes that they will help someone (myself included) at some point.

Yesterday I was reading the Agile Web Development with Rails (2nd Edition) book (hereafter AWDwR2E) and came across these useful little pieces of knowledge.

Leveraging ActiveRecord in a non-Rails application

Start by including rubygems and then include the activerecord gem:

require “rubygems”
require_gem “activerecord”

Next, establish your database connection:

ActiveRecord::Base.establish_connection(:adapter => “mysql”, :host => “localhost”, :database => “yourdatabase”)

Then, create a class that extends ActiveRecord::Base so your class will take advantage of all of the ORM plumbing that ActiveRecord provides:

class Friend < ActiveRecord::Base

Note that you should have a table called friends in yourdatabase for this to work. Assuming your friends table has the columns name and email, you could locate a friend named “John Doe” and update his email address with the following lines:

john_doe = Friend.find(“John Doe”)
john_doe.email = “john.doe@gmail.com”

That’s all there is to it, and that’s pretty awesome!  This (ActiveRecord) is one of the really great features of Rails that makes it so easy to write a database-driven application.  Unlike most ORM frameworks, ActiveRecord (and by extension, Rails) relies on convention rather than configuration and thus you don’t have to tell it what table represents your class or what columns in the table map to what properties in your class.  It uses reflection to determine all of this although you can override it’s defaults if you need to do so.