特定データに関するscaffoldアクションの実装&基礎的なリファクタリング手法:開発現場でちゃんと使えるRails 4入門(3)(2/2 ページ)
エンタープライズ領域での採用も増えてきたRuby on Railsを使ってWebアプリケーションを作るための入門連載。最新版の4に対応しています。今回は、show、edit、update、deleteアクションを個別で自作し、できたコードをフィルターや部分テンプレートでリファクタリングしよう。
特定のデータを編集する「edit」アクション
まず編集するデータを取得しましょう。editアクションの実装は前述のshowアクションと同じです。
def edit @user = User.find(params[:id]) end
このメソッドの実行後、レンダリングされたビューの「app/views/users/edit.html.erb」がレスポンスされます。この編集のためのビューではフォームが必要になるので次のように実装します。
<h1>ユーザー編集</h1> <%= form_for(@user) do |f| %> <div class="field"> <%= f.label :name %><br> <%= f.text_field :name %> </div> <div class="field"> <%= f.label :department %><br> <%= f.text_field :department %> </div> <div class="actions"> <%= f.submit %> </div> <% end %> <%= link_to 'Back', users_path %>
このビューはnewアクションのものとほとんど同じ内容です。form_forメソッドは引数の@userが保存済みのデータであれば、それを更新するupdateアクションへのパスでHTMLタグを生成してくれるので、newアクションと同じように実装できます。
最後に、UsersControllerのeditアクションへのパスは「edit_user_path(user)」で定義できています。indexアクションのビューのtableタグに新しい列を作り、以下のeditアクションへのリンクを追加してください。
link_to '編集', edit_user_path(user)
これにより、一覧画面から編集ページにアクセスできますが、まだupdateアクションを実装していないので更新はできません。
特定のデータを更新する「update」アクション
続けて、updateアクションを実装しましょう。updateアクションは次のように実装します。こちらはcreateアクションと少し似ています。
def update @user = User.find(params[:id]) if @user.update(user_params) redirect_to user_url(@user) else render 'edit' end end
まずは、findメソッドで更新する特定のデータを取得します。
次に、if文の条件部で、そのモデルオブジェクトのupdateメソッドを使ってパラメーターを更新します。このupdateメソッドは引数のハッシュのキーと一致するパラメーターをその値で更新します。
ここでモデルオブジェクトのupdateメソッドの引数としている「user_params」は前回作ったStrongParameterです。
また、saveメソッドと同じようにupdateメソッドは保存前にバリデーションチェックを行って成功ならtrue、失敗ならfalseを返します。updateアクションでは成功時にそのモデルオブジェクトのidでshowアクションにリダイレクトし、失敗時にはeditアクションのビューをレンダリングして修正を促します。
補足:Rails 4系からは更新HTTPメソッドは「PUT」から「PATCH」に変更
余談ですが、Rails 4系からは更新に関してHTTPメソッドは従来使っていた「PUT」から「PATCH」に変更されました。
互換性のためPUTも使えるようになっており、過去のドキュメント類もPUTで説明されていますが、PATCHに変更されたということを記憶にとどめておいてください。
特定のデータを削除する「destroy」アクション
最後は特定のデータを削除するdestroyアクションです。
def destroy @user = User.find(params[:id]) @user.destroy redirect_to users_url end
まずfindメソッドで削除するデータを取得し、そのモデルオブジェクトのdestroyメソッドを呼びます。これにより、データベースからデータが削除されます。削除後はindexアクションにリダイレクトするようにします。
削除用のリンクはやや注意が必要で、次のように実装します。
link_to '削除', user_path(user), method: 'DELETE'
RESTの考え方において、データの削除はそのデータのURLに対して「DELETE」のHTTPメソッドでリクエストします。そのため上のようにlink_toメソッドにmethodオプションを渡します。
コーディングで大事な「リファクタリング」をしていこう
ここまでで、Rails標準のscaffoldと同じ機能が実装できました。データを追加・編集したり削除したりして検証してみてください。
しかし、標準scaffoldで生成された「Book」に関するコードと自作scaffoldで作成した「User」に関するコードの間には、オブジェクトの違い以外にも異なる点が見られます。また標準scaffoldでは、各所で「リファクタリング」がされています。ここからは、その手法について見ていきましょう。
リファクタリングの重要性については、以下の記事を参照してください。
Railsにおけるアクションのコールバック「フィルター」
標準scaffoldコントローラーと自作scaffoldコントローラーはアクションのメソッド名やその機能は同じですが、標準scaffoldコントローラーにはshowアクションやedit、update、destroyアクションで特定のデータをインスタンス変数に代入する処理が見られません。これは、どのアクションでも事前に行う処理であるため、アクションの前に実行される「コールバック処理」として別の箇所で定義されています。
Railsでは、このようなアクションのコールバックを「フィルター」と呼び、以下のようなメソッド呼び出しでアクションの前後とその両方に呼び出される処理を定義します。
before_action :method_name # メソッドの前に行うコールバック after_action :method_name # メソッドの後に行うコールバック arround_action :method_name # メソッドの前後に行うコールバック
いずれも第1引数に呼び出すメソッド名を与えます。それらのメソッドはプライベートメソッドとしてコントローラー中に定義します。標準scaffoldコントローラーに倣い、特定のデータをインスタンス変数に代入する処理をapp/users_controller.rbに実装しましょう。
before_action :set_user, only: %w(show edit update destroy) private def set_user @user = User.find(params[:id]) end
そして、このアクションの前に処理されるコールバックがあるため、アクションにある代入処理が不要となります。よって、それらを削除します。対象となるアクションはフィルターにonlyオプションで与えているアクションです。
共通部分を別の新しいファイルに定義する「部分テンプレート」
ビューにおいて、ユーザーを作成したり編集したりするフォームを定義するform_forメソッドは引数に渡したモデルオブジェクトがデータベースに保存済みか否かで遷移先のパラメーターを変えてくれました。
そのため、「作成」のビュー(app/views/users/new.html.erb)と「編集」のビュー(app/views/users/edit.html.erb)はフォームの部分が全く同じです。この部分を共通化する「部分テンプレート」という方法がRailsには用意されています。
部分テンプレートは共通部分を別の新しいファイル(app/views/users/_form.html.erb)に定義します。このとき、ファイル名の先頭を「_」(アンダーバー)にしてください。
<%= form_for(@user) do |f| %> <div class="field"> <%= f.label :name %><br> <%= f.text_field :name %> </div> <div class="field"> <%= f.label :department %><br> <%= f.text_field :department %> </div> <div class="actions"> <%= f.submit %> </div> <% end %>
そして「作成」のビュー(app/views/users/new.html.erb)と「編集」のビュー(app/views/users/edit.html.erb)のフォームの部分を次のメソッド呼び出しで置き換えてください。
<%= render 'form' %>
これにより、共通部分をメソッド呼び出しで置き換えることができ、仮に属性が追加された場合でも1箇所変更するだけでよくなります。
次回は、Rails 4.1の新機能について
以上で前回から2回にわたって取り組んできたscaffoldの機能の実装は完成です。お疲れさまでした。
実は、今回の自作scaffoldと標準scaffoldには、まだ「JSONをリクエストされた場合の機能」などで実装の差がありますが、それらの違いについては、今後の連載で説明していきますので、楽しみにしていてください。
次回は、本来ですとRailsのMVCモデルの「M」である「ActiveRecord」について解説する予定でしたが、ここでいったん息抜きです。4月8日に新たにリリースされたRails 4.1の新機能に迫ってみましょう。
- Railsアプリの設計をMVCごとに見直しリファクタリングして連載総まとめ
- 「設定より規約」のRailsで必要なセッティングの基礎知識と国際化/多言語対応
- ActionMailerのSMTP設定、テンプレートで送信&ActiveModelの基本的な使い方とバリデーション
- RailsテストフレームワークRSpecのインストールと基本的な使い方、基礎文法
- RailsのテンプレートエンジンSlimの書き方とActionViewのヘルパーメソッド、レイアウトの使い方
- Rails開発を面白くするアクションコントローラーの5大機能とルーティングの基本
- ActiveRecordにおけるモデルの「関連」とコールバックの使い方
- ActiveRecordの基本機能とマイグレーション、バリデーション
- 現場で使えるか見極めたいRails 4.1の新機能8選
- 特定データに関するscaffoldアクションの実装&基礎的なリファクタリング手法
- scaffoldの中身を理解するためにMVCコンポーネントと7つのアクションを個別で自作する
- 簡単インストールから始める初心者のためのRuby on Railsチュートリアル
著者プロフィール
林 慶(Rails技術者認定シルバー試験問題作成者)
平成2年大阪生まれ。2006年から高専で情報工学を学んでいたが当時は所謂プログラミングができない工学生だった。卒業後、高専の専攻科に上がったもののマンネリ化したキャンパスライフに飽きたため休学して渡豪。そこでプログラミングに対するコンプレックスを克服するためにRuby on Railsなどでアプリケーションを作ることを覚える。
帰国後から現在までは復学し推薦システムに関する研究を行いながら、アジャイルウェアでRuby on Railsアプリケーションの開発業務に従事している。
好きなメソッドはinject。
監修者プロフィール
山根 剛司(Ruby業務開発歴7年)
兵庫県生まれ。1997年からベンチャー系のパッケージベンダーで10年間勤務。当時、使用していた言語はJavaとサーバーサイドJavaScript。
2007年よりITコンサル会社に転職し、Rubyと出会って衝撃を受ける。基幹システムをRuby on Railsで置き換えるプロジェクトに従事。それ以来Ruby一筋で、Ruby on Railsのプラグインやgemも開発。
2013年より、株式会社アジャイルウェアに所属。アジャイルな手法で、Ruby on Railsを使って企業向けシステムを構築する業務に従事。
Ruby関西所属。好きなメソッドはtap。
Twitter:@spring_kuma、Facebook:山根 剛司
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- Railsで目指せ、情熱エンジニア(10):コントローラとモデルをリファクタリングする
前回用意したRailsアプリのコントローラのテスト(スペック)をもとに、今回はコントローラとモデルのリファクタリングの実例を紹介します。 - Railsコードリーディング〜scaffoldのその先へ〜
- 資格試験は転職に役立つか:Rails技術認定試験がスタート、合格者に聞いた
- @IT自分戦略研究所 資格辞典:Rails技術者認定試験(Rails 3 Certified Programmer)