読み込み中...

【Rails】Viewの責務の分離に役立つDraper gem

プログラミング #draper #gem #rails #クリーンコード
2024年4月11日
2024年5月9日
【Rails】Viewの責務の分離に役立つDraper gem

Railsは非常に便利なフレームワークですが、アプリケーションが大きくなるにつれ、コードの複雑さも増していきます。

責務の分離を意識し、コードの保守性や拡張性を高めることは開発速度の向上やバグ発生防止に有効です。

本記事では、Railsで開発する際に、Viewの責務の分離に役立つDraperというgemを紹介します。

このgemを活用することで、よりクリーンで保守性の高いコードを書くことが可能となります。

Draperとは

Draperは、構造に関するデザインパターンの1つであるデコレーターパターンを実現するためのgemです。

View固有のロジックをModelやViewから分離することで、コードの責務を明確にし、可読性を向上させることができます。

GitHub: https://github.com/drapergem/draper

Draperのメリット具体例

以下のようなerbのコードがあったとします。

<% @posts.each do |post| %>
  <p>
    <% if post.published? %>
      <%= post.content %>
    <% else %>
      Unpublished
    <% end %>
  </p>
<% end %>

こちらViewにロジックが書かれており、もし、Viewの単体テストをするとき、このケースを想定したテストコードを書かないといけません。

今回は一箇所のみなので問題ありませんが、Viewでは同じような処理が他のインスタンスでも行われるのが一般的であり、このインスタンスごとにデータを準備するのでは、関心が広くなり単体テストも作りにくくなります。

もし、Draperを利用すれば以下のようなコードとなります。

erb

<% @posts.each do |post| %>
  <p><%= post.published_content %></p>
<% end %>

Draperクラス

class PostDecorator < Draper::Decorator
  delegate_all

  def published_content
    return post.content if post.published?

    'Unpublished'
  end
end

controller

@posts = Post.all.decorate

そして、テストをする際は以下のようにモックを定義することが出来ます。(@postsを作る処理をスタブしてこのモックの配列を返すようにするのですが割愛しております。)

double('Post', published_content: 'Content')

こうすることで、Viewでの単体テスト時、Viewではあくまでも published_content メソッドが呼ばれるところまでを確認範囲とし、実際のロジックはDraperのクラスの範囲とすることが出来ます。

今回の例では、一箇所のみのコードサンプルのためあまりピンとこないかもしれませんが、Viewで参照する複数のインスタンスでそれぞれ複雑な処理をしているとき、Viewの単体テストでそれぞれのロジックに対応するテストデータを作成してテストするより、Draperを使ってView固有のロジックを別クラスとし、Viewでは呼び出しだけを担保する方がViewの責務もシンプルになり、単一責任の原則に則るので良い状態と言えます。

このように、コードを書くときは、いかに「単体テストがしやすいか?」の視点で書くことを意識すると、疎結合で良いクラス設計ができると考えています。

余談

なぜモデルではなく、Draperのクラスで定義するかというと、もしView固有のメソッドをすべてモデルに書くとなると量が多くなります。

また、View以外で使用するメソッドとの名称のコンフリクトも起きかねません。View固有であればなるべくDraperで定義したほうがモデルの肥大化を防ぐ事が出来ます。

以上、参考になれば幸いです。



システム開発やDX戦略などでお困りの際はお気軽にご相談ください。

※通常1営業日以内に回答致します。

お問い合わせはこちら
Top