What to code when you know what you want

My last few posts have built up a simple wrapper class that helps to add behavior to an object when we're displaying information to our users.

  1. Ruby delegate.rb Secrets
  2. The easiest way to handle displaying bad data
  3. Simplify your code with your own conventions
  4. Formatting collections of objects with SimpleDelegator
  5. How to make your code imply responsibilities

We've been using SimpleDelegator which uses method_missing to do its magic. But there's another way of forwarding messages with the standard library Forwardable.

In our presenters, we've been wrapping a main object and a view to handle concerns for the display but there are certain features that we don't want to augment with our wrapper. In Rails we have methods like link_to which help us to create links in our display, and in previous code I showed how we used the view reference in our presenters to handle sanitizing content:

class Presenter < SimpleDelegator
  def sanitize(*args)
    @view.sanitize(*args)
  end
end
class UserPresenter < Presenter
  def bio_html
    sanitize(user.bio)
  end
end

This sanitize method highlights an aspect of handling the behavior of our presenters: sometimes we know exactly what we want to happen, other times we don't know or don't care.

Use shortcuts for forwarding when you know what you want

When building my presenters, we knew that we needed to have the view handle sanitization because it was built into the framework. But writing methods every time we wanted to send a message to the view is cumbersome. This is how we made it even easier:

require 'forwardable'
require 'delegate'
class Presenter < SimpleDelegator
  extend Forwardable
  delegate [:sanitize, :link_to, :paginate] => :view
end

With this simple change, we can use all of these specified methods inside our presenters and they'll automatically be handled by the specified object, the view.

When this Presenter class is loaded, these methods (sanitize, link_to, and paginate) will be generated for us. They'll define the named methods and automatically pass along any arguments.

This allows us to create simpler classes without the need to create method definition blocks ourselves and our other methods end up looking like this:

class ProfilePresenter < ::Presenter
  def social_media_link(options={})
    link_to('Tooter', tooter_url, options)
  end
end

The access to link_to is implicit: we expect it to be there already. Implicit behavior can be dangerous in that it requires that anyone working with the code know to expect the behavior. Your development team should be aware of the structure of your presenter classes and understand that these methods will automatically be forwarded to the view. But this is all a part of finding patterns in your code.

Rails-flavored forwarding

If you're using Rails, you don't need to use Forwardable because every class in your program will already have a delegate method defined by ActiveSupport.

The syntax is similarly easy:

require 'delegate'
class Presenter < SimpleDelegator
  delegate :sanitize, :link_to, :paginate, :to => :view
end

Your code will be more succinct when you use shortcuts provided by Forwardable and other libraries. SimpleDelegator allows us to create objects which act as a pass-through filter for behavior. Because of method_missing, we can treat our wrapper like a replacement for another object but with Forwardable, we must specify our intentions when we design the class.

Try it out and let me know how you handle managing behavior among objects in your wrappers. Everyone on my Clean Ruby mailing list gets regular tips like this. Signup below.