最近気が付くと Model に何かと集中させてしまいがち。
このままいくと「Let’s go fat Model!」です。
解決策について書いてあるものを見つけたので、何度かに分けて試してみたいと考えています。
今回は Decorator を試します。
参考
- masato_hi のブログ - Rails で導入してよかったデザインパターンと各クラスの役割について
- Qiita - 中/大規模開発のための Rails 設計パターン
- Rails でやってしまいがちな保守性を下げてしまうコードとその解決策
- class SimpleDelegator
Decorator
調べた限り、Decorator は Model(View) へ書きがちな表示にまつわる実装を持たせるためのデザインパターンに当たるものだそう。
特定の日付の表示パターンであったり、数字のスコアを A とか B と表示させたり、姓と名を合わせたフルネームを返す実装を寄せるためのもの。
これで、表示だけにかかわる Model の実装を減らすことができる。
ただし、Rails の文脈で書かれている Decorator と、Ruby の文脈で書かれている Decorator は異なっているような記述が多いので注意すべきものだと感じます。
少なくとも Ruby の文脈で書かれている時、「表示に関連した実装を寄せる」みたいなことは関係ない。
Decorator を試す
参考にしたものを見ると、だいたい (drapper)[https://github.com/drapergem/draper] を利用しているパターンが多いようです。
とりあえず今回は使わないことにします。
UserDecorator を作る
first_name last_name ageを持つ users テーブル User モデルがすでにあるものとする。UserDecorator を実装して、full_name と generation を返すようにする。
1 | require 'delegate' |
SimpleDelegator を継承したクラスを用意する。SimpleDelegator は引数に与えたオブジェクトへメソッドの呼び出しをすべて移譲する。
UserDecorator を使う
用意した UserDecorator を使ってみる。
コントローラー
1 | class UsersController < ApplicationController |
View テンプレート
一覧を表示するindex.html.erbでは、
取得できているのはUser::ActiveRecord_Relationなので、each の中でUserDecoratorを使う。
1 | <h1>Index</h1> |
表示すると以下のようになる。
1 | Index |
一覧を表示する show.html.erb では、
コントローラで UserDecorator を使用したインスタンスを取得済みなので素直にメソッドを呼び出す。
1 | <h1>Show</h1> |
表示すると以下のようになる。
1 | Show |
それぞれ、UserDecorator で拡張したメソッドが使えた。
UserDecorator を Module で作ってみる
Module で拡張してみる。といってもただの Module の使い方というだけの気もする。UserDecoratorModuleを作る。
1 | module UserDecoratorModule |
UserDecorator を Module で使う 1
素直にモデルで include する。
1 | class User < ApplicationRecord |
使う側は User を呼び出せば、もちろん UserDecoratorModule の機能を使える。
1 | class UsersController < ApplicationController |
UserDecorator を Module で使う 2
インスタンスに extend するパターン。
1 | class UsersController < ApplicationController |
index.html.erbでは、
コントローラで取得できているのはUser::ActiveRecord_Relationなので、each の中でUserDecoratorModuleを使う。
1 | <h1>Index</h1> |
一覧を表示するshow.html.erbでは、コントローラで extend 済みなので素直にメソッドを呼び出す。
1 | <h1>Show</h1> |
draper を使ってみる
導入
Gemfile に以下を記述してインストール。
1 | gem 'draper' |
続けて以下コマンドを実行します。
1 | bundle exec rails generate draper:install |
実装
UserDecorator を Draper::Decorator の継承として準備します。
1 | class UserDecorator < Draper::Decorator |
.decorate を呼び出すと Model 名と対応したデコレーターを付与してくれる。.allの呼び出し時点(User::ActiveRecord_Relationの時点)でデコレーターでの拡張をできるのは便利。
もちろんdraperを使わなかったときみたいなUserDecorator.decorate(User.find(params[:id]))という呼び出しができる。
好みだけ言うなら後者がいい。
1 | class UsersController < ApplicationController |
今回は Decorator を試してみました。
先人たちの Fat ~~ を避けるための知恵をまだまだ吸収したいところです。
ではでは。