Ruby on Rails2.1やってみた。

rubyが大分使えるようになってきたので、Ruby on Railsにチャレンジしてみた。
vmware上にCentOSが入っているので、そちらに環境を作ってみた。

準備

Railsのダウンロードページにいくと、手順が表示されています。
http://www.rubyonrails.org/down


まずはrubyを。これは大丈夫。次にrubyのパッケージ管理ツールであるRubyGems。これはまだ準備してなかったのでCentOS上でfirefoxを起動し、RubyForge(http://rubyforge.org/frs/?group_id=126)よりrubygems-1.1.1.tgzをダウンロード。解凍後setup.rbを実行する。

  tar xvzf rubygems-1.1.1.tgz
  ruby rubygems-1.1.1/setup.rb

次はRails。このコマンドでインストールできる。

  gem install rails --include-dependencies

ついでにRailsでよく使われるのがMySQLらしいので、mysqlも入れてみた。

  yum install mysql-server

早速MySQLにアクセスしようとすると、こんなエラーが出ました。

  ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)

これはMySQL自体が起動していないことが理由でした。サービスを起動しておきましょう。

  /etc/rc.d/init.d/mysqld start

アプリケーションの作成

まずはアプリケーションを作ります。アプリケーションルートとなる名前を決めたら次のコマンドを実行します。

  rails アプリケーション名 -d mysql

アプリケーション名に指定したディレクトリが出来ました。
次にDBの準備。

  mysql -u root

MySQLにログインし、データベースを作成。データベースは3種類作成する必要があるようです。「本番用(production)」「開発用(development)」「テスト用(test)」のそれぞれについて、以下のコマンドを実行します。

  create database アプリケーション名_データベース種類 default character set utf8;

■追記(2008 6/17)
rake db:create:allを実行するとconfig/database.ymlの内容で3点セットを自動生成してくれるみたいです。


次は実際のモデルを作成します。
Railsを使ってちょっとしたプロジェクト支援ツールを作ろうと思っているので、とりあえずProjectクラスとSubProjectクラスを定義します。この2つの関連としてはProjectがSubProjectを複数保持するような1対多。

  • Projectクラスの情報として、プロジェクト名、プロジェクトの説明、開始日、終了日
  • SubProjectクラスの情報として、プロジェクト名、プロジェクトの説明、プロジェクトID(外部キー)

これらの定義に従って生成するには以下のようにします。

  ruby script/generator scaffold project name:string description:text from:date to:date
  ruby script/generator scaffold sub_project name:string description:text project_id:integer

実はこれでmodelクラスだけでなく、controllerクラスやCRUDの画面、DB定義まで出来てしまうのです。では早速DBにテーブルを作成してみます。

  rake db:migrate

最後にProjectとSubProjectの関連が1対多になるようにProjectクラスの定義を編集します。自動生成されたapp/model/project.rbを開くと以下のようになっているので、

class Project < ActiveRecord::Base
end

定義を追加しておきます。

class Project < ActiveRecord::Base
  has_many :sub_projects
end

動作確認

それではサーバーを起動してみます。

  ruby script/server

javaでいうtomcatですかね、RailsをインストールするとWEBRickというサーバーが利用できるようになります。3000ポートで起動するらしいので「ttp://localhost:3000/projects」でアクセスしてみます。

こんな画面が表示されます。今はまだデータが何もありません。「New Project」リンクをクリックしてデータを登録します。型が日付だと入力がプルダウンになるんですねぇ。

適当に入力項目を埋めて「Create」を実行し、一覧画面に戻ります。すると先ほど登録したレコードが表示されています。

他にもこのレコードを編集する「Edit」や削除する「Destroy」があります。簡単なCRUD操作がノンプログラミングで実現できます。

テスト実施

先ほどのgenerator実行時にログが出力されるのですが、その中に興味深いものを発見しました。

      create    test/unit/project_test.rb
      create    test/fixtures/projects.yml

どうやらRailsはテストケースまでテンプレートとして自動生成してくれるようです。では早速project_test.rbを見てみると、

require 'test_helper'

class ProjectTest < ActiveSupport::TestCase
  # Replace this with your real tests.
  def test_truth
    assert true
  end
end

となっていました。
次にprojects.ymlを見てみると、

one:
  name: MyString
  description: MyText
  from: 2008-06-15
  to: 2008-06-15

two:
  name: MyString
  description: MyText
  from: 2008-06-15
  to: 2008-06-15

となっており、テストデータまで用意してくれています。同様にsub_projects.ymlもテストデータが用意されています。

one:
  name: MyString
  description: MyText
  project_id: 1

two:
  name: MyString
  description: MyText
  project_id: 1

テストを実行する前に、まずはテストデータを少し書き換えます。idが自動で振られるとSubProjectの方との1対多の関係がテストしづらいのでProjectのデータのidをとりあえず1と2にします。あとプロジェクト名もせっかくなので区別がつくように編集しました。

one:
  id: 1
  name: MyString1
  description: MyText
  from: 2008-06-15
  to: 2008-06-15

two:
  id: 2
  name: MyString2
  description: MyText
  from: 2008-06-15
  to: 2008-06-15

次にテストケースにちゃんとしたテストを追加します。

class ProjectTest < ActiveSupport::TestCase
  def test_find
    projects = Project.find(:all)

    assert_equal(2, projects.size)
    assert_equal("MyString1", projects[0].name)
    assert_equal(2, projects[0].sub_projects.size)
    assert_equal(0, projects[1].sub_projects.size)
  end
end

JUnitなどを知っている人であれば大体分かると思います。
定義したProjectのクラスメソッドであるfindメソッドを実行し、全件取得します。テストデータは2件なので、レコード数は2が返ることが期待されます。その後1件目を取得し、名前が「MyString1」であることをチェックします。最後に、ProjectとSubProjectの関連を調べます。0番目のものはSubProjectオブジェクトを2つ持つようにデータを編集しましたが、1番目のものは何もデータを設定していないので0のはずです。
ではテストを実行します。

  rake

これで全テストが実行されます。
このテストだけ実行したい場合は次のようにします。

  ruby unit/project_test.rb

これを実行すると次のように表示されました。うまくいったようです♪

Loaded suite unit/project_test
Started
.
Finished in 0.178636 seconds.

1 tests, 4 assertions, 0 failures, 0 errors

JavaだとDBUnitというツールで同様のことが実現できますが、最初からある程度テストデータまで作ってくれているというのは驚きです。


あと、irbと同じ感覚で使えるscript/consoleがあります。

  ruby script/console

とすると、対話モードになるので、

p1 = Project.new(:name=>"サンプルプロジェクト")
p1.save

と入力すると、実際にDBにレコードがinsertされます。
なお、ここでinsertされる先のdatabaseはdevelopment環境です。サーバーを起動してブラウザ上で作成したデータもdevelopmentにレコードがinsertされていました。テストで利用するdatabaseはtestとなるようです。


とりあえず今日はここまで。