The wonderful magic of the association proxy
April 16th, 2007
UPDATE: This post here does a much better job at explaining how cool the association proxy is. Check it out.
I stumbled onto a feature I didn’t even know Rails had today, simply because it felt like that’s the way it should work. I didn’t even realize the leap of faith until after the test passed and I realized what was going on.
Let’s say you have a model Foo with a class method called get_snazziest. Now let’s say that Bar has_many :foos@
Here is the cool part:
@bar = Bar.new
@bar.foos.get_snazziest
Apparently, when you call a method on an association proxy, Rails looks for class methods in the associated model with the same name. If it finds one, it runs it. But the really cool part is that it will inject the scope into any calls to find.
Lets put this another way. In the app I was just playing with, I have to validate an address by making sure its zip code belongs to it state.
So I had models like:
class State < ActiveRecord::Base
has_many :zips
end
class Zip < ActiveRecord::Base
belongs_to :state
def self.find_by_code(code)
find :first,
:conditions => [
'start <= :code AND end >= :code',
{ :code => code }
]
end
end
So I wanted an elegant way to ask the state if it has a specific zip, without writing any more complex SQL. The solution?
class State < ActiveRecord::Base
has_many :zips
def has_zip?(code)
zips.find_by_code code
end
end
@state = State.find_by_abbrev('CA')
@state.has_zip?('95472') # => true
#SQL => SELECT * FROM zips WHERE (zips.state_id = 1) AND (start <= '95472' AND end >= '95472') LIMIT 1
A search for a zip, scoped by a state, easy as pie.
The great thing about Rails is that the API is intelligent enough that you can start to intuitively understand how things may work. When I wrote that code I did not know it would call a class method on Zip but at the same time I just assumed it would work.
Yay for Rails working the way I expect it to without knowing better!
Leave a Reply