Super easy collect_* extenstion for ActiveRecord arrays

Apologies if this was already posted, I sent this over the weekend via Google groups and it never showed up. So I'm trying again. :slight_smile:

A while back I had an idea for extending Array#collect to work like the ActiveRecord find_by_* method_missing method. I put it on the back burner until I was showing a co-worker a bit about Ruby on Rails and realized how convenient such a feature could be. As we all know Ruby is such a fantastic language that it makes these sorts of things easy, and sure enough it was a simple little hack. I thought I'd post it here in case others might find it useful.

Here's how collect normally works:

locations = Location.find(:all)

just_the_names_and_ids = locations.collect{|loc| [loc.id, loc.name]} => [[1, "New York Zone"], [2, "Los Angeles Zone"], [4, "Boston Zone"], [5, "San Francisco Zone"], [6, "Development Center"]]

Here it is with the new collect_* feature:

just_the_names_and_ids = locations.collect_id_and_name => [[1, "New York Zone"], [2, "Los Angeles Zone"], [4, "Boston Zone"], [5, "San Francisco Zone"], [6, "Development Center"]]

just_the_names = locations.collect_name => [["New York Zone"], ["Los Angeles Zone"], ["Boston Zone"], ["San Francisco Zone"], ["Development Center"]]

names_and_cities = locations.collect_name_and_city => [["New York Zone", "New York"], ["Los Angeles Zone", "Los Angeles"], ["Boston Zone", "Boston"], ["San Francisco Zone", "San Francisco"], ["Development Center", "Chicago"]]

Just like the find_by_* methods you can use any attribute name _and_ any additional attribute names instead of {|array| [array.x, array.y, array.z]} etc. Of course the original way still works too.

It's not limited to one _and_, so you can do as many as you want:

names_cities_and_ids = locations.collect_id_and_name_and_city

Or even:

even_more = locations.collect_id_and_name_and_city_and_state_and_zip_code_and_relative_humidity_and_enough_already

You get the idea. :slight_smile:

Caveats are that it won't work with included associations, you'll need to resort to a block for that. There's nothing technically difficult about adding that feature but I couldn't think of a clean syntax for allowing it. Any suggestions are welcome.

To add this functionality just paste the code below into environment.rb (or include it from a .rb file in /lib). This could also be made into a plugin. I'll make one if anyone is interested.

class Array def method_missing(method_id, *arguments)    if match = /collect_([_a-zA-Z]\w*)/.match(method_id.to_s)      attributes = match.captures.last.split('_and_')      self.collect{|array| attributes.collect{|attr| array[attr.to_sym]}}    else      super    end end end

Full back story with other examples are at http://dashing.com/blog/2006/12/09/super_easy_collect_for_activerecord_arrays

It also works with Arrays of hashes too.

I hope someone finds this useful.

Cheers, John

zdennis wrote:

I like this alot. Any objections if I include this with my ActiveRecord::Extensions plugin?

Sure, be my guest.

Cheers, John