2007-12-11
■[技術][Ruby]Rails 2.0のscaffoldを使ってみた
Rails 2.0になって何が変わったのか俯瞰してみるには、とりあえずscaffoldを作ってコードを見てみるのがよかろう、と思ったので作ってみた。
ありがちで恐縮だが、Personモデルのscaffoldを作る。要素は名前と年齢の二つだけ。シンプル。
まず、アプリケーションの初期化を行う。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使っていればいいか。
■[技術][Subversion]CVS, Subversionのコミットログの日時が9時間ずれる件
CVSにしろSubversionにしろ、コミットログにはコミット日時を記録する。しかし、コミットログをViewVCやTracのリポジトリブラウザで、あるいはcvsコマンドを叩いて参照してみると、時間がちょうど9時間遅れている。
バージョン管理システムに慣れない人はこれを不思議に思うことが多いようで、この件で過去何度か質問を受けたことがある。
これは設定ミスでもなんでもない。9時間遅れの時間が表示されるのが正しい。
時間がずれているように見えるのは、バージョン管理システムが時間をUTC(協定世界時)で管理しているためである。これに対して、日本で使っているタイムゾーンであるJST(日本標準時)は、UTCより9時間進んでいる。よって、バージョン管理システムに時間を問い合わせると9時間遅い時間を返してくるように見えるのである。
そして、リポジトリそのもののタイムゾーンを変更する手段は無い。全てのバージョン管理システムがそうなのかはわからないが、とりあえずCVSとSubversionはそうだ。たとえリポジトリを載せているマシンのタイムゾーンを変更しても、それらには無関心にUTCを使い続ける。
これには理由がある。
オープンソースプロジェクトで使われているリポジトリを見ればわかることだが、リポジトリは世界中の開発者から利用される場合がある。ところが、地球には時差があるため、開発者たちの時計はそれぞれ異なる時間を指している。リポジトリで時間を管理する際、いったい誰の時計を基準とすべきだろうか?こうなると、全ての時間の基準となっているUTCを使うのがベストなのだ。
WinCVSやTortoiseCVSのような自分のPCにインストールして自分だけが使うツールであれば、そんな事情は気にする必要がない。だから、これらでログを参照すると、時間は我々の目に入る前にJSTに加工される。しかし、Webベースのリポジトリブラウザのように、サーバサイドで動いて不特定多数から参照される可能性があるアプリケーションではUTCを使わざるを得ないのである*1。
ちなみに、これは$Id$, $Date$, $Log$をキーワード置換する際にも問題になってくるはずである。しかし、ヘタにツールを噛ますよりは、そういうものだと受け入れた方がよろしい。単純に9時間足せば良いわけだし。もっとも、キーワード置換の使用自体オススメできないので、可能であれば無くしてしまうべき。
■[技術][Ruby]古いバージョンのRailsアプリケーションを作る
Rails 2.0のインストール後に、Rails 1.2のアプリケーションを作りたくなった場合どうしたらよいのだろう。
普通にrailsスクリプトを実行したら、最新バージョンのものが実行される。よって、これではRails 1.2のアプリケーションを作ることができない。
$ rails --version Rails 2.0.1 $
1.2と2.0の間にある隔たりは大きいので、config/environment.rbのRAILS_GEM_VERSIONを書き換えるだけでは不十分だ。
しかし、古いバージョンのgem自体は、gem cleanupしない限りは残り続けるはず。
$ gem list rails -l *** LOCAL GEMS *** rails (2.0.1, 1.2.6) $
やっぱりあるね。だから、作れるはず。
で、railsスクリプトの中身を覗いてみたところ、次のようにオプションを渡せばよいことがわかった。
$ rails _1.2.6_ --version Rails 1.2.6 $
_バージョン番号_ というオプションを渡すことで、使用するバージョンを特定できる。このオプションは必ず最初に渡さなければならないので注意。
一度初期化してしまえば、アプリケーションディレクトリの内部はそのバージョン用に作られるので、後はバージョンを意識しなくて良くなる。
$ rails new (略) $ grep RAILS_GEM_VERSION new/config/environment.rb RAILS_GEM_VERSION = '2.0.1' unless defined? RAILS_GEM_VERSION $ rails _1.2.6_ old (略) $ grep RAILS_GEM_VERSION old/config/environment.rb RAILS_GEM_VERSION = '1.2.6' unless defined? RAILS_GEM_VERSION
Rails 2.0が出たとはいえ即座に現場投入とはいかないだろうし、なによりまだ資料が少ないから、特にRails入門者はRails 1.2を使いたいはず。使い分けよう。
参考書通りにgem install rails -yと叩いたらRails 2.0が入ってしまい、扱い方が違いすぎて涙目な入門者向けに、古いバージョンのrailsをインストールする*2コマンドも書いておくか。ヘルプ見ればわかるけど。
# gem install rails -y -v 1.2.6
rubygemsのバージョンが0.9.5以上の環境では-yオプション不要。付けても今のところちょっとしたメッセージが追加で出てくるだけで害は無いが。