こんなことありませんか?
Modelの情報を少し加工してViewに表示したいことってありますよね?
例えば、ArticleというModelがあってその情報を少し加工してViewに表示したい、という以下のような場合があると思います。
<%= publication_status(@article) %>
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_status
や article_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のいいところは、
- Modelに書かずにModelに対応したViewへの表示フォーマットを用意出来ること
- Decoratorの中にActionViewのhelperを使用することが出来ること
だと思っています。
1のほうはいいとして、2のほうはactive_recordで登場する例を用いると以下での link_to
のようなhelperをDecoratorの中に直接書けるということです。
amatsuda/active_decorator
module UserDecorator
def full_name
"#{first_name} #{last_name}"
end
def link
link_to full_name, website
end
end
<% @users.each do |user| %>
<%= user.link %><br>
<% end %>
なかなかに便利だと思うので是非導入して見てください