【Rails】Viewの責務の分離に役立つDraper gem
プログラミング #draper #gem #rails #クリーンコードRailsは非常に便利なフレームワークですが、アプリケーションが大きくなるにつれ、コードの複雑さも増していきます。
責務の分離を意識し、コードの保守性や拡張性を高めることは開発速度の向上やバグ発生防止に有効です。
本記事では、Railsで開発する際に、Viewの責務の分離に役立つDraperというgemを紹介します。
このgemを活用することで、よりクリーンで保守性の高いコードを書くことが可能となります。
目次 / Index
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で定義したほうがモデルの肥大化を防ぐ事が出来ます。
以上、参考になれば幸いです。