%w(Akita On Rails) * 2.0 - Rolling with Rails 2.0 - The First Full Tutorialメモ

ブラジル人のFabio Akitaさんという方がRails 2.0のチュートリアルを英語で作ってくださっている。ありがとう!!

そのチュートリアルのメモ。まずは作業ディレクトリをつくった上でチュートリアルをさっそく始めてみる。

% mkdir Rails2.0
% cd Rails2.0
% rails blog

Akitaさん曰く、Railsの設定環境は全部configの下にあり、以下の3つのファイルが重要だとのこと。

  • config/environment.rb
  • config/initializers/inflections.rb
  • config/initializers/mime_types.rb

いくつかのバージョンのgemやプラグインを動かす可能性があり、これらを管理するのは混乱してしまうので、2.0ではinitializers以下のファイルがenvironments以下のファイルと同時に読み込まれるようになったらしい。そういえば、Railsの0.Xの時にはinitializersってなかったような気が。

次は、データベースの設定。config/database.ymlで行う。Akitaさん曰く、Rails2.0からKCODEが標準でtrueになっているらしい。それで、これは始めから文字コードUTF-8になっていることを意味するとのこと。なのでUTF-8を使う限りは明示的にエンコーディングを示す必要がないらしい。ただし、このエンコーディングの設定はデータベースにアクセスするときに"SET NAMES UTF8"と伝えてアクセスすることに波及するらしい。こういうのってハマるよね。

Akitaさんのチュートリアルの例だとMySQLで話がすすんでいるのだけれども、Rails2.0で標準につくられるconfig/database.ymlはSQLite3になっており、チュートリアルどおりではない。config/database.ymlの中身は以下のとおり。

# SQLite version 3.x
#   gem install sqlite3-ruby (not necessary on OS X Leopard)
development:
  adapter: sqlite3
  database: db/development.sqlite3
  timeout: 5000

# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test:
  adapter: sqlite3
  database: db/test.sqlite3
  timeout: 5000

production:
  adapter: sqlite3
  database: db/production.sqlite3
  timeout: 5000

なので、このままデータベースの作成へ進んでみる。データベースの作成はrake経由でできるみたい。

db:charset Retrieves the charset for the current environment’s database
db:collation Retrieves the collation for the current environment’s database
db:create Create the database defined in config/database.yml for the current RAILS_ENV
db:create:all Create all the local databases defined in config/database.yml
db:drop Drops the database for the current RAILS_ENV
db:drop:all Drops all the local databases defined in config/database.yml
db:reset Drops and recreates the database from db/schema.rb for the current environment.
db:rollback Rolls the schema back to the previous version. Specify the number of steps with STEP=n
db:version Retrieves the current schema version number
%w(Akita On Rails) * 2.0 - Rolling with Rails 2.0 - The First Full Tutorial - Part 1より)

% rake db:create:all
(in /home/gotoh/Ruby/Rails2.0/blog)
rake aborted!
no such file to load -- openssl

(See full trace by running task with --trace)

あれ?opensslの何か必要なの?gemで探してみる。

# gem1.8 search openssl --remote

*** REMOTE GEMS ***

jruby-openssl (0.1.1, 0.1, 0.0.4, 0.0.3, 0.0.2, 0.0.1)
JRuby-OpenSSL (0.1)

明らかに違う予感。Googleで調べてみるとどうもRubyのopensslライブラリが必要らしい。参考:dan5yaの日記:Rails 2.0.2に移行する。一応Rubyも最新版に上げておこう

# ruby -r openssl -e ""
ruby: no such file to load -- openssl (LoadError)
# aptitude search openssl | grep ruby
p   libopenssl-ruby                 - OpenSSL interface for Ruby                
p   libopenssl-ruby1.8              - OpenSSL interface for Ruby 1.8            
p   libopenssl-ruby1.9              - OpenSSL interface for Ruby 1.9 
# aptitude install libopenssl-ruby

あらためてデータベースを作成してみる。

% rake db:create:all
(in /home/gotoh/Ruby/Rails2.0/blog)
"db/development.sqlite3 already exists"
"db/production.sqlite3 already exists"
"db/test.sqlite3 already exists"

えっー。エラーは出てないけどこんな仕打ち?まあ、無視して次にいく。

チュートリアルの次の章タイトルが"Sexyness"。Akitaさんの興奮が伝わってくる。Sexynessってこういうときに使うんだ。勉強になる。一方、チュートリアルの内容はさっぱり理解できない。Rails 2.0からRESTfulになったそうだが、これに伴いscaffoldの挙動が昔と変わっているらしい。2.0のscaffoldの振る舞いは昔はscaffold_resourceと呼ばれていたものらしい。はて?とりあえず、チュートリアルどおりにscaffoldしてみる。

./script/generate scaffold Post title:string body:text

./app以下にいつもどおりのディレクトリ群ができている。チュートリアルを読み進めると、AkitaさんがSexy Migrationと絶賛するMigrationに関するファイルの説明が登場する。db/migrate/001_create_posts.rbの中身は以下のとおり。

class CreatePosts < ActiveRecord::Migration
  def self.up
    create_table :posts do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end

  def self.down
    drop_table :posts
  end
end

2005年にRailsに触れたときにはmigrationなんて使っていなかったのでこういうファイルが自動でできるのに驚き。先ほどのscaffoldコマンドの後ろのオプションはデータベースのフィールドと型を示していたのね。フィールド名titleで型はstring型、フィールド名bodyでtext型。なるほど。そして、昔ならばupdated_atとcreated_atで用意していたデータ更新時刻のフィールドがtimestampsとしてまとまっていると。あと、t.カラムの型という表現形式になったらしい。「t.string :title」という表現は、string型のtitleフィールドなのね。

次にこれをデータベースに反映させる。

% rake db:migrate
(in /home/gotoh/Ruby/Rails2.0/blog)
== 1 CreatePosts: migrating ===================================================
-- create_table(:posts)
   -> 0.0086s
== 1 CreatePosts: migrated (0.0088s) ==========================================

なるほど、これは楽だ。前の段階に戻す場合には"rake db:rollback"とすれば良いらしい。

次は、サーバを動かしてみる。

./script/server

ブラウザ越しにhttp://0.0.0.0:3000/にアクセス。ちゃんとサーバープログラムは動いているみたい。

チュートリアルの指示どおりにルートを書き換える。

ActionController::Routing::Routes.draw do |map|
  map.root :controller => 'posts' # この行を加える。
  map.resources :posts
〜 以下省略 〜

そして、public/index.htmlをリネームする。

% mv public/index.html public/old.index.html

サーバを再起動してみてみるとrootページが変わっている。

続いて、Commentを付け加える

./script/generate scaffold Comment post:references body:text
rake db:migrate

references型って何だ?って疑問に思った途端チュートリアルでAkitaさんが答えてくれる。にくい構成だ!Akitaさん曰く、references型というのは、他のテーブルのidを外部キーとして参照するための型。post:referencesというのは、モデルpostのidを参照するという意味。昔だと

./script/generate scaffold Comment post_id_:integer body:text

post_id_:intergerと書かなければならなかたっところをpost:referencesで良いようにしているそうな。確かにSexyだ。

次にapp/models/post.rbとapp/models/comment.rbの関係を明示する。エントリーに対してコメントは1対多の関係にあるので以下のようにする。

# app/models/post.rb
class Post < ActiveRecord::Base
  has_many :comments
end

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :post
end

サーバを走らせて、見てみる。RESTfulなアプリケーションならば、そうあるべき動作とこのscaffoldで作成されたアプリケーションの動作は異なるらしい。原因は、config/route.rbの設定。なので、RESTfulになるように書き換えるらしい。ここいらへんはRESTfulアプリケーションの挙動をしらないのでいまいちわからないが言われたとおりにしてみる。config/route.rbを以下のようにする。

ActionController::Routing::Routes.draw do |map|
  map.root :controller => 'posts'
  map.resources :posts, :has_many => :comments

ルーティングに二つのモデル間の関係性を書いて、それでルーティングさせるのかすごいな。

次にコントローラに手をいれる。チュートリアルどおりに進める。次ビュー。なんか0.X当時とかなり変わっているような。

さらにチュートリアルを読み進めるとrouteをどうやって決めるかの説明がある。

route HTTP verb Controller Action
comments GET index
comments POST create
comment(:id) GET show
comment(:id) PUT update
comment(:id) DELETE destroy
new_comment GET new
edit_comment(:id) GET edit

こっちの説明はどうもわからない。routeはURLを意味するだろうか?

comments_url http://localhost:3000/comments
comments_path /comments

こっちは、わかる。ソース内で左のように書いたら、右に変換されるということ。

route HTTP verb URL
post_comments(@post) GET /posts/:post_id/comments
post_comments(@post) POST /posts/:post_id/comments
post_comment(@post, :id) GET /posts/:post_id/comments/:id
post_comment(@post, :id) PUT /posts/:post_id/comments/:id
post_comment(@post, :id) DELETE /posts/:post_id/comments/:id
new_post_comment(@post) GET /posts/:post_id/comments/new
edit_post_comment(@post, :id) GET /posts/:post_id/comments/edit

これは微妙。真ん中は、HTTPの命令。右がURL。左のようにソースコードを書くと、真ん中の命令が右のURLにそれを行ったと解釈されるということ?知識が足りないのでわからん。飛ばす。

ビューをチュートリアルどおりに書き換えてコメントが投稿できるようになった。さらに、チュートリアルどおりに進んでminiblog完成。とりあえずここまで、書いて疲れてしまったので、後は省略。

チュートリアルどおりにname spaceを用いたルーティングを使って認証機能をつける。最後にメソッドauthenticateを付け加えるファイルは、app/controllers/admin/posts.rbではなくapp/controllers/admin/posts_controller.rbなので注意。

さすが、2.0にもなると変わりようが激しいね。

%w(Akita On Rails) * 2.0 - Rolling with Rails 2.0 - The First Full Tutorial - Part 2は、またあとでやることにする。