A Beginner Guide to Creating Class Macros

If you are an experienced rubyist or at least have taken time to play with Rails or many other ruby gems, chances are you have already seem code like this:

class Person < ActiveRecord::Base
  has_many :books # <---- that's what i'm talking about!
end

The first time i’ve stumbled upon one of these weird “things”, i’ve figured that somehow the gem had created a new language construct or something alike that scared the hell out of me!

That’s until i’ve finally came to know that when you are declaring a class, you’re actually executing code, not only declaring methods, constants and instance variables. So, after all, code like the one above is simply calling a method from within a class definition.

At first look, it seems that it would be pretty useless to execute code inside a class definition instead of within a method, but when the ruby flexibility and power come to play, you’re really empowered by this possibility as it allows you to create new methods, modify object behavior, redefine instance variables – and much more – dynamically! Let’s see an example:

# I assume that a 'buzzer' function simply puts "Bzzzzz"
class Foo
  define_buzzer :lucas
end
Foo.new.buzz_lucas # -> Bzzzzz

The example above will create a method that is named according to a symbol you give as an argument. If that doesn’t look straightforward to you, let’s try another example:

class Foo
  define_buzzer :lucas, :extra_buzz => "Hey! Hey! Hey!"
end
Foo.new.buzz_lucas # -> Bzzzzz Hey! Hey! Hey!

Now that’s something you might want to use: a dynamically named class that generates dynamically modified code! So instead of writing this:

class Foo
  def send_welcome_mail
    subject = "Welcome E-Mail"
    to = customer.email
    from = "Welcomer <welcomer@foo.bar>"
   send_email(subject,to,from)
  end
  def send_purchase_mail
    subject = "Your purchase has been confirmed!"
    to = customer.email
    from = "Foo Sales <sales@foo.bar>"
    send_email(subject,to,from)
  end
end

You could simply write this and get the exact same functionality, without the messy repetition:

class Foo
  def self.define_mailer(*args)
    define_method "send_#{args[0]}_mail" do
      send_email(args[1][:subject], args[1][:to] , args[1][:from])
    end
  end    
 
  define_mailer :welcome, :subject => "Welcome E-mail", :from => "Welcomer <welcome@foo.bar>", :to => customer.email
  define_mailer :purchase, :subject => "Your purchase has been confirmed!", :from => "Foo Sales <sales@foo.com>", :to => customer.email
end

Actually there’s much more that you can do with this powerful possibilities ruby gives you – which are outside the scope of this little introduction – but let’s start with the basics: how can you start creating your own class macros and get that “how the heck did you do that?” look from your fellow non-rubyist friends (if you are like me, chances are there won’t be much that are outside the ruby club yet :).

Part One: Define class methods to define macros that can be used within a class

When you are inside a class definition, all method calls are directed to the class of the class you are defining (which is Class, and that’s a weird sentence, isn’t it?). When you define methods using the: def self.class_method declaration, you are inserting the method into an intermediary class that is between the class you are defining and it’s class (which is Class, remember?) – some folks call this intermediary classes singleton classes or eigenclasses – so this way whenever you call a method inside a class definition, the interpreter will look for that method firstly in this “intermediary class” and then in the class Class (oh dear..) and up to the parent-of-all-parents Object (or, on ruby 1.9, BasicObject) class.

This means that if you define class methods to a class, you can actually call these methods inside the class definition to get the functionality we’re looking for! Let’s try that:

class Foo
  def self.create_mother_call_for(name)
    define_method("call_#{name}_mother") do
      puts "Hey #{name}'s mother! Come here!"
    end
  end
 
  create_mother_call_for :john
end
Foo.new.call_john_mother # -> Hey john's mother! Come here!

And that’s it, you’ve created your first macro!

Part Two: Let’s broaden the horizon defining those methods inside Modules!

Let’s say you got shared functionality between some classes (you have, don’t you?) and it’s pretty repetitive stuff but not enough to violate the Single Responsibility Principle and to create another class to handle that. It seems like a good time to use our recently-learned class macros to help us there. To use class macros that live outside the class methods, you can use a method called extend, which includes module methods into that “intermediary class” we talked about (and that’s the last time  i’m calling them like that! From now on you’ll hear eigenclass!). Nothing better than an example to show how beautifully you can do that:

module MotherCaller
  def create_mother_call_for(name)
    define_method("call_#{name}_mother") do
      puts "Hey #{name}'s mother! Come here!"
    end
  end
end
 
class Baby
  extend MotherCaller
  create_mother_call_for(:john)
end
 
Baby.new.call_john_mother # -> Hey john's mother! Come here!

And it works just as you wanted! Class macros can now be included into any class’ eigenclass!

Part Three: You’re getting excited aren’t you?

I really wanted to give just an introduction to those of you that never heard of this kind of ruby trick, so it’s totally out of the scope of this simple blog post to talk about some pretty serious stuff you can do with class macros and metaprogramming. So, to help those of you who want to learn more, here goes a bunch of resources you can use to learn more about this kind of trickery:

I really recommend you dig deep into this topic since it allows you to fully understand some advanced code and create solutions in a very specific, creativity-enabling way!

Do you have any thoughts on this? Comments are more than welcome!

  • Peter

    Nice article. Little typo at the top.
    has_many instead of have_many

    • http://techie.lucaspr.im Lucas d’Acampora Prim

      Thank you!
      Already fixed the typo, thanks a lot!

  • http://blog.segment7.net drbrain

    These aren’t macros, ruby doesn’t have macros.

  • http://twitter.com/ironchamber Joseph McCormick

    I like how you balance conversational and instructional tones, and the content itself was good (In-action metaprogramming lessons make me happy :) ) – thanks dude!

    • http://techie.lucaspr.im Lucas d’Acampora Prim

      Thanks a lot, Joseph! It´s very motivating to receive comments like this! :)
      It´s a pleasure to share knowledge with the ruby community!

  • http://twitter.com/simonbaird Simon Baird

    The article is good but I’m not sure it’s appropriate to call them “macros” since the term has a specific meaning in programming generally. Not sure what to call them though, maybe class methods for meta programing?

  • JESii

    The term ‘macro’ certainly does have a lot of historical meaning (as in assembler language, for example) and this isn’t a macro in that sense.  However, the Ruby community has widely adopted the term ‘Class Macro’ for this particular metaprogramming construct. See the book “Metaprogramming Ruby” (http://pragprog.com/book/ppmetr/metaprogramming-ruby) to learn more.

    • http://techie.lucaspr.im Lucas d’Acampora Prim

      Actually, this book is exactly where i got the name “class macros” from!

      Thanks a lot for your clearification!