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