[coreserve] 管理画面
管理画面、つまりセッションの作成、修正、削除は、jascaffoldベースで作りました。追加した機能としては以下の通りです。
- カウンセラー以外にはアクセスできないようにした
- 新規作成だけは常時使用するので、初期値の表示等、使いやすくする工夫をした
- 一覧表示をステータス別に表示できるようにした
- セッションとクラアントの関連づけを操作できるようにした
- データのチェックの機能を追加した
この部分は、使う人間が限られているので、多少の問題があってもなんとかなるという判断で、テストを後回しにして実装してしまいました。だから、多少問題が残っているかもしれません。結果的に見ると、Railsの機能がわからなくて迷走した所もあったので、かえって時間がかかったかもしれません。
アクセス制限は、LoginEngineの機能を利用して次のように実装しました。
class ReservationController < ApplicationController before_filter :login_required, :must_be_counselor ..... private def must_be_counselor unless current_user and current_user.kind_of?(Counselor) flash[:warning] = 'カウンセラー以外にはアクセスできません' return false end end end
ステータス別一覧表示は、こんな感じです。
def list @status = (params[:status] or session[:status] or "all").to_s.intern list_param = { :per_page => 10, :order=>'start desc', } if @status == :all list_param[:conditions] = ['counselor_id = ?', current_user.id ] else list_param[:conditions] = ['counselor_id = ? and status_code = ?', current_user.id, CounselingSession::StatusToStatusCode[@status] ] end @counseling_session_pages, @counseling_sessions = paginate :counseling_sessions, list_param session[:status] = @status end
新規作成の時は、既存データの最後の次の1時間を初期値として表示するようにしました。
def new @counseling_session = CounselingSession.new newest = CounselingSession.find(:first, :order => " start desc", :conditions => ['counselor_id = ?', current_user.id ]) if newest @counseling_session.start = newest.end else @counseling_session.start = Time.now end @counseling_session.end = @counseling_session.start + 60.minutes end
セッションとクラアントの関連づけは、まず、クライアントの情報を表示するビューに、以下を追加します。
<tr> <td class="<%= localized_label_class_on(@counseling_session, 'status_code') %>"> クライアント </td> <td> <%= select("counseling_session", "client_id", Client.find_all.collect {|p| [ p.lastname, p.id ] }, :include_blank => true) %> </td> </tr>
これだけで次のようなHTMLが生成され、クライアントの名前を選択するコンボボックスが表示されます。
<select id="counseling_session_client_id" name="counseling_session[client_id]"> <option value=""></option> <option value="2">aaa</option> <option value="3">bbb</option> </select>
value="2"という所は、クライアントのIDです。あとは、これをセッションに保存しておいて、更新で利用します。
def update @counseling_session = CounselingSession.find(params[:id]) if params[:btn_cancel] @counseling_session.attributes = params[:counseling_session] render :action => 'edit' elsif @counseling_session.update_attributes(params[:counseling_session]) begin client = Client.find(session[:client_id]) if client client.counseling_sessions << @counseling_session end rescue @counseling_session.client = nil end flash[:notice] = localize(:model, 'counseling_session') + localize(:command, :successfully_updated) redirect_to :action => 'show', :id => @counseling_session else render :action => 'edit' end end
データのチェックは、モデルの validate というメソッドに実装すると、新規作成の時も修正の時も自動的にそのメソッドが呼ばれて、エラーメッセージの表示等も(jascaffold生成ロジック等が)かなり自動的に処理をしてくれます。
ここは次のような感じでテストから作成しました。
def test_validate_status # ステータスが空きなのに、クライアントが関連づけられていたらおかしい assert_raise(ActiveRecord::RecordInvalid) do @s.status = :free @s.client = @cl1 @s.save! end end
実装はこうです。
def validate unless self.counselor_id errors.add_to_base("カウンセラーIDが不正です") end if self.end == nil or self.start >= self.end errors.add_to_base("日付が不正です") end check_double_booking errors.add_to_base("不正なステータスです") unless StatusCodeToStatus[status_code] case status when :free, :not_done errors.add_to_base("このステータスではクライアントに関連づけられません") if client_id when :done, :reserved errors.add_to_base("このステータスではクライアントが必要です") unless client_id end end
validateにチェックを組み込んでおくと、不正なデータがデータベースに格納できないことは、ほぼ保証できます。それだけでは、エラーがうまく表示されなかったり、アプリケーションエラーになったりしますが、データが壊れなければ、運用しながら少しづつ対応していくこともできます。
scaffoldで枠組みを作って、validate で必要なチェックだけ(テストファーストで)実装して、後は動かしながら考えるという方法は、すごく実用的だと思います。