前回の記事はこちら。
「Getting Started with Rails」をやってみた(1)

「5 Getting Up and Running Quickly with Scaffolding」から再開。

5.6 scaffoldを作ったり、リソースを作ったり。

次はscaffoldか。
さて、本家はどこまで作ってくれるんだろう。

rails generate scaffold Post name:string title:string content:text
      invoke  active_record
      create    db/migrate/20110424014627_create_posts.rb
      create    app/models/post.rb
      invoke    test_unit
      create      test/unit/post_test.rb
      create      test/fixtures/posts.yml
       route  resources :posts
      invoke  scaffold_controller
      create    app/controllers/posts_controller.rb
      invoke    erb
      create      app/views/posts
      create      app/views/posts/index.html.erb
      create      app/views/posts/edit.html.erb
      create      app/views/posts/show.html.erb
      create      app/views/posts/new.html.erb
      create      app/views/posts/_form.html.erb
      invoke    test_unit
      create      test/functional/posts_controller_test.rb
      invoke    helper
      create      app/helpers/posts_helper.rb
      invoke      test_unit
      create        test/unit/helpers/posts_helper_test.rb
      invoke  stylesheets
      create    public/stylesheets/scaffold.css

お、全部出来てるわw
凄いな、コントローラ、モデル、ビュー、テストまで一式だ。

次にデータベースの更新。

$ rake db:migrate

migrationってのがよくわかんないんだけど、すげ=便利そう。
CakePHPはこの辺弱かったし、似たようなことは出来るんだけど、そこまでみんな使ってない気がするな。
けどまあ、奥が深そうだ。

次、モデルのバリデーションか。

class Post < ActiveRecord::Base
  validates :name,  :presence => true
  validates :title, :presence => true, :length => { :minimum => 5 }
end

見た目で大体何やっているのかわかるのがすごいな。
presenceは未入力かどうかのチェックらしい。

なんかコンソールを使うとか書かれているので、そのまま入力。

$ rails console

んで、言われるがままに入力すると、同じ内容が帰ってきた。

>> p = Post.new(:content => "A new post")
=> #<Post id: nil, name: nil, title: nil,
     content: "A new post", created_at_at: nil,
     updated_at: nil>
>> p.save
=> false
>> p.errors
=> #<OrderedHash { :title=>["can't be blank",
                           "is too short (minimum is 5 characters)"],
                   :name=>["can't be blank"] }>

これすげー!
コンソール上でRails上のクラスとかメソッドが呼べるのかあ。
なんかいろいろ使えそうなんだけど、いまいち使い方が思いつかない・・・。

お、次はいよいよデータベースの内容を表示する一連の流れかな。
コントローラの中身を確認してみると、いろいろ出来てる。
んで、ビューもおんなじ様にいろいろ出来ている。

んで、レイアウトを変更するとな?

# /app/views/layouts/application.html.erb

- <body>
+ <body style="background: #EEEEEE;">

色つけただけ。
ブラウザで確認すると、確かに変わっている。
Railsだとデフォルトのレイアウトはapplication.html.erbって名前なのか。

んで、記事の投稿、編集、削除、参照という一連のCRUDの流れの説明。
ビューファイル的には殆ど変わらない投稿と編集ページが、どうやってFormのアクション先を振り分けているかは良くわからなかった。
けど、Railsがうまく隠蔽してくれているといえばそうなのか。
これはちょっとでもルールから外れるとすげー面倒くさそうかも。

んで、ざーっと読み飛ばしてw「7 Adding a Second Model」へ
コメントのモデルを作るってことね。

7.2個目のモデルを作る

$ rails generate model Comment commenter:string body:text post:references
$ rake db:migrate

post:referenceって書くだけで参照してくれるのか。
そういえばテーブルも作りもCakePHPとほぼ一緒だな。

Postモデルにも関連付けを追加。
has_manyだと。

# /app/models/post.rb

class Post < ActiveRecord::Base
  validates :name,  :presence => true
  validates :title, :presence => true, :length => { :minimum => 5 }
+ has_many :comments
end

次にroutes.rbに以下の項目を追加するらしいんだけど、これが全くさっぱりわからん。
後から分かってくるかな?

# /configroutes.rb

- resources :posts
+ resources :posts do
+   resources :comments
+ end

迷わず行けよ、行けばわかるさ。

Commentsコントローラの生成ね。

$ rails generate controller Comments

んでポストのshowビューに以下の様に変更。
コメントを投稿できるようにするっぽい。

# /app/views/posts/show.html.erb

<p class="notice"><%= notice %></p>
 
<p>
  <b>Name:</b>
  <%= @post.name %>
</p>
 
<p>
  <b>Title:</b>
  <%= @post.title %>
</p>
 
<p>
  <b>Content:</b>
  <%= @post.content %>
</p>
 
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

この中で

<%= form_for([@post, @post.comments.build]) do |f| %>

がよくわかんないけど、これでよろしくやってくれているんだろう。

んで、Commentsコントローラにもメソッド追加

def create
  @post = Post.find(params[:post_id])
  @comment = @post.comments.create(params[:comment])
  redirect_to post_path(@post)
end

createメソッドがPostsコントローラから始まっているところがみそっぽいな。
実際にフォームから送信しようとすると

/posts/2/comments

みたいなURLになっているので、大したものだなあ。
この辺はちょっとどこかで解説みないとわかんないな。

んで、再度showビューを編集して、コメントの一覧が見えるようにする。

+ <h2>Comments</h2>
+ <% @post.comments.each do |comment| %>
+   <p>
+     <b>Commenter:</b>
+     <%= comment.commenter %>
+   </p>
+  
+   <p>
+     <b>Comment:</b>
+     <%= comment.body %>
+   </p>
+ <% end %>

なんだかよくわからないことが増えてきた。
先ほどroutes.rbに追加したのは、どうやら

/posts/1/comments/1

みたいなルーティングを実現したい時に使うっぽい。
あとフォーム周りはようわからんな。
これ実際に自分でつくろうとすると、必ず躓くな。
が、ひとまず前に進もう。

8.リファクタリング

次はリファクタリング。
まさかのリファクタリング指南コーナーか。

まずはパーツ作り。

# /app/views/comments/_comment

<p>
  <b>Commenter:</b>
  <%= comment.commenter %>
</p>

<p>
  <b>Comment:</b>
  <%= comment.body %>
</p>
# /app/views/comments/_form.html.erb

<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

次に読み込む先をごにょる。

# /app/views/posts/show.html.erb

<p class="notice"><%= notice %></p>
 
<p>
  <b>Name:</b>
  <%= @post.name %>
</p>
 
<p>
  <b>Title:</b>
  <%= @post.title %>
</p>
 
<p>
  <b>Content:</b>
  <%= @post.content %>
</p>
 
<h2>Comments</h2>
<%= render :partial => "comments/comment",
           :collection => @post.comments %>
 
<h2>Add a comment:</h2>
<%= render "comments/form" %>
 
<br />
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> 

ポイントは

<%= render :partial => "comments/comment",
           :collection => @post.comments %>

<%= render "comments/form" %>

の2箇所。

やっていることは大体わかる。
ただ、collectionって指定すると、中でループを展開してくれるっぽい。
あとは、form_forメソッドで、指定しているのは、関連付けされているモデルの場合の指定方法みたいだ。
buildってメソッドもその一環らしい。
それぞれモデルクラス、formヘルパークラスの説明部分にそれっぽいことが書いてあった。
けど、英語か・・・。
細かい部分は自信ないな。
けど、この辺のヘルパーやモデルの関係もCakePHPと似ている。
より強力な感じだけども。

9.コメントの削除

次は削除処理か。

# /app/views/comments/_comment.html.erb

+ <p>
+   <%= link_to 'Destroy Comment', [comment.post, comment],
+                :confirm => 'Are you sure?',
+                :method => :delete %>
+ </p

link_toはURLヘルパーかな?
もはや何ヘルパーに属しているかさえも分からんくなっているな。
comment.postでそれぞれ:comment.post_id:idをどうやら指定しているのかな?

んー、ちと分かりづらいな。
次のコントローラ部分へのメソッドの追加をみるとそんな感じなんだけど。

# /app/controllers/comments_controller.rb

+  def destroy
+    @post = Post.find(params[:post_id])
+    @comment = @post.comments.find(params[:id])
+    @comment.destroy
+    redirect_to post_path(@post)
+  end

んで、記事が消されたらコメントも消そうというのが以下の設定。
ふむふむ。

# /app/models/post.rb

class Post < ActiveRecord::Base
  validates :name,  :presence => true
  validates :title, :presence => true, :length => { :minimum => 5 }
+ has_many :comments, :dependent => :destroy
end

dependentってのは分かりやすいわ。

長くなったので今回もここまで。
続きます。

「Getting Started with Rails」をやってみた(3)へ続く。