Ruby has some pretty useful built-in tools that you probably won’t know if you (as a significant number of Ruby developers) are used to the “Rails magic” and don’t know how to make use of some “Ruby magic”.

One of these tools is the Forwardable module, that provides delegation of methods to a designated object. To see how this module can be useful, let’s imagine we need to implement a decorator for the Student model of one application.

First implementation

The Student class has :first_name , :last_name , :professors (an array of Professor objects), :birthday and :course (a Course object) as attributes, and the decorator should respond to all these messages added by :full_name , :professors_names and :course_name .

We could implement the decorator as following:

The code does exactly what we need but it looks a little verbose with all these methods from the Student class being rewritten. It would be nice if we could delegate all these methods to the Student instance writing less code.

Let Forwardable do the job for us

The first thing we need to do is extend our StudentDecorator class with the Forwardable module. Then we will be able to use the methods def_delegator and def_delegators provided by it.

def_delegator: takes the source object as the first argument, the delegated method as second and an optional third param that will serve as an alias for the delegated method.

takes the source object as the first argument, the delegated method as second and an optional third param that will serve as an alias for the delegated method. def_delegators: as the previous, takes the source object as the first argument and the remaining arguments are a list of delegated methods.

With these methods available we can rewrite the decorator:

In line 5, def_delegators will define a list of methods starting with the second argument and delegate them to the object passed as the first one. In line 6, def_delegator will define a method named course_name and delegate the message name to student.course . Note that we can pass a nested object as the first argument in def_delegator , to do so we just need to pass it as a string instead of a symbol.

After refactoring, we’ve reduced our code from 39 to 20 lines. It looks cleaner and now we don’t have to write an entire method every time we need to add a method that will be directly passed to the decorated object.

The new implementation looks good, but I think we can improve the code’s expressiveness by changing the way we delegate these methods.

Creating our own Delegator

Now that we know how to delegate methods, we can create our own Delegator module, defining a more expressive syntax to delegate methods.

The Delegator module would look like this:

Then we can include the new module in our Decorator class and use the new syntax to delegate methods:

The delegate method takes a list of methods as its first argument, the named parameter to: takes the designated object and the optional param as: takes an alias to the given method. In our implementation, if any value is set to as: only the first method in the list will be defined.

I think this is a clearer syntax, making it easier to understand that we’re delegating methods to a specific object with a given name. Also, it’s similar to how the delegate method works in ActiveSupport, facilitating the lives of coworkers who are not necessarily familiar with the Forwardable syntax.