map_by_method now works with ActiveRecord associations

Posted by Dr Nic on August 12, 2007

I was always annoyed that map_by_method was broken for ActiveRecord has_many associations. 6 mths later I finally fixed it.

That’s the magic of Open Source Software. [/end sarcasm]

So now, the following example works like it should:

$ gem install map_by_method
$ console
> require 'map_by_method'  # stick this in your environment.rb for Rails
> user = User.find_by_name "Dr Nic"
> user.companies.map_by_name
=> ['Dr Nic Academy', 'Dr Nic Institute of Being Silly']
> user.companies.map_by_id_and_name
=> [[1, 'Dr Nic Academy'], [9, 'Dr Nic Institute of Being Silly']]

Recap: why use map_by_method?

Try the following example:

> user.companies.map_by_employees.flatten
=> list of all employees of user

Versus:

> user.companies.map { |company| company.employees}.flatten
or
> user.companies.map(&:employees).flatten

Or compare:

> user.companies.map_by_id_and_name
=> [[1, 'Dr Nic Academy'], [9, 'Dr Nic Institute of Being Silly']]

Versus:

> user.companies.map { |company| [company.id, company.name]}

That is, it looks and feels just like ActiveRecord’s #find method, with its find_by_first_name_and_last_name magic.

Summary

No {, }, |, &, or : required. Just clean method names.

Bonus other gem

In the spirit of ActiveRecord hacks, there is to_activerecord:

$ gem install to_activerecord
$ console
> require 'to_activerecord'  # stick this in your environment.rb for Rails
> [1,2,3].to_user
=> [list of User with id's 1,2,3]

To me, this suffix operator reads cleaner than the traditional:

> User.find([1,2,3])

For example, if you want to perform an operation on the list of Users:

> ids = [1,2,3]
> ids.to_user.map_by_name
=> ['Dr Nic', 'Banjo', 'Nancy']

Versus:

> User.find(ids).map_by_name
Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. pangel Sun, 12 Aug 2007 10:57:15 UTC

    I am not convinced !

    user.companies.map(&:employees).flatten
    

    versus

    user.companies.map_by_employees.flatten
    

    I do not see such a difference compared to the method_missing time we lose here.

  2. Dr Nic Sun, 12 Aug 2007 11:07:56 UTC

    [start jedi mind trick] That’s not the example you are looking for[/end trick]

    I agree that example isn’t the best, so how about this one:

    user.companies.map_by_id_and_name_and_employees
    

    The Symbol.to_proc cannot be used here, so you are left with:

    user.companies.map {|company| [company.id, company.name, company.employees}
    
  3. Stephen Bartholomew Sun, 12 Aug 2007 11:22:43 UTC

    I’d also argue that:

    >> user.companies.map_by_employees.flatten
    

    is more readable and concise than:

    >> user.companies.map(&:employees).flatten
    

    Readability is higher in book any day.

    Great work once again Dr.Nic!

  4. dies-el Sun, 12 Aug 2007 11:39:46 UTC

    ‘map_by_id_and_name’ looks made for the select form helper. One less model-method to hand make!

    Nice one!

  5. bryanl Sun, 12 Aug 2007 16:23:10 UTC

    good stuff. i was looking for magic like this just the other day.

  6. chris Sun, 12 Aug 2007 20:04:42 UTC

    Great stuff, as always. Just wanted to point out that “map_by_method” link in the first line of your post, is broken.

  7. Dr Nic Sun, 12 Aug 2007 20:10:23 UTC

    @chris [via] - ok, I’ve fixed the link, but just to make sure, can you click the links harder next time?

  8. Vann Mon, 13 Aug 2007 09:41:25 UTC

    Nic,

    Is there something going on with map_by_method and ActionController For some reason it looks like any of my RESTful actions are failing; even if I don’t require ‘map_by_method’ in the environment. I get something like :

    NoMethodError (undefined method `to_str’ for [:action, "index"]:Array):
    /usr/local/lib/ruby/gems/1.8/gems/map_by_method-0.7.0/lib/map_by_method.rb:12:in `method_missing’
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/layout.rb:241:in `to_s’
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/layout.rb:241:in `render_without_benchmark’
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/benchmarking.rb:50:in `render’

    Or does it somehow extend Array that ActionController doesn’t like? As great as it would’ve been to use it, I had to uninstall 0.7.0, sorry… let me know if you want to see of the rest of the log….

  9. Dr Nic Mon, 13 Aug 2007 10:09:15 UTC

    @Vann [via] - I noticed a similar problem around the active_record_store.rb just a little while ago. I’ll post an update when I fix it. My initial thought is that its the added #respond_to? method in 0.7. Anyway, still investigating.

    So much magic in so many places.

  10. Dr Nic Mon, 13 Aug 2007 10:10:44 UTC

    @Vann [via] - as for “even if I don’t require ‘map_by_method’” - you might have this in your .irbrc file perhaps.

  11. Vann Mon, 13 Aug 2007 16:25:52 UTC

    @dr nic …I actually do have it in my .irbrc file, I’ll remove it from there and let you know if anything is fixed. But does it matter unless you run anything in the console?

  12. Morning Brew #60 Mon, 13 Aug 2007 17:48:03 UTC

    [...] map_by_method now works with ActiveRecord associations. [...]

  13. renek Thu, 16 Aug 2007 11:06:36 UTC

    @Dr Nic [via]
    -
    Great module Dr Nic!

    I only had some trouble with the regex for the ‘respond_to?’ method.
    The regex in map_by_method_regex is:

    /(map|collect|select|each|reject)(_by)?_([\w\_]+\??)/

    which does (unfortunately) also match ‘each_pair’. This caused some trouble in my app since an array responds to a method that belong to hashes. Removing the ‘?’ after ‘(_by)’ in the regex works for me.

  14. Bug Sun, 19 Aug 2007 06:13:38 UTC

    @Dr Nic [via] - Yeah, ok, but only in Rails does this make a difference. It seems pretty specific to the Rails domain of homogeneous arrays of model objects, readable-as-English code, and the need for blazing-fast development.

    With those as givens, of course, this is pretty neat.

  15. btucker Thu, 23 Aug 2007 21:18:52 UTC

    Any progress on the issue reported by Vann above? Ran into this too, and had to downgrade back to 0.6.0.

  16. mike Sat, 01 Sep 2007 17:14:04 UTC

    Symbol#to_proc can also be used with all the enumerable methods that take blocks, like sort_by and select.

  17. Dr Nic Sun, 02 Sep 2007 07:39:02 UTC

    @mike [via] - true, I need to explicitly add them into the map_by_method code for them to work.

  18. Dr Nic Sun, 02 Sep 2007 07:39:41 UTC

    Regarding the 0.7.0 issue with Rails, its something to do with the respond_to? method I added. I’m still to investigate.

  19. Dr Nic Thu, 06 Sep 2007 10:11:29 UTC

    All known issues are resolved in 0.8.1. Thxs to Mislav Marohnić for hunting down some of the issues!

  20. Dr Nic Thu, 06 Sep 2007 10:13:27 UTC

    Except the each_pair issue. I’ll investigate this later.

  21. Dr Nic Fri, 07 Sep 2007 11:08:42 UTC

    map_by_method 0.8.2 should fix the each_pair issue

  22. [...] map_by_method now works with ActiveRecord associations [...]

Comments