Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
RAILS HELPERS: FINE AND DANDY LIKE COTTON CANDY

I hated Rails helpers. I saw them as dumping grounds; one-off procedural aberrations in a sea of objects. But I didn’t just complain. I acted. I created the “frill” gem, an implementation of the decorator pattern that I extracted from a project that actually needed a decorator pattern in it’s view layer.

I had another project that seemed ideal for “frill”. It was a data analytics application that required a good deal of manipulation of the raw numeric data points for presentation.

For example, a typical stack of manipulations might look like:

  1. human size a number (for example, turn a “disk_space” attribute representing 1024 bytes into “1 KB”)
  2. if the underlying datapoint exists, render the human sized number, semantically and visually distinguishing the number from the units
  3. if the value is at this point not nil, render the number and its units red or green depending on whether or not it’s healthy
  4. if the underlying datapoint is nil, render a “N/A” message

We implemented the logic using “frill”. Our views looked like a paragon of simplicity:

= environment.disk_space

We had cleverly hidden all of the complexity in a series of modules that frill would dynamically decorate onto our models:

module HumanSizer
  include Frill

  def disk_space
    HumanSizedNumber.new(super)
  end
end

module Renderer
  include Frill
  after HumanSizer

  def disk_space
    if super
      render “human_sized_number”, number: super
    else
      render “not_available”
    end
  end
end

#etc...

The frill library took care of stacking all of the decorators together, extending the objects at runtime with the relevant modules.

So what went wrong?

  1. Indirection. If these were the only two modules in the frills directory, then perhaps it wouldn’t have mattered. But when there was no obvious sign pointing from the view or controller to the relevant modules, you were often left scratching your head (or jumping into a debugger session) to determine the thread of execution when something went wrong
  2. Rigidity. The framework worked well for 90% of cases. But what about the other 10% of the time when you need to alter the presentation stack in some small but suble way? It turns out it was hard to remove existing decorations, or alter their presentation stack.

The latter problem proved especially tenacious. And lacing the decorators with all kinds of conditionals about the random one off cases where such and such decorator didn’t help anything.

HELPERS – STATELESS, COMPOSABLE, FLEXIBLE

When the frill library didn’t work out, we tried using helpers – and discovered that Rails helpers were the answer we’d been seeking all along. We created simple helper methods that we could stack together in pretty much any way our presentation demanded.

= report_NA_for_missing(colorized(human_sized(environment.disk_space)))
= colorized(percentage(environment.density_ratio))

Following the thread of decoration was now trivial.

It dawned on me that there’s really just a simple rule to follow with helpers: keep them stateless. If they’re simple, stateless methods that always return the same output for a given input, then they’re easy test and easy to stack in new and interesting ways. You can even create higher order functions quite easily by taking advantage of the “method” method for turning a method into an object:

= call_reporter_if_nil(method(:report_NA), colorized(human_sized(environment.disk_space)))
= call_reporter_if_nil(method(:report_unknown), colorized(percentage(environment.density_ratio)))

LET THE FUNCTIONAL PROGRAMMING REVOLUTION BEGIN

P.S. I don’t actually endorse that last code snippet.

Comments
  1. Jared Carroll says:

    Great post Matt.

    I’ve worked with several Rails codebases where presenters were the norm. One
    used Ruby’s http://www.ruby-doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/Object.html#method-i-DelegateClass which required more classes, more setup in the controller, and resulted in some
    confusing object identity issues.

    I prefer classic, “Rails way”, stateless helpers. The only disadvantage is that you can’t have two helpers with the same name, but I don’t see this as a justification for introducing new classes.

    An interesting middle ground is the active_decorator gem: https://github.com/amatsuda/active_decorator.

  2. Will Read says:

    We made another stop that Matt didn’t mention in Draper-land. The pain was that “stacking” behaviors like colorize and human_size meant mixing in more modules to our decorators. More files, more classes, and still not very readable when you’re in the view wondering how you might expect a number to be rendered.

  3. Brian Butz says:

    To me, this is a case for why you want presenters over decorators in your views; you want to limit the number of paths your code can go down, not increase it. I think the justification for introducing an object is to address exactly the problems mentioned in this post: a Plain-Ol’ Ruby Object won’t be mixing in all kinds of whats-its from wheres-its, reducing the need to go into it and debug a crazy stack. On the plus side, you also get a nice, simple object to test that is also easy to pass around.

  4. Matthew Parker says:

    @Brian – those PORO presenters don’t stay plain for long, in my experience. They need to be able to call in the full array of Rails view helpers in practice, which makes them trickier to test, trickier to construct, and trickier to pass around. The draper gem can fit this bill nicely, though your presenters won’t pass the PORO test.

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *