Logo

Ruby Glazed

  • Archive
  • RSS
  • Ask me anything

Sugar glazed tips and random thoughts about software, startups and life.

Activerecord Plurals - A shot at the rails source code

In a nutshell:

gem install activerecord_plurals

Usage: https://github.com/gurdotan/activerecord_plurals

In detail:

I find myself many times in need of fetching all IDs or all names of an ActiveRecord query result.  To generalize, I’d like to get all [attribute]s of a certain ActiveRecord model.  Instead of this:

>> Beer.select(:id).map{&id}
=> [1, 2, 3, 4, 5]

I’d like to do something more semantic:

>> Beer.ids
=> [1, 2, 3, 4, 5]
>> Beer.names
=> ["Miller", "Coors", "Heineken", "Tuborg", "Guinness"]

There comes a time in every Rails programmer’s life when he \ she have to open the Rails source code and start digging in the mud. Since I found myself repeating the same pattern of ActiveRecord queries followed by map() calls over and over again, I figured this was my queue. ActiveRecord uses a lot of meta-programming techniques.  Time to code!


# activerecord_plurals.rb
#
class << ActiveRecord::Base

  alias_method :singular_method_missing, :method_missing

  def method_missing(method_id, *args, &block)
    singular_method_id = method_id.to_s.singularize.to_sym
    if method_id == singular_method_id
      singular_method_missing(method_id, *args, &block)
    else
      self.select(singular_method_id).map(&singular_method_id)
    end
  end

end

What’s going on here?  First, we’re reopening the ActiveRecord::Base meta-class (also called an eigenclass by some books) with the class << syntax.  Then we provide a new implementation for method_missing along with an around alias.  This is necessary since ActiveRecord::Base already implements method_missing and we want to wrap the original implementation.  The new implementation’s logic just checks if the invoked method is in singular or plural form.  In case of a singular form, we delegate the method call to the singular_method_missing which is a reference to ActiveRecord’s original implementation.  Otherwise we assume a plural method invocation - we select the requested field and map the query result into an array. 

This is not enough though.  method_missing should always be implemented with respond_to?.  If you can call it, you should be able to recognize it! Add the following to the above class:


  alias_method :singular_respond_to?, :respond_to?

  def respond_to?(*args)
    return true if singular_respond_to?(*args)
    singular_method_id = args[0].to_s.singularize.to_sym
    instance_methods.include?(singular_method_id)
  end

All of the meta-programming techniques demonstrated in this post - Ghost methods (method_missing), method aliases(alias_method), and meta classes - are also heavily used in ActiveRecord’s source code, so I highly recommend you to have a look. If you’re completely new to Ruby meta-programming concepts you can read more here.

The full source code is available on Github.  Or, you can just install it as a gem

gem install activerecord_plurals

or as a rails plugin

rails plugin install git://github.com/gurdotan/activerecord_plurals.git

This is my first gem so any feedback, good or bad, is very welcome :-)

    • #ruby meta-programming,
    • #activerecord
    • #active record
    • #ruby meta programming
    • #rails plugins
  • 1 year ago
  • 1
  • Comments
  • Permalink
  • Share
    Tweet

About me [Gur Dotan]

Avatar Software engineer, adrenaline junkie, husband, beer-aholic, Tel-Aviv dweller. When I'm not hacking away, you can find me jamming on my guitar, anxiously waiting for the next surf \ snow session. Previously a Konteran and Caprizioner, I am now a proud co-founder and chief code brewer of SOOMLA. SOOMLA is the complete virtual economy solution for mobile game devs. We enable automatic creation and management of In-App Purchase Stores. Our code is open source and available at http://project.soom.la.

Twitter

loading tweets…

I Dig These Posts

See more →
  • RSS
  • Random
  • Archive
  • Ask me anything
  • Mobile

Effector Theme by Carlo Franco.

Powered by Tumblr