Rails 2.0のscaffoldを使ってみた

Ruby on Rails 2.0になって何が変わったのか俯瞰してみるには、とりあえずscaffoldを作ってコードを見てみるのがよかろう、と思ったので作ってみた。
ありがちで恐縮だが、Personモデルのscaffoldを作る。要素は名前(name)と年齢(age)の二つだけ。シンプル。

まず、アプリケーションの初期化を行う。DBはお手軽に扱いたいのでsqlite3を使うことにした。これだとconfig/database.ymlの編集も不要なので楽。

$ rails trial -d sqlite3
(略)
$ cd trial

で、Rails 1.2であれば、まずはmigrationファイルを作ってDBにmigrateし、その後でおもむろにscaffold生成を行うところである。しかし、Rails 2.0ではここでいきなりscaffoldの生成を始める。

$ ruby script/generate scaffold person name:string age:integer
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/people
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      create  app/views/people/index.html.erb
      create  app/views/people/show.html.erb
      create  app/views/people/new.html.erb
      create  app/views/people/edit.html.erb
      create  app/views/layouts/people.html.erb
      create  public/stylesheets/scaffold.css
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/person.rb
      create    test/unit/person_test.rb
      create    test/fixtures/people.yml
      create    db/migrate
      create    db/migrate/001_create_people.rb
      create  app/controllers/people_controller.rb
      create  test/functional/people_controller_test.rb
      create  app/helpers/people_helper.rb
       route  map.resources :people
$

出力を見ると、migrationファイルが一緒に生成されているのがわかる。Rails 2.0ではこいつもscaffoldジェネレータが面倒を見ることになったのだ。ついでに、テンプレートファイルの名称がXXX.rhtmlからXXX.html.erbに変更されているのも見てとれる。

そして、オプションとして与えているname:stringとage:integerに注目。従来はscaffoldジェネレータが勝手にDBのスキーマ情報を参照して表示・編集するフィールドを定めていたが、今回はスキーマを作る前にジェネレータを使うので、それができない。従って、modelのどのフィールドを対象とするのか、プログラマが明示的に指定する必要がある。これを指定しないと、何も表示せず何も編集できない退屈なscaffoldが作られてしまう。また、migrationファイルもこのパラメータに従って作られている。

class CreatePeople < ActiveRecord::Migration
  def self.up
    create_table :people do |t|
      t.string :name
      t.integer :age

      t.timestamps
    end
  end

  def self.down
    drop_table :people
  end
end

migrationファイルも新スタイルであるが、まぁどう書けばよいかは見た目から察することができると思う。見てのとおり実に単純な設定なので、NOT NULL制約やらサイズ制限やらを足したい場合は手を加えるべし。今回はこのまま使うことにする。

そして、migrate。

$ rake db:migrate
 (in /home/idesaku/temp/trial)
 == 1 CreatePeople: migrating ==================================================
 -- create_table(:people)
    -> 0.0372s
 == 1 CreatePeople: migrated (0.0441s) =========================================

$

ちなみに、従来通り先にmodelとmigrationファイルの生成を行ってからscaffoldを作ろうとすると、「すでに同名のmigrationファイルがある」といったエラーが出てscaffoldの生成処理が停止する。--forceオプションを加えても上書きしない(されても困るが)。この場合、--no-migrationオプションを加えて実行すること。

あとはこれまでと変わらない。

実際にページを表示したときにレコード数ゼロだと寂しいので、適当に足しておこうか。本当はmigrationファイルにレコード追加処理も書いておくべきなのだろうが、面倒なので今回はscript/consoleを使ってちゃっちゃと突っ込んでしまう。

$ ruby script/console
Loading development environment (Rails 2.0.1)
>> 10.times do
?> Person.create(:name => "Miku Hatsune", :age => 16)
>> end
=> 10
>> quit
$

そして、サーバ起動。

$ ruby script/server

で、ブラウザでhttp://localhost:3000/peopleにアクセス。

でてきた。どうでもいいがみっくみくである。

ところでpaginationが標準から外れてプラグイン化したわけだが、Rails 1.2のscaffoldではpaginationを利用していたはずだ。Rails 2.0ではどうしているのであろう?と思い調べてみたのだが・・・単にpaginationを使わないようになっていた。

New personもちゃんと表示される。

generate時にname:stringやらを渡したが、あれをやっておかないと、ここが空っぽになる。

ちなみに、http://localhost:3000/people.xmlにアクセスすると・・・。

一覧表示していた内容がXML形式で送られてくる。REST対応の一環である。

このへん、コントローラではこんな書き方をしている。

  # GET /people
  # GET /people.xml
  def index
    @people = Person.find(:all)

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @people }
    end
  end

Rails 1.2のそれにXMLのレスポンスを追加してあるのだが、これがRails 2.0のスタイルってわけである。Rails 2.0はRESTfulなのだ。

とりあえずここまで。

Rails 1.2の頃と比べて手順とお作法にいくらかの違いがあるものの、使い勝手はおおむね変わらない。むしろ、modelがscaffoldジェネレータの管理下に入ったのは、手順が一本化されてすっきりとして良い感じだ。ただ、Rails 1.2向けに作られた既存の入門書通りに操作してもいろいろトラブるわけで、Rails入門者は涙目かもしれんな。まぁそういう人々はしばらくは1.2使っていればいいか。