Action Association Caching

Hey again guys,

I did come up with a problem few weeks ago and I still don't have an elegant solution for it. I'd like to hear your opinions on how to deal with it, since it's rather basic and could be quite common.

Imagine a view showing peoples clothing. Stefan's hat: stefan.cloths.find_by_location "head" Stefan's shirt: stefan.cloths.find_by_location "shirt" Stefan's jeans: stefan.cloths.find_by_location "jeans" Stefan's left_glove: stefan.cloths.find_by_location "left_glove" Stefan's right_glove: stefan.cloths.find_by_location "right_glove" etc...

quite a few queries. bad performance. the thing is though, before reaching the view the association has been already initialized. so, the rows already exist in the action, and they are retrieved again one-by-one in the view.

to avoid retrieving them again, i build a hash in the action this way: for cloth in cloths; @cached_clothes[cloth.location] = cloth; end and in the view: Stefan's hat: @cached_clothes["head"]

It "works". But does not seem elegant. an other approach i tried: Stefan's hat: stefan.clothes.select{|c| c.location == "head"}.first Doesnt seem to be the correct "rails way" either.

Is there a "rails way" for doing this?

Thank you :wink: Stefan

to avoid retrieving them again, i build a hash in the action this way: for cloth in cloths; @cached_clothes[cloth.location] = cloth; end and in the view: Stefan's hat: @cached_clothes["head"]

You can make this slightly neater by saying @cached_clothes =
stefan.clothes.index_by {|c| c.location}

It "works". But does not seem elegant. an other approach i tried: Stefan's hat: stefan.clothes.select{|c| c.location == "head"}.first Doesnt seem to be the correct "rails way" either.

Seems reasonable enough. If there were a lot of clothes and clothe
locations the hash approach might be more efficient, but until then it
could be over the top. If you wanted the syntax to be cleaner I suppose you could fiddle
around with an association extension, eg

has_many :clothes do    def select_by attribute, value      detect {|c| c.send(attribute) == value}    end end

and then you can do stefan.clothes.select_by :location, 'head' You could even go the whole hod and leverage method_missing to handle
things like clothes.select_by_location 'x'

If you're only ever doing this by location, you can make things a
little shorter

has_many :clothes do    def located_on place      detect {|c| c.location == place}    end end

and then you can write stefan.clothes.located_on 'head'

or apply association extensions to the first technique:

has_many :clothes do    def located_on place      @cache ||= index_by {|c| c.location}      @cache[place]    end end (this may or may not be a concern, but in this case you do need to
worry about @cache getting stale, eg as is reloading the association
won't clear it)

Fred

Thanks again fred.

This association extension thing seems like the piece of the puzzle I was missing.