Show:

Keep the Rails templates and models clean with the Decorator pattern

December 1, 2017 Programming

AxiomQ

At the beginning of their career many new Rails developers put complex presentation logic directly into the templates, that looks something like this:

<%= @player.first_name.capitalize + " " + @player.nick_name.capitalize + " " + @player.last_name.capitalize %>

At that point, that kind of solution seems reasonable, but after awhile they all ask the same question: Is this the right place for such a code?

Of course, the answer is always negative, and they rush to find a better place for that code.

By default, Player model seems like the right place for new clean code, and full_name method will probably serve the purpose very well. That now looks something like this:

class Player < ApplicationRecord
  def full_name
    "#{first_name.capitalize} #{nick_name.capitalize} #{last_name.capitalize}"
  end
end

and in the template we can easily call our new full_name method:

<%= @player.full_name %>

Great, everything works as expected, and the developer is very proud. This feeling will last as long as developer does not have a lot of similar methods, that can make Player model very huge and difficult to maintain. Which brings us to the Decorator pattern. Lets see how we can use it to keep our Rails templates and models clean.

What is the Decorator pattern?

Decorator pattern enables us to easily add an enhancement to an existing object and to avoid possible problems when we throw all methods in one class (Player model in our case).

So, lets create our decorator. For me, the best practice is to create decorators directory inside app directory and to put all of my decorators there. Then we will create PlayerDecorator class which will inherit from SimpleDelegator, which is one of native Ruby class from the Ruby Delegator library. Now we can add full_name method directly to PlayerDecorator class and remove one from the Player model.

class PlayerDecorator < SimpleDelegator
  def full_name
    "#{first_name.capitalize} #{nick_name.capitalize} #{last_name.capitalize}"
  end
end

We can decorate a Player model instance before passing it to a template (e.g. form some controller action). Or, we can decorate an Player model instance directly, within a view, like this:

<%= PlayerDecorator.new(@player).full_name %>

So, to conclude, when you move your complex presentation logic into the decorators, your templates and models will stay clean, and easy to maintain.