URLの設計
昨日は、jascaffoldで作成したresevationコントローラをいろいろいじりながら Rails の勉強をしました。断片的な作業は進んでいるのですが、確認をしてないので、ここからテストを作って確認しながら、コントローラとビューを開発していきたいと思います。
まず、全体のURLとコントローラについてまとめてみます。このシステムでは、以下のコントローラを作成します。
- reservetion
- カウンセラーのみが操作できるコントローラ。CounselingSessionというトランザクションデータのCRUD(Create, Read, Update, Delete)を行なう。
- client
- LoginEngineのUserControllerを継承して作成したユーザ管理用のコントローラ。ClientというマスターデータのCRUDとログイン、ログアウトを行なう。
- main
- ゲスト用の表示、クライアント用の表示と予約、キャンセル等の業務処理を行なう。
- test_login
- LoginEngine等のテスト
それで、役割別の流れを言うと、最初にルートにアクセスした時には、初期画面として空き状態にあるカウンセリングセッションを一覧表示します。これはゲストにも見える情報のみで構成されていて、ゲストとして利用できるのは、この画面だけです。この画面は main/index とします。
はじめて予約しようとする人は、表示された空き時間の中から、希望する時間に対応した「予約」というリンクをクリックします。そこから自動的にログイン画面にリダイレクトされて、さらにそこで「はじめての人はユーザ情報を登録してください」というリンクをクリックして、自分のユーザ情報を登録します。この流れは、clientコントローラの中で処理されます。
登録済のユーザは、ログインするとmain/index に戻りますが、ここはゲストでなくクライアント向けの表示になっていて、空き情報に加え、自分が予約した情報も見ることができます。
ログインしたユーザは、その一覧から「予約」と「キャンセル」の操作ができます。ここは、mainコントローラの処理になります。
カウンセラーは、ログインすると、reservetionコントローラにリダイレクトされ、こちらからは、全ての操作ができます。
ざっとこんな流れで、アクション名は開発しながら考えていきます。
では、mainコントローラを作って、ルートからそこに飛ぶような設定を行なってみましょう。
$ script/generate controller main exists app/controllers/ exists app/helpers/ create app/views/main exists test/functional/ create app/controllers/main_controller.rb create test/functional/main_controller_test.rb create app/helpers/main_helper.rb
class MainController < ApplicationController def index render_text 'main' end end
これで、http://localhost:3000/mainにアクセスすると "main" という文字が表示されます。http://localhost:3000/でここに飛ぶようにする為に、config/routes.rbを変更します。
ActionController::Routing::Routes.draw do |map| map.connect ':controller/service.wsdl', :action => 'wsdl' # Install the default route as the lowest priority. map.connect ':controller/:action/:id' map.connect '', :controller => 'main', :action=>'index' # ← 追加 end
そして、public/index.htmlを削除して、http://localhost:3000/にアクセスすると、"main"が表示されました。
ついでに、デバッグ用の余計なコントラーラにアクセスしないような設定をしてみます。
ActionController::Routing::Routes.draw do |map| map.connect '', :controller => 'main', :action=> 'index' if ENV['RAILS_ENV'] == "production" # 本番モードでは指定したコントローラ以外にアクセスできないようにする map.connect ':controller/:action/:id', :requirements => { :controller => /main|client|reservation/ } else map.connect ':controller/:action/:id' end map.connect '*anything', :controller => 'main', :action => 'unknown_request' end
こうすると本番モードで起動した時には、main client reservation という3つのコントローラ以外にはアクセスできないようになります。Railsの設定ファイルはRubyスクリプトなので、こういう時に小回りがきいていいですね。それ以外のURLでアクセスされた時には、unknown_requestというアクションが起動されるので、下記のようにエラー処理用のメソッドを追加します。
class MainController < ApplicationController def index render_text 'main' end def unknown_request render_text '不正なURLです' end end
下記のように試しに本番モードで起動してから、http://localhost:3000/user/とhttp://localhost:3000/test_login/にアクセスして、エラーとなることを確認しておきます。
$ script/server -eproduction
実はここで、database.ymlの設定を忘れていてエラーとなっていたのですが、routes.rbの間違いだと思いこんで、それを直してもうまくいかず、かなりハマってしまいました。本番モードではブラウザへのエラー表示が最低限になるので、原因がわかりにくいですね。log/production.logを見てようやく気がつきました。でも、情報を出さないのはセキュリティ的な配慮だからしょうがないというか、むしろ好ましいわけで、ログを見なかった私が悪い。
それでは、ここからテストファース(風味)で作っていきますので、まず生成されたままのコントローラのテストを実行してみます。
$ ruby test/functional/main_controller_test.rb Loaded suite test/functional/main_controller_test Started . Finished in 0.020338 seconds. 1 tests, 1 assertions, 0 failures, 0 errors||<
これは、次のような当然成功するテストをやっているだけです。
def test_truth assert true end
これを次のように変更します。同時に、couseling_sessionsのfixtureも加えます。
def test_index get :index assert_response :success assert_match /free1/, @response.body assert_match /free2/, @response.body end
これはセッション情報が無いので、ゲストとしてmain/indexにアクセスしたことになります。ここには、空き情報が表示されるはずなので、fixtureで設定したデータのメモの欄にある "free1" "free2" というテキストがレスポンスには含まれているはずです。それをテストしています。
$ ruby test/functional/main_controller_test.rb Loaded suite test/functional/main_controller_test Started F Finished in 0.068964 seconds. 1) Failure: test_index(MainControllerTest) [test/functional/main_controller_test.rb:19]: <"main"> expected to be =~ </free1/>. 1 tests, 2 assertions, 1 failures, 0 errors
実行するとこのようにエラーになりました。ここから、業務の流れにそって、テストファースト(風味)で開発していこうと思います。