RAILS HELPERS: FINE AND DANDY LIKE COTTON CANDY

April 26, 2013 Matthew Parker

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.

About the Author

Matthew Parker

Matt Parker is Head of Engineering for Pivotal Labs

Previous
Sencha Touch BDD Part 2
Sencha Touch BDD Part 2

Sencha Touch BDD tl;dr A multi-part series of articles on how to test Sencha Touch applications. It uses Ja...

Next
The Pivotal People Changing the Way We Live
The Pivotal People Changing the Way We Live

A paradigm shift is underway, as real-time data, rich analytics, and robust cloud applications converge. Th...

×

Subscribe to our Newsletter

!
Thank you!
Error - something went wrong!