最近気が付くと 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 ~~
を避けるための知恵をまだまだ吸収したいところです。
ではでは。