Friday, July 24, 2009

Initializing i-vars in an ActiveRecord model

I thought it would be simple--and, in the end, it was--but I had to comb The Web and then do some testing before I understood how to do it. The problem is simple: I want to initialize some of the i-vars of an ActiveRecord model whenever an instance is created.

There are two ways to do this, as I now understand it. If all you want is to add some initialization when an object is created you can do so by overriding the initialize method. This is what one would first imagine, but you must be aware that ActiveRecord::Base defines a particular signature for initialize requiring a hash, and you must be sure to invoke this as part of your override, e.g:

class Note < ActiveRecord::Base
def initialize(options={})
super(options)
self.title ||= ""
self.content ||= ""
self.date ||= Date.today
end

def to_s
"#{self.title}, #{self.date}, #{self.content}"
end
end


Now if you start a rails console (ruby script/console) you can type Notes.new and you will see that the title and content are "" and the date is today's date. Without this initialization, all three i-vars would be nil.

Something to keep in mind is that Note.initialize will NOT be called for objects read from the database. At least, that is my understanding from reading comments on The Web. I haven't actually tried it to be sure.

What this means is that the overriding of initialize will NOT do anything for objects read directly from the database. This may or may not be what you want. It is not what I want, however--I want all in-memory Note objects to be well formed, regardless of whether they hold newly created data or data read from the database. Therefore, I can't use the override hack. I have to use something else, and that something is the after_initialize hook provided by ActiveRecord, as illustrated below:

class Note < ActiveRecord::Base
after_initialize :after_initialize

def after_initialize
self.title ||= ""
self.content ||= ""
self.date ||= Date.today
end

def to_s
"#{self.title}, #{self.date}, #{self.content}"
end
end

According to the documentation, after_initialize is guaranteed to be called when Note.new is called (truly after initialize) or when Note.find instantiates an object from the database.

To see this in action (as it were), please see how I use the Note class in my little Family Planner application at www.github.com/dgoldhirsch/fpl.

No comments:

Post a Comment