2.0のscaffoldから書き直してみる
Rails2.0.2環境にして試行錯誤中。前回までにRails1.2.6の既存プロジェクトを2.0.2環境で稼働出来るようにはなった。しかし、プロジェクトを新規作成してから始めようとすると、また勝手が違っていることに気付く。ここは基本に返って、scaffoldから同じプロジェクトtest_slip202を作り直してみることに。
作業環境
プロジェクト作成〜サーバー起動
- Rails2.0.2環境版、1アクションで複数のモデルを同時に保存プロジェクトtest_slip202を新規作成した。
$ rails test_slip202 $ cd test_slip202 $ script/server => Booting Mongrel (use 'script/server webrick' to force WEBrick) => Rails application starting on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server ** Starting Mongrel listening at 0.0.0.0:3000 ** Starting Rails with development environment... ** Rails loaded. ** Loading any Rails specific GemPlugins ** Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart). ** Rails signals registered. HUP => reload (without restart). It might not work well. ** Mongrel 1.1.3 available at 0.0.0.0:3000 ** Use CTRL-C to stop.
- 素早くMongrelサーバーが起動して、http://0.0.0.0:3000 にアクセス可能な状態になる。(WEBrickよりサクサク反応して快適!)
- About your application’s environmentをクリックして、稼働環境を確認してみる。
Welcome aboard You’re riding the Rails! About your application’s environment Ruby version 1.8.6 (universal-darwin9.0) RubyGems version 1.0.1 Rails version 2.0.2 Active Record version 2.0.2 Action Pack version 2.0.2 Active Resource version 2.0.2 Action Mailer version 2.0.2 Active Support version 2.0.2 Application root /Users/zari/railsapp/test_slip202 Environment development Database adapter sqlite3
- デフォルトのデーターベースはsqlite3に変更になったようだ。
scaffoldを実行する
- 以前のscaffoldと違って、モデル名に続けて「フィールド名:データ形式」の書式でオプション指定しておく必要がある。
$ script/generate scaffold slip number:integer executed_on:string total_yen:integer
- scaffoldのオプション指定の手間は増えるが、下記の通りマイグレーションファイルが自動生成されるので、結果として効率はさらに上がった。
scaffoldで何が起こったか?
- Rails2.0以前の動作
- 上記に加えて、Rails2.0から追加された動作
Rails2.0から追加された動作1、2、についてもう少し詳しく見てみた。
1.マイグレーションファイルの追加
- scaffold後は、以下のようなマイグレーションファイルが自動生成されている。
# マイグレーション: app/db/migrate/001_create_slips.rb class CreateSlips < ActiveRecord::Migration def self.up create_table :slips do |t| t.integer :number t.string :executed_on t.integer :total_yen t.timestamps end end def self.down drop_table :slips end end
- マイグレーションファイルの書式もちょっと変更されている。
# マイグレーション: app/db/migrate/001_create_slips.rb class CreateSlips < ActiveRecord::Migration def self.up create_table :slips do |t| t.integer :number, :total_yen t.string :executed_on ...(中略)...
-
- t.timestampsとうい書き方は何?...それは、以下と同等らしい。
# マイグレーション: app/db/migrate/001_create_slips.rb class CreateSlips < ActiveRecord::Migration def self.up create_table :slips do |t| t.datetime :created_at, :updated_at # または... t.datetime :created_on, :updated_on ...(中略)...
2.routesファイルへの追記
- scaffold後、config/routes.rbにはオレンジ色の部分が追記された。
ActionController::Routing::Routes.draw do |map| map.resources :slips ...(中略)... map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end
マイグレーションの実行
$ rake db:migrate
(in /Users/bebe/railsapp/test_slip202)
== 1 CreateSlips: migrating ===================================================
-- create_table(:slips)
-> 0.0041s
== 1 CreateSlips: migrated (0.0043s) ==========================================
URLの変化
- ところが、アクションに対するURLが変化していた。
アクション | Rails2.0のURL | Rails2.0のメソッド | 以前のURL | 以前のメソッド |
---|---|---|---|---|
index | http://0.0.0.0:3000/slips | GET | http://0.0.0.0:3000/slips | GET |
new | http://0.0.0.0:3000/slips/new | GET | http://0.0.0.0:3000/slips/new | GET |
create | http://0.0.0.0:3000/slips | POST | http://0.0.0.0:3000/slips/create | POST |
show | http://0.0.0.0:3000/slips/1 | GET | http://0.0.0.0:3000/slips/show/1 | GET |
edit | http://0.0.0.0:3000/slips/1/edit | GET | http://0.0.0.0:3000/slips/edit/1 | GET |
update | http://0.0.0.0:3000/slips/1 | PUT | http://0.0.0.0:3000/slips/update/1 | POST |
destroy | http://0.0.0.0:3000/slips/1 | DELETE | http://0.0.0.0:3000/slips/destroy/1 | POST |
- URLだけ見ると、以前のURLの方が分かり易いと思った。
- Rails2.0からは、リクエストメソッドGET、POSTに加え、PUT、DELETEも利用するようになった。URLとGET、POST、PUT、DELETEメソッドを組み合わせて、コントローラーのアクションを呼び出す。(以前のURLとGET、POSTで処理する方法も引き続き利用できる。)
生成されたコード
コントローラー
- indexアクションを例に見てみると、respond_to do〜endブロックが追記されている。
class SlipsController < ApplicationController # GET /slips # GET /slips.xml def index @slips = Slip.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @slips } end end ...(中略)...
- 上記のように、基本的にすべてのアクションにrespond_to do〜endブロックが追記された*1。
- respond_to do〜endブロックでは、要求されるレスポンスに応じた描画方法で処理し、その結果を返してくれる。
- この場合は、htmlかxmlで結果が返ってくる。(拡張子.xmlが付加されたhttp://0.0.0.0:3000/slips.xmlだとxmlで描画される。)
- もし、xmlなんか興味ない、htmlだけで十分、という場合は以下のようにしても良いのではないだろうか。
# コントローラー: app/controllers/slips_controller.rb class SlipsController < ApplicationController def index @slips = Slip.find(:all) end def show @slip = Slip.find(params[:id]) end def new @slip = Slip.new end def edit @slip = Slip.find(params[:id]) end def create @slip = Slip.new(params[:slip]) if @slip.save flash[:notice] = 'Slip was successfully created.' redirect_to(@slip) else render :action => "new" end end def update @slip = Slip.find(params[:id]) if @slip.update_attributes(params[:slip]) flash[:notice] = 'Slip was successfully updated.' redirect_to(@slip) else render :action => "edit" end end def destroy @slip = Slip.find(params[:id]) @slip.destroy redirect_to(slips_url) end end
- このようにして見ると、コントローラーでの以前との違いはredirect_toの書き方だけのようだ。
redirect_to(@slip) | redirect_to :action=>'show', :id=>@slip と同等 |
redirect_to(slips_url) | redirect_to :action=>'index' と同等 |
- 上記のシンプルな書き方を可能にしているのが、routesファイルに追記されたmap.resources :slipsの1行。試しにコメントアウトしてみると、オブジェクトそのもの、またはsllips_urlのような書式の部分は全部エラーになる。(ビューファイルも含めて)
ビュー
- 拡張子.rhtmlは.html.erbに変更された。(.rhtmlも引き続き利用可能)
- コントローラー、アクション、idを指定するあらゆる箇所で、上記コントローラーと同じ記法が利用されている。その部分だけ抜粋してみた。
<%= link_to 'Show', slip %> <%= link_to 'Edit', edit_slip_path(slip) %> <%= link_to 'Destroy', slip, :confirm => 'Are you sure?', :method => :delete %> <%= link_to 'New slip', new_slip_path %> <% form_for(@slip) do |f| %> <%= link_to 'Back', slips_path %>
- form_for(@slip) do |f|ブロックの中では、f.submit、f.label、f.error_messsage_on、f.error_messagesのような書き方も追加された。
map.resources :slipsは何をしているのか?
- rails2.0ではrake routesで、現在のルーティング定義を確認できる。実行してみると...
$ rake routes
(in /Users/zari/railsapp/test_slip202)
...(ここから)...
slips GET /slips {:controller=>"slips", :action=>"index"}
formatted_slips GET /slips.:format {:controller=>"slips", :action=>"index"}
POST /slips {:controller=>"slips", :action=>"create"}
POST /slips.:format {:controller=>"slips", :action=>"create"}
new_slip GET /slips/new {:controller=>"slips", :action=>"new"}
formatted_new_slip GET /slips/new.:format {:controller=>"slips", :action=>"new"}
edit_slip GET /slips/:id/edit {:controller=>"slips", :action=>"edit"}
formatted_edit_slip GET /slips/:id/edit.:format {:controller=>"slips", :action=>"edit"}
slip GET /slips/:id {:controller=>"slips", :action=>"show"}
formatted_slip GET /slips/:id.:format {:controller=>"slips", :action=>"show"}
PUT /slips/:id {:controller=>"slips", :action=>"update"}
PUT /slips/:id.:format {:controller=>"slips", :action=>"update"}
DELETE /slips/:id {:controller=>"slips", :action=>"destroy"}
DELETE /slips/:id.:format {:controller=>"slips", :action=>"destroy"}
...(ここまで)...
/:controller/:action/:id
/:controller/:action/:id.:format
- 結果を見ると、map.resources :slipsによって、...(ここから)......(ここまで)...の範囲が定義されているようだ。
- respond_toに対応するため、「.:format」を付加したものが必ずペアで存在している。(これがslips.xmlのような書式を可能にしていると思う。)
- 書式は以下のようになっている。(以下「.:format」を除いた一覧)
名前付きルート | HTTPメソッド | URLフォーマット | コントローラー、アクション |
---|---|---|---|
slips | GET | /slips | {:controller=>"slips", :action=>"index"} |
POST | /slips | {:controller=>"slips", :action=>"create"} | |
new_slip | GET | /slips/new | {:controller=>"slips", :action=>"new"} |
edit_slip | GET | /slips/:id/edit | {:controller=>"slips", :action=>"edit"} |
slip | GET | /slips/:id | {:controller=>"slips", :action=>"show"} |
PUT | /slips/:id | {:controller=>"slips", :action=>"update"} | |
DELETE | /slips/:id | {:controller=>"slips", :action=>"destroy"} |
- 上記定義によって、素晴らしくシンプルなurl指定を可能にしているようだ。
map.resources :slipsが提供する仕組みは、今までダラダラ書いていたurl指定を素晴らしいシンプルさで表現できるようになった。しかし、その代わりに、その仕組みは見えにくくなったと感じている。(たぶん、自分がルート定義に慣れていないからだろう...。)ルート定義については、今までほとんど活用していなかった...。Rails2.0を便利に使うためには、もう少し勉強が必要だ。
*1:newアクションにはあり、editアクションには無いのが気になるが...。なぜ?