Black Bytes
Help more people learn by sharing this post!

Metaprogramming in The Wild

You may have read about Ruby metaprogramming before & maybe you have used it in some of your projects, but how are some of the most popular open-source projects making use of this feature?

Find out in this post!

Rails Example

Rails makes heavy use of metaprogramming, so it’s a good place to start looking.

For example, when you want to check the current environment in your Rails app, you do something like:

But what is env? And how does that work? The answers are in the StringInquirer class, which is a subclass of String. This class uses method_missing so that you can call env.production? instead of env == production.

This is what the code looks like:

This is saying: “if the method name ends with a question mark then do the comparison, otherwise keep going up the ancestors chain”.

The original code can be found here.

Sinatra Delegation

If you have used Sinatra before you may know that there are two ways to define your routes: by using the get / post methods directly, outside of any class. Or by defining a class that inherits from Sinatra::Application.

The Sinatra DSL (Domain-Specific Language) methods are defined inside Sinatra::Application, so how can you use them outside of this class?

Well, there are two things going on here: metaprogramming & module extension.

Sinatra defines a Sinatra::Delegator module inside base.rb, which is used to delegate method calls to a target. The target is set to Sinatra::Application by default.

This is a simplified version of the delegate class method, defined in Sinatra::Delegator:

This code is creating a set of methods (with define_method) that will forward the method calls to Sinatra::Application (the default value for Delegator.target).

Then the main object is extended with the methods defined in Sinatra::Delegator, which makes the Sinatra DSL available outside the Sinatra::Application class.

It’s worth noting that Ruby has two built-in classes for method delegation, in case you need them: Delegator & Forwardable.

The Paperclip Gem

Paperclip is a gem which allows your application to handle file uploads. Files are associated with ActiveRecord models. To define an attached file on a model you can use the has_attached_file method.

For example:

This method is defined on paperclip.rb and it uses metaprogramming to define a method on the Model. This method can be used to access the file attachment (which is an instance of the Paperclip::Attachment class). For example, if the attached file is :avatar, Paperclip will define an avatar method on the model.

Here is the define_instance_getter method, which is responsible for that:

From this code we can see that the attachment object is stored under the @attachment_#{name} instance variable. On our example that would be @attachment_avatar.

Then it checks if this attachment already exists, and if it doesn’t, it creates a new Attachment object and sets the instance variable.

Here is the original source code.

Conclusion

As you have seen, metaprogramming is a very powerful and flexible technique, but whenever you want to reach for it remember that quote that says: “With great power comes great responsibility”.

If you enjoyed this post don’t forget to subscribe to my newsletter 🙂