Rails Decorators using the Draper gem

Rails models are getting fatter and fatter and the complexity of helper classes increases.
Rails Decorators using the Draper gem

You might have experienced this yourself: Over time, Rails models are getting fatter and fatter and the complexity of helper classes increases.

It is good practice to keep complicated logic away from your views. But dealing with a lot of helper methods is also tedious and adding view-related logic to the model is not an option either.

The Decorator design pattern helps to solve this problem.


Draper decorators

There is a nice gem called Draper that facilitates this.

In our example, a decorator comes as another level of abstraction between the model and the view.

class PersonDecorator < Draper::Decorator
  delegate_all

  def full_name
    "#{firstname} #{lastname}"
  end
end

@person = Person.new(firstname: 'John', lastname: 'Doe').decorate

@person.full_name # 'John Doe'
@person.firstname # 'John'

Calling #decorate on a model returns a decorated instance. We now have access to the #full_name method.

Delegation

The delegate_all call ensures that model methods are available in an instance of the decorator as well. You can use a decorated object the same way as a model.

Draper also takes care that the class of a decorated instance is set to the class of the model. That way, all Rails helpers like e.g. form_for work as expected when you pass a decorated instance. In fact the decorated instance behaves and looks like a regular model instance.

@person = Person.first
@decorated_person = @person.decorate

@decorated_person.is_a?(Person) # true
@decorated_person.is_a?(PersonDecorator) # true

@person == @decorated_person # true

Accessing helper modules

Additionally, a decorator has access to Rails helper modules via the helper proxy method. This is useful when you want to hide complex logic from the templates.

class PersonDecorator < Draper::Decorator
  delegate_all

  def author_headline
    if author == helper.current_user
      h.content_tag(:h3, 'You')
    else
      h.content_tag(:h3, author.name)
    end
  end
end

Now in your template you just call @person.author_headline and you are done. No conditions in the template. Your designers will be thankful!

More on decorators

To find out more about alternative implementations and internals of creating decorators in Ruby, I recommend to read Dan Croak’s article at Giant robots smashing into other giant robots

Photo by Tiago Gerken on Unsplash

Want to join our Engineering team?
Author Headshot
Babbel
We are a team of more than 750 people from over 50 nations with a shared passion for languages. From our offices in Berlin and New York, we help people discover the joys of self-directed language learning. We currently offer 14 different languages — from Spanish to Indonesian — that millions of active subscribers choose to learn.
We are a team of more than 750 people from over 50 nations with a shared passion for languages. From our offices in Berlin and New York, we help people discover the joys of self-directed language learning. We currently offer 14 different languages — from Spanish to Indonesian — that millions of active subscribers choose to learn.

Recommended Articles

On The Linguistic Trail With Nomadic Languages

On The Linguistic Trail With Nomadic Languages

Does not having a fixed regional identity change how language evolves?
8 French Expressions About Love From Around The World

8 French Expressions About Love From Around The World

Try expressing your love in French with these romantic phrases from France, Cameroon, Canada, Haiti, Senegal and other French-speaking countries.
How To Talk About Tech And The Internet In Polish

How To Talk About Tech And The Internet In Polish

Start surfing the internet in a whole new language.