Rails Viewの表示のためにDecoratorを用意してHelperとModelを助ける


こんなことありませんか?

Modelの情報を少し加工してViewに表示したいことってありますよね?

例えば、ArticleというModelがあってその情報を少し加工してViewに表示したい、という以下のような場合があると思います。

<%= publication_status(@article) %>


app/helpers/articles_helper.rb

def publication_status(article)

if article.published_at?
"Published at #{article.published_at.strftime('%A, %B %e')}"
else
"Unpublished"
end
end

ただし、これだと違和感があって、BookというモデルもArticleモデルと同じようなメソッドを作ろうとしたときに、少しだけしかフォーマットの仕方を変えないのに同じようなメソッドを作る必要が出てきます。

book_publication_statusarticle_publication_status のような2つのメソッドになるかと思いますが、グローバルな空間にこのように同じようなメソッドを作り続けるのは嫌ですね。

だったらModelで定義してしまえ!とModelで定義して下記のように呼び出すことも出来るかと思います。ただし、このメソッドはViewの表示部分のロジックを担っているわけで、その責務をModelには持たせたくない。

<%= @article.publication_status %>

そこで登場するのが Decorator というわけです。Decoratorは対応したModelのViewへの表示フォーマットのためのロジックを請け負ってくれます。

それではどのように使っていくか見ていきましょう。


以上の導入の例は以下のdraperのREADMEの導入部を使用させて頂きました。 

drapergem/draper

ただし、今回実装で使用するのは下記のactive_decoratorになります。

amatsuda/active_decorator



実際の使い方

今回、decoratorを導入するために以下のactive_decoratorを使用します。

amatsuda/active_decorator

使い方は簡単で


1. gemの 「active_decorator」をbundle

gem 'active_decorator'


2. decoratorのファイルを作成

rails g decorator article # 対応するモデルに合わせて。


3. decoratorのファイルにメソッドを記述

# app/models/article.rb

class Article < ActiveRecord::Base
end

# app/decorators/article_decorator.rb
module ArticleDecorator
def publication_status
if published?
"Published at #{published_at.strftime('%A, %B %e')}"
else
"Unpublished"
end
end
end

# app/views/articles/show.html.erb
<%= @article.publication_status %>


Decoratorの利点

これまでの説明から、Decoratorの役割はイメージ出来たかと思います。Modelに対応したViewへのロジックをModelと切り離して書けていい感じですよね。

このDecoratorのいいところは、


  1. Modelに書かずにModelに対応したViewへの表示フォーマットを用意出来ること

  2. Decoratorの中にActionViewのhelperを使用することが出来ること

だと思っています。

1のほうはいいとして、2のほうはactive_recordで登場する例を用いると以下での link_toのようなhelperをDecoratorの中に直接書けるということです。

amatsuda/active_decorator


app/decorators/user_decorator.rb

module UserDecorator

def full_name
"#{first_name} #{last_name}"
end

def link
link_to full_name, website
end
end



app/views/users/index.html.erb

<% @users.each do |user| %>

<%= user.link %><br>
<% end %>

なかなかに便利だと思うので是非導入して見てください