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