rails override default getter for a relationship (belongs_to)

brad picture brad · Mar 23, 2010 · Viewed 17.4k times · Source

So I know how to override the default getters for attributes of an ActiveRecord object using

def custom_getter
  return self[:custom_getter] || some_default_value
end

I'm trying to achieve the same thing however for a belongs to association. For instance.

class Foo < AR
  belongs_to :bar

  def bar
    return self[:bar] || Bar.last
  end
end

class Bar < AR
  has_one :foo
end

When I say:

f = Foo.last

I'd like to have the method f.bar return the last Bar, rather than nil if that association doesn't exist yet.

This doesn't work however. The reason is that self[:bar] is always undefined. It's actually self[:bar_id].

I can do something naive like:

def bar
  if self[:bar_id]
    return Bar.find(self[:bar_id])
  else
    return Bar.last
  end
end

However this will always make a db call, even if Bar has already been fetched, which is certainly not ideal.

Does anyone have an insight as to how I might have a relationship such that the belongs_to attribute is only loaded once and has a default value if not set.

Answer

Randy Simon picture Randy Simon · Mar 23, 2010

alias_method is your friend here.

alias_method :original_bar, :bar
def bar
  self.original_bar || Bar.last
end

The way this works is that you alias the default "bar" method as "original bar" and then implement your own version of "bar". If the call to original_bar returns nil then you return the last Bar instance instead.