Getting Started With Rails

本家のGetting Startedってのを真似て進めてみました。
以下はその際のメモ書きです。

railsアプリ作成

rails blog -d mysql
cd blog

デフォルトデータベースはSQLite3。この設定をMySQLで生成させる指定をつける。

DB作成

database.yml編集。

development:
  adapter: mysql
  encoding: utf8
  database: blog_development
  pool: 5
  username: root
  password:
  socket: /var/lib/mysql/mysql.sock

実行。

rake db:create

generate

script/generate controller home index

デフォルトルートの変更

config/route.rbに以下行を追加。

  map.root :controller => "home"

これでHomeControllerのindexアクションがデフォルトになる。

Scaffolding

scaffoldingが出てきた。

>script/generate scaffold Post name:string title:string content:text
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/posts
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      exists  public/stylesheets/
      create  app/views/posts/index.html.erb
      create  app/views/posts/show.html.erb
      create  app/views/posts/new.html.erb
      create  app/views/posts/edit.html.erb
      create  app/views/layouts/posts.html.erb
      create  public/stylesheets/scaffold.css
      create  app/controllers/posts_controller.rb
      create  test/functional/posts_controller_test.rb
      create  app/helpers/posts_helper.rb
       route  map.resources :posts
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/post.rb
      create    test/unit/post_test.rb
      create    test/fixtures/posts.yml
      create    db/migrate
      create    db/migrate/20090115171317_create_posts.rb

特に追加インストールなどせずに使えている?
とりあえず先に進む。

>rake db:migrate
(in /home/qnzm/tmp/blog)
==  CreatePosts: migrating ====================================================
-- create_table(:posts)
   -> 0.0074s
==  CreatePosts: migrated (0.0140s) ===========================================

db/migrate配下にpostsテーブル作成用のクラスが作成されていてそれが適用された。

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

      t.timestamps
    end
  end

  def self.down
    drop_table :posts
  end
end

引数指定したフィールドがデフォルトで設定されているのかな。

Postモデルに検証を追加

class Post < ActiveRecord::Base
  validates_presence_of :name, :title
  validates_length_of :title, :minimum => 5
end

readline

script/consoleの実行にはreadlineのインストールが必要でした。

sudo yum install readline-devel
sudo ruby extconf.rb
make
sudo make install

script/console

コンソールからモデルクラス作っていじって反応みたり、できるらしい。

>script/console
Loading development environment (Rails 2.2.2)
>> p = Post.create(:content => "A new post")
=> #<Post id: nil, name: nil, title: nil, content: "A new post", created_at: nil, updated_at: nil>
>> p.save
=> false
>> p.errors
=> #<ActiveRecord::Errors:0xb7d97bd4 @errors={"name"=>["can't be blank"], "title"=>["can't be blank", "is too short (minimum is 5\
 characters)"]}, @base=#<Post id: nil, name: nil, title: nil, content: "A new post", created_at: nil, updated_at: nil>>
>>

パーシャル

「新規作成」と「編集」で重複してしまうフォームの形式をパーシャルなテンプレートとして別にしておき、render指定で取り込むことができる。
_form.html.erbとして、

<% form_for(@post) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :title, "title" %><br />
    <%= f.text_field :title %>
  </p>
  <p>
    <%= f.label :content %><br />
    <%= f.text_area :content %>
  </p>
  <p>
    <%= f.submit "Save" %>
  </p>
<% end %>

を作っておいて、

<h1>Editing post</h1>

<%= render :partial => "form" %>

<%= link_to 'Show', @post %> |
<%= link_to 'Back', posts_path %>

Commentモデルの導入

script/generate model Comment commenter:string body:text post:references

このmodelの指定は分かりやすい。同時に項目の構成も指定しているわけか。

>rake db:migrate
(in /home/qnzm/tmp/blog)
==  CreateComments: migrating =================================================
-- create_table(:comments)
   -> 0.0465s
==  CreateComments: migrated (0.0576s) ========================================

commentsテーブルができました。
ルーティングやDDL文なんかもRubyソースコードで表現されていて、Rubyの世界で開発しきるという感じ。

Adding a Route

map.resources :posts do |post|
  post.resources :comments
end

Commentページの作成

@commentsと@commentを間違えたりしてエラー吐いた。それらを調整して動くように仕向けていく中、結果として全体の振る舞いを見ていっていた気持ち。
comment_controller.rb内。

  def new
    @post = Post.find(params[:post_id])
    @comment = @post.comments.build
  end

buildで新しいCommentオブジェクトは作られるけど、このタイミングではまだデータベースに保存されていない、新たなオブジェクトができただけ、とか。

class Comment < ActiveRecord::Base
  belongs_to :post
end
class Post < ActiveRecord::Base
  has_many :comments
end

で関連を表していたり、とか。(最初、Postクラスに上記一行抜けていたため、「commentsフィールドなんて知りません」とエラーが出てた)

link_toに指定する"post_comment_path"

    <td><%= link_to 'Show', post_comment_path(@post, comment) %></td>
    <td><%= link_to 'Edit', edit_post_comment_path(@post, comment) %></td>

viewのところどころで指定した"post_comment_path"といったアクション。
これがどう展開されるのかというと、どうも

map.resources :posts do |post|
  post.resources :comments
end

を指定した段階で、コントローラアクションにindex/create/show/update/destroy/new/editを使用することになっていて、それらに対応するアクションメソッドが自動的に利用可能になるみたい。

post_comment_pathはshowアクションに関連付いて、edit_post_comment_pathはその名の通りeditアクションに関連付く。
commentはpostに対する従属モデルだから、名前もネストした形で表現されている。routes.rbの表現もそれを表している。
引数がある/ない、単数形/複数形のアクション等等…ちょっと自分の頭でまだ整理できていない感じ。すとんと落ちてない。

ぼんやりと考える

慣れればほいほいと作業できそうではあるけれど、確かにテーブルにベッタリで全体が駆動しているってイメージ。
この動作がチュートリアルとして説明されるということは、「このやり方をとってる限り開発効率は高い(サクサク作れる)」ということかな。
逆に、ルーティング方法とテーブル構成が切れているような場合は、工夫というかもっとRailsの仕組みを知る必要がありそうというか。
データベースのスキーマがそのままアプリケーションに直結していればしているほど、作りやすそうな印象。