af83

Cleanup Rails' views with decorators

Ruby on Rails is an awesome framework to quickly build web applications. The "build a blog in 15 minutes" video caught everybody's attention a few years ago. To reach this result, Rails relies on the MVC pattern, and lots of conventions.

Yet, having more than 5 developers working over several months on the same Rails application is not the same story.

Every team hits the same wall after only a few months, code becomes complex to maintain, productivity starts to decrease, etc. We can find a lot of reasons to this (too much logic in controllers, fat models, lack of services layers, etc.), but, I will just name one: logic in view.

Logic in view is bug prone, difficult to test, and can be a pain for front end developers.

Let's consider a classic use case: having ACL on models because we do not want our precious resources to be writable by all users.

Libraries like CanCan make adding ACLs to a Rails applications quite easy. But, views can be easily polluted with stuff like:

<% if can? :update, @article %>
  <%= link_to "Edit", edit_article_path(@article) %>
<% end %>

Yes, you got it, logic in view. And, since we do not have proper view inheritance, this code will have to be copy-pasted over and over. I would rather have something like:

<%= @article.link_to_edit %>

So, how can we remove logic from the view?

Decoration saves the day

As usual, we can find a solution with a pattern. Today, the decorator pattern will help us remove logic from views in our Rails applications.

The idea is to insert an object between the model and the view. This object will know of the model, the context (most of the time, a current_user), and have access to the view's helper methods.

There are several good libraries in the Ruby world like Draper or active_decorator.

We will use Draper in the next examples. So, let's create a decorator for the Article class.

class ArticleDecorator < Draper::Decorator
  delegate_all

  def link_to_edit
    if h.can? :update, object
      h.link_to "Edit", h.edit_article_path(object)
    end
  end
end

class ArticlesController < ApplicationController
  def edit
    respond_with @article = Article.find(params[:id]).decorate
  end
end

A few things to notice here. From the controller, we call the decorate method on a instance of Article, this will return a decorated version of the article object.

In the ArticleDecorator class, we have access to the decorated object with the object method, and to all view helpers through the h object.

We can now call the link_to_edit method on the article object from our view, and the decorator will take care of the logic, and return a formatted link if the user has the right to edit this resource, and nothing if he does not.

The code is easier to test, we just instantiate a new instance of the ArticleDecorator, and check the output of the link_to_edit method. No need to test the whole rendered view, searching through the DOM object tree.

This is a classic Ruby class, so we have access to inheritance and modules to easily share behavior through several objects. It would not be hard to write a LinkToAction module to provide link_to_edit, link_to_destroy methods.

So, we gain code readability, code reuse, better testing, and in addition, we even lost a few lines of duplicated code. It seems a good deal to me.

blog comments powered by Disqus