誰も読んでいないブログからの転載
最近MVCがどうとかという内容が話題になっていますが、ちょっと乗っかった内容です。
Railsで初心者によく見られる良くないコードは、コントローラーでたくさんの処理を実装してコントローラーの一つのアクションが30行、40行になってしまうことです。それに対して、モデルに適切に処理を移すのが良いんだということを言うんですが、”適切に”って何?じゃー、コントローラーには何を書くのがいいの?っていう質問への僕なりの回答です。
良いメソッドとは?
直接回答する前に、まずは前提の共有から、プログラムにおいて良いメソッドとはどのようなメソッドでしょうか?僕の解は、以下です。
"引数と返り値が最小限になっているメソッドです"
(この部分については別途説明が必要な気がしますが、まぁなんとなくご理解いただけるかなと思います。)
Railsのコントローラーの仕事?
では、Railsのコントローラーの仕事において、まず引数について考えてみます。コントローラーのアクションの引数ですが、一般的なrubyのメソッドの引数としては何も渡って来ません。では何を元に処理を行うかというとparamsなどのインスタンスメソッドから取得する値を元に処理を行いますね。よくアクセスするのは、以下の3つくらいでしょうか。
- params
- session
- request
RailsではコントローラーがHTTPの仕様を吸収する部分になっているので、柔軟に処理する必要があるのは仕方のないことだと思います。良いメソッドの前提からすると、引数が最小限になっていないのです。これがコントローラーを肥大化・難しくさせる原因の一つかもしれないですね。
さて、返り値はなんでしょうか?コントローラーのアクションでは以下の2つが返り値です。
- レンダリング先 render / redirect_to
- @ view とのデータのやり取りのためのインスタンス変数
まずは、レンダリング先について。どのviewを利用してレスポンスを返すのかを指定することが必要です。redirect_to もこれに含まれます。これはわかりやすいですね。
もう一つの返り値はインスタンス変数ですが、これをどう考えるかが難しいところだと思います。良いメソッドの前提からすると、返り値も最小限になるべきなのです。つまり、本当に必要なものしか設定すべきではありません。しかし、@がいっぱい設定されている悪いコードはたくさん見たことがあります。viewとの受け渡しする必要があるものが増えるということは、間違い・不具合を生みやすくなるでしょう。
まとめると、Railsのコントローラーは、params, session, requestなどを引数にとって、レンダリング先と@を設定することが仕事です。良いメソッドは、両方共最小限にする必要があるのですが、ともに複数あるので、難しいんですよねー。
良いアクションにする方法
という事を踏まえて、引数と返り値を最小限にする方法を考えていきます。良い実装にするために、工夫して減らす事が必要なんですね。
まず、メインのアクションでは、本当に扱いたいオブジェクトのみを扱うようにすべきです。paramsから、もう少しいうとparams[:user] と一つのキーだけを使うようにして、それをモデルに引き渡して、返り値を @user
に入れる。その @user
を元にどうレンダリングするか決めるという事だけにできると最小限になります。Scaffoldで出力されるコードはまさにそうなっているので、お手本だと思います。
では、例えば /posts/1/comments/4 というようなpathを組んだ時には params[:post_id] というパラメータも飛んできます。これはどのように扱うと良いかというと、 before_filter を使うとうまく処理できます。
def set_post
@post = Post.find params[:post_id]
end
という感じですね。このように、メインで扱う以外のparamsや@は別のところで処理するように工夫していくのです。こうすることで、メインのアクションが肥大化するのを防げます。
では、sessionはどうするかですが、認証系のプラグインでもよくやられているように current_user というようなヘルパーメソッドでアクセスできるようにしておくと便利です。
他によく問題になるのが、メインのオブジェクトではないが、画面上に必要となるほかのオブジェクトをどうするかです。例えば、おすすめ○○といったものかもしれません。どうすると良いかは、そのオブジェクトが何の情報から生成されるかを考えることから始めます。例えば、時間に応じて表示するのかもしれませんし、そのログインしているユーザから表示するかもしれません。
そのオブジェクトを生成するためにparamsの情報を一切利用しないというのであれば、viewから直接modelを呼んでも良いです。変にコントローラーで定義するとviewとの接続が複雑になり、コードが複雑になって不具合を生む原因になります。
paramsを元に生成される情報なのであれば、コントローラーで処理すべきことだと思います。そうすると、メインのオブジェクトともう一つのオブジェクトを一つのアクションで設定することになります。その場合は、良いメソッドの前提からすると2つのメソッドに分けてしまうというのも手だと思います。
このように、工夫することでコントローラのアクションの行数を減らすして、よりよいコードにすることができます。安易にコントローラーのアクションの行数を増やすことはやってはいけません。皆さん気をつけましょう!
まとめ
コントローラのアクションの引数は params, session, rquestで、返り値はレンダリング先と @を設定すること、それが仕事。
よいアクションのメソッドにするには、アクションでメインの操作対象としているオブジェクトの params[:user] と絞って、@は極力少なくする。
メインの操作対応以外のオブジェクトの操作は、before_filterやヘルパーメソッドを使って、処理するように工夫する。