Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

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 %>

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

shizuma
web&DeepLearningエンジニア。 ACES.inc←東京大学大学院/東京←鹿児島/blog https://blog.seishin55.com ; Qiita https://qiita.com/shizuma ; note https://note.mu/seishin55
https://seishin55.com
aces
ACES(エーシーズ)は、 画像認識を中心としたAIアルゴリズムの力で、リアル産業のDX(デジタルトランスフォーメーション)を推進し、シンプルな社会を実現する会社です。ヒトの働き方をデジタルの力で 自動化・効率化することで、誰もが生き生きと 生きられる社会を実現していきます。
https://acesinc.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away