では早速投稿の部分を見ていく。

まずはControllerから。
app/controllers/posts_controller.rb
def new
@post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @post }
end
end
@post に空のPostを作っている。
続いてView。
app/views/posts/new.html.erb
<h1>New post</h1>
<% form_for(@post) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :content %><br />
<%= f.text_area :content %>
</p>
<p>
<%= f.submit "Create" %>
</p>
<% end %>
<%= link_to 'Back', posts_path %>
form_for(@post) というのが、フォームを形成する部分だろう。
ここから最後のendまでが1ブロックになっている。
これが Closure と呼ばれるRubyの得意分野。
Closure の中では、@post が f に代入される。
f.label :name の部分が、ラベルの定義。
画面上には "Name" と表示されるわけだが、これがどこで定義されてるのかも謎。
f.text_field は、INPUT TEXTタグの定義。
f.text_area は、TEXTAREAタグの定義。
f.submit は、INPUT SUBMITタグの定義。
まぁこの辺りは大体わかるだろう。
ここで少し考えてみる。f は @post だから、Postクラスのインスタンスである。
だから、f.label なんていう表現が出来ることに若干の違和感を感じる。
Postクラスがlabelメソッドを持っているとは到底思えない。
おそらくだが、これは Closure の機能。
form_for Closure の中でだけ、f.label という記述が有効になる。
f というインスタンスにフォーム関連のメソッドを拡張した。
これは mix-in などと呼ばれる機能。つまりDI(Dependency Injection) である。
最後に link_to で、トップページへのリンクを作成している。
では、何か一つ投稿してみよう。


無事、投稿に成功。
ついでにデータベースの中身も見てみる。

では次に Controller を見てみよう。
新規登録画面のソースを見ると、formタグは以下のように定義してある。
<form action="/posts" class="new_post" id="new_post" method="post">
つまり、投稿時のURLは "/posts" だ。
これだと Controller.index に処理が移りそうな気がするが
実際には Controller.create になる。
app/controllers/posts_controller.rb
# POST /posts
# POST /posts.xml
def create
@post = Post.new(params[:post])
respond_to do |format|
if @post.save
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to(@post) }
format.xml { render :xml => @post, :status => :created, :location => @post }
else
format.html { render :action => "new" }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
end
end
end
コメントにもあるが、POSTだと create に処理が移る。
これはRailsの慣習らしいので、覚えておくとよい。
では、Controller を見ていく。
@post = Post.new(params[:post]) で、画面から入力された値を元に @post を生成する。
…と簡単に言っているが、実は色々なことをやっている。
少し見てみよう。
まず、HTMLフォームは以下のように生成されている。
<form action="/posts" method="post"> <input id="post_name" name="post[name]" size="30" type="text" /> <input id="post_title" name="post[title]" size="30" type="text" /> <textarea cols="40" id="post_content" name="post[content]" rows="20"></textarea> <input id="post_submit" name="commit" type="submit" value="Create" /> </form>
post[XXX]がポイント。
これで Post.new(params[:post]) とすることによって
以下のような Post インスタンスが生成される。
Post
name = '画面で入力されたname' title = '画面で入力されたtitle' content = '画面で入力されたcontent'
Ruby は動的言語なので、name や title といったフィールド(インスタンス変数)は
事前の定義が無くてもいきなり作成できる。その証拠に、Post Model を見てみよう。
app/models/post.rb
class Post < ActiveRecord::Base end
フィールド定義は何もない。
定義が無いということは記述量が少なくて良い面でもあるが
一つの記述ミス(今回ならばHTML側)によって簡単にシステムが動かなくなってしまうという
危険性もある。
この危険を減らす最大のポイントは「自動生成」。
今回の例ならば、script/generate scaffold Post name:string とした段階で
新規入力画面のHTML(post[name])は自動生成されている。
これならば、入力ミスをすることは無い。
@post.save で、@post をDBにINSERTする。
ここで成功すれば真が返る。
とりあえず今回は成功したと仮定して処理を追っていく。
flash[:notice] = 'Post was successfully created.'
これは、投稿成功画面に表示されるメッセージ。
flash というのは特殊な変数なのだろう。
最後に画面遷移。redirect_to(@post) ということは、リダイレクトか。
実際、投稿後のURLを見ると以下のようになっている。
http://localhost:3000/posts/1
つまり、今投稿したメッセージの表示画面ということだ。
Controller は以下。
app/controllers/posts_controller.rb
# GET /posts/1
# GET /posts/1.xml
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @post }
end
end
これらのマッピング "/posts/1" => show はどこでも定義されていない為
Railsのデフォルトマッピングなのだろう。
おそらく親クラスである ApplicationController に書いてあるのだろうが、それはまた今度。
show がやっていることはただ一つ。引数で指定されたidから
投稿されたPostを引っぱってきて画面に表示するだけ。
Viewは以下のようになっている。
app/views/posts/show.html.erb
<p> <b>Name:</b> <%=h @post.name %> </p> <p> <b>Title:</b> <%=h @post.title %> </p> <p> <b>Content:</b> <%=h @post.content %> </p> <%= link_to 'Edit', edit_post_path(@post) %> | <%= link_to 'Back', posts_path %>
h というのはおそらくメソッドなのだが、どこで定義されているかは不明。
とりあえず今は無視しておく。
さて、notice はどこに行ったのだろう。
ここで、親Viewの登場。
app/views/layouts/posts.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Posts: <%= controller.action_name %></title>
<%= stylesheet_link_tag 'scaffold' %>
</head>
<body>
<p style="color: green"><%= flash[:notice] %></p>
<%= yield %>
</body>
</html>
ほら、あった。
ここの yield の部分に、show.html.erb の中身が展開される。
yield は、Closure を呼び出す文法。ここでも Closure が使われていたのだ。
ここでトップページに戻ってみる。

今投稿した内容が表示されている。
ここで、先ほど放っておいた部分を見てみよう。
app/views/posts/index.html.erb
<% for post in @posts %>
<tr>
<td><%=h post.name %></td>
<td><%=h post.title %></td>
<td><%=h post.content %></td>
<td><%= link_to 'Show', post %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
@posts には投稿のリストが格納されている。
特に目新しいことはやっていない。投稿の内容とリンクを表示しているだけ。
Destroy の部分が何やら変わっているが、これは後で見ることにしよう。