Tuesday, February 17, 2009

Override attribute accessor and before_type_cast

Our Episodic Video Publishing Web Application is written in Ruby on Rails. I recently came across a situation where I had a model where I wanted to override it's attribute accessors in some cases. Consider a model that looks like this.

create_table "podcasts", :force => true do |t|
t.string "name"
t.boolean "use_show_name", :default => true
t.integer "show_id"

As you can see above that each podcast belongs to a show. I also have a form for this model and in the name field I want to display the name of the podcast unless "use_show_name" is true then I want to display the name of the show that this podcast belongs to.

<%= text_field :podcast, :name, :class => "text", :disabled => @podcast.use_show_name %>

I don't want to put this logic in the ERB template but instead I want to put it in the model so that anyone that calls Podcast.name will get the appropriate value depending on the "use_show_name".

class Podcast < ActiveRecord::Base
belongs_to :show
def name
return self.use_show_name ? self.show.name : read_attribute(:name)
alias_method :name_before_type_cast, :name

So there a few things going on above.

  • We override the "name" method by simply implementing it. This works because ActiveRecord using method_missing to implement it's "name" getter but since there is now a "name" method, method_missing will not be invoked.

  • In the method we check "use_show_name" to see what value to return.  If it is true we get the value from the show object.  Otherwise, we call read_attribute to get the value of the name attribute that stored in the DB.

  • Lastly, we alias the name method as "name_before_type_cast" since this is the method that is called by FormHelper.text_field.  ActiveRecord form helpers call the before_type_cast so that they can get the string value of the attribute but we want it to get the value from our "name" method.

1 comment: