I recently had a fantastic experience giving a presentation at RubyConf called The Secrets of the Standard Library. The slides are available and once the video is up I'll send along a link to that.

There are no secrets in the Standard Library

There are no secrets, of course, because the code is right there for you to read but many developers don't realize that powerful tools like SimpleDelegator and Forwardable are built into Ruby. There's no gem to find and install, just require the file you need and you've got it. It's as easy as:

require 'delegate' class MyWrapper < SimpleDelegator; end require 'forwardable' class MyClass extend Forwardable end

My goal in the RubyConf presentation was to show that these tools are available and to describe how they work. Using the tools is one thing, but understanding them is another.

In previous posts we've been looking at ways to use SimpleDelegator to handle presentation behaviors and there's been a bit of Forwardable too. Once you've got a clear understanding of them, you can be more creative with their use and build your own tools to make your code more habitable.

I find that implementing my own library can be a great exercise in understanding an existing one. It was fun to write two short lines in an attempt to create SimpleDelegator and Forwardable in what would fit in less than 140 characters:

# My SimpleDelegator class D;def initialize(o);@o=o;end; def method_missing(m, *r, &b);@o.send(m,*r,&b);end;end # My Forwardable module Fwd;def fwd(a,m,n=m);self.class_eval "def #{n}(*r,&b);#{a}.__send__(:#{m},*r,&b);end";end;end

Expanding those out with more descriptive names reveals a bit more about what they do:

# My SimpleDelegator class Delegator def initialize(object) @object = object end def method_missing(method, *args, &block) @object.send(method, *args, &block) end end # My Forwardable module Forward def forward(to, method_name, alternative=method_name) self.class_eval " def #{alternative}(*args, &block) #{to}.__send__(:#{method_name},*args, &block) end " end end

In both my simple implementation and the actual implementation one uses method_missing at run time and the other defines methods at compile time.

Knowing this, and seeing how they work, can help you make decisions about when to use one over the other. Defined methods are faster than method_missing but they require a more rigid structure.

When do you use SimpleDelegator and when do you use Forwardable?

I generally follow these simple rules for choosing which library to select:

Use SimpleDelegator when you either don't know or don't care which messages may be sent to your object. This excepts (of course) the methods you define in your wrapper. Use Forwardable when you know the messages to forward ahead of time.

Both libraries follow the same pattern that a message to one object will be sent along unaffected to the relevant object. But there are times when building your own gives you a better understanding and more control over when errors occurr.

Building your own SimpleDelegator can be tricky. There's quite a lot of work done for you already to help ensure that your wrapper acts like the wrapped object so do read the code and get familiar with the trade-offs when writing your own.

Errors instead of method_missing

Sometimes you may want more control or restriction on the behavior of objects.

You can build a wrapper with Forwardable, for example, by swapping out the object of concern and instead of everything passing through method_missing , you'll get a NoMethodError .

Here's a short example:

class Person attr_accessor :name, :color, :number, :email end require 'forwardable' class Wrapper extend Forwardable delegate [:name, :color, :number] => :person attr_accessor :person end wrapper = Wrapper.new wrapper.person = person1 wrapper.name #=> person1 name value wrapper.person = person2 wrapper.name #=> person2 name value wrapper.email #=> NoMethodError

A wrapper like this allows us to specify only the methods we want to pass-through and any others that person may have will raise an error.

How have you used these libraries? How do you decide what to use?

What have you built to do similar things?

Last but not least, I recently wrote about undestanding delegation on my blog and how it helped me create the casting gem.

If you enjoyed this post, sign-up for my newsletter below and get more.