A case for decorators in Rails

Decorators, at the beginning of my Rails journey, seemed exotic and unnecessary.

Draper, a popular gem for decorators, calls them an “object-oriented layer of presentation logic to your Rails application.” I struggled to see the practical benefit.

A particular use case sold me on decorators’ benefits.

Use case: default field values

TourExec is a Rails app that helps businesses take reservations online.

When customers make a reservation, they see a friendly introduction from the business. TourExec provides a default introduction, which a business can override with a custom one.

Screen Shot 2016-08-06 at 7.13.49 PM

In the database, the business’s intro field is blank unless they’ve added a custom introduction. We need to show the default intro if the field is blank, or show the custom intro if the field is filled.

You might wonder why I don’t store the default right in the database. The default text is dynamic, based on the business’s name (which can change), and I want the flexibility to retroactively change the default text.

Option 1: Use conditionals in views

I could use conditionals within views to show either the default or the custom intro:

- if @business.intro.present?
  = @business.intro
- else
  We're excited to have you on a tour at #{@business.name}!

This approach fell apart when I started building the form for businesses to update their intro message. I wanted businesses to see the default and be able to tweak or replace it.

Screen Shot 2016-08-06 at 7.18.33 PM

I had to hijack the form helper to show the default if the field was blank.

- f.input :intro, value: (
  (@business.intro.present? && @business.intro) ||
  "We're excited to have you on a tour at #{@business.name}!")

This felt messy and unclear.

Option 2: Use the model

I considered overriding the “intro” method in the Business model to return the default if the intro was blank.

# app/models/business.rb


def intro
  (attributes["intro"].present? && attributes["intro"]) ||
  "We're excited to have you on a tour at #{name}!"

It felt wrong to add presentation logic to the model. If I wanted to use HTML in the default message, I’d have to use raw HTML in the model or bring in ActionView helpers, which have no place in a model.

Option 3: Use a decorator

Decorators handle “presentation logic” for your models. That is, if a model’s fields should be shown in a certain way, the decorator is responsible for making that happen. Draper is a great tool for quickly implementing decorators.

First, I installed the draper gem. Then, I created a decorator for businesses.

# app/decorators/business.rb

class BusinessDecorator < Draper::Decorator

  def intro
    if object.intro.present?
      "Book a tour with #{object.name} below!"

The decorator introduces an intro method on top of the model. It can reach into the model’s intro method with object.intro to grab the actual field value.

When I set @business in my controllers, I have to decorate the object:

@business = current_user.business.decorate

Now, in views, I can reference the field as I normally would.

= @business.intro

And I have to do absolutely nothing to my form helpers:

- f.input :intro

Decorators won!

For this implementation of default field values, decorators seem the best option. I’d love your thoughts and feedback in the comments.

More about decorators

Decorators can do a lot more, like:

  • Show a date field in a particular format throughout your app
  • Show a status field with particular language throughout your app
  • Anything else that involves showing data from your models in a particular way

Check out the Draper docs for more information. Draper doesn’t have a monopoly on decorators; it just makes them faster to implement with a proven approach.

Hired.com brings Rails job offers to you. Free for candidates. Salaries from $75,000 to $250,000. Sign up now!

Leave a Reply