Ruby Metaprogramming

Ruby is an amazing programming language in many aspects, especially regarding metaprogramming! The fact that everything is an object, give to the programmer the ability to manipulate methods and closures, the inheritance chain classes, callbacks, have modules with common code, and a lot of other nice things!

In this Ruby Pill, I’ll cover 3 module callbacks (included, extended and method_added), to dynamically add methods to a class.

Some Module Callbacks

Let’s take a module with the 3 callbacks that we’ll use:

module Example
  def self.included(base); end

  def self.extended(base); end

  def self.method_added(method_name); end
end

Despite the descriptive names, follows a summary of each method:

  • included(base): executed after you include the module. If you add some method here, it will be an instance method of your class.
  • extended(base): executed after you extend the module. If you add some method here, it will be a class method of your class.

    In both cases, base refers to the class where you include/extend the module.

  • method_added(method_name): Here you can put the code to be executed after you’ve been added a method in your class.

Adding Methods Dynamically

Here is our module with its callbacks and implementations:

module Example
  def self.included(base)
    add_new_method(base)
  end

  def self.extended(base)
    add_new_method(base)
  end

  def self.method_added(method_name)
    puts "Adding #{method_name.inspect}"
  end

  private

  def self.add_new_method(base)
    base_name = base.name
    define_method("#{base_name.downcase}_new_method") do |value|
      "Method added in #{base_name} class => Value passed: #{value}"
    end
  end
end

The magic is inside our add_new_method private method. It’s called inside included and extended callbacks, passing the base variable, that is our class. Using define_method method, we add a new method called <CLASS_NAME>_new_method.

To see them in action, we could use it like this:

class Wow
  include Example
end
puts Wow.new.wow_new_method(:wow) 
# => Adding :wow_new_method
# => Method added in Wow class => Value passed: :wow

class Cool
  extend Example
end
puts Cool.cool_new_method(:cool)
# => Adding :cool_new_method
# => Method added in Cool class => Value passed: :cool

Cool! Besides add new instance/class methods to our classes, we can see the message of the method_added callback after each one is added!

Final Thoughts

This only scratches some of Ruby capabilities! There’s a world of possibilities, methods, and callbacks to use if you want to take a deep dive on Ruby metaprogramming!

One thing that you should be aware of is that how much more you use this kind of Ruby features, your code can become too slow, so be careful!

Did you already know about these module callbacks?

Let me know if you liked this post! If you have any suggestions or critics, post a comment below!