Creating microposts
在 Chapter 7 建立了表單來實現登入,表單會向 Users controller 的 create action 發送 POST 請求。建立 micropost 的功能也類似這樣,不同的地方是,表單不會放在單獨的 /microposts/new 頁面上,而是在網站的首頁(例如 root path:/),mockup 如下:

之前當我們離開首頁時,畫面會長得像這樣:

也就是會有「Sign up」按鈕顯示在中間。因為只有已登入的使用者才能建立新的 micropost,這節的目標就是要根據使用者的登入狀態來顯示不同的首頁內容。
我們要先來定義 Microposts controller 的 create action,它和 Users controller 的 create action 類似,不同的是,建立 micropost 時,要使用 user/micropost 的關聯關係來建立 micropost 物件,如下所示,注意,micropost_params 中的 strong parameters 只允許透過 web 來修改 micropost 的 content 屬性:
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
end
為了建立能產生 microposts 的表單,使用了以下的程式碼,也就是根據使用者登入狀態來顯示不同的 HTML 內容:
app/views/static_pages/home.html.erb
<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
</div>
<% else %>
<div class="center jumbotron">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="http://www.railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
</div>
<%= link_to image_tag("rails.png", alt: "Rails logo"),
'http://rubyonrails.org/' %>
<% end %>
在 if-else 的分支上使用這麼多程式碼有點雜亂,之後會使用 partial 來整理,留做練習題。
為了讓上述程式碼能夠執行,我們還要建立兩個 partial,首先是首頁的側邊欄:
app/views/shared/_user_info.html.erb
<%= link_to gravatar_for(current_user, size: 50), current_user %>
<h1><%= current_user.name %></h1>
<span><%= link_to "view my profile", current_user %></span>
<span><%= pluralize(current_user.microposts.count, "micropost") %></span>
注意,和 profile 側邊欄一樣,上述的 user info 顯示使用者的所有 microposts 數量,不過顯示上有個細微的差別是,在 profile 側邊欄,「Microposts」是標示,所以顯示「Microposts(1)」是合理的;在本例中,如果是「1 microposts」就不合語法了,所以我們調用了 pluralize 方法,顯示成「1 micropost」、「2 microposts」等。
接下來我們來建立產生 microposts 的表單,跟註冊表單有點類似:
app/views/shared/_micropost_form.html.erb
<%= form_for(@micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
為了讓上述表單能夠執行,我們要做兩件事,(和之前一樣)第一我們要透過關聯定義 @micropost:
@micropost = current_user.microposts.build
把上述程式碼加進 controller 裡:
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
@micropost = current_user.microposts.build if logged_in?
end
def help
end
def about
end
def contact
end
end
因為只有使用者登入後,current_user 才存在,所以 @micropost 變數只能在使用者登入後才能定義。
第二,要重新定義 error-message partial:
<%= render 'shared/error_messages', object: f.object %>
回想之前的 error-message partial 是直接引用了 @user 變數,但我們現在提供的變數是 @micropost。為了在這兩個地方都能使用 error-message partial,我們可以把表單變數 f 傳進 partial,透過 f.object 取得相對應的物件,所以在:
form_for(@user) do |f|
f.object 是 @user,然後在:
form_for(@micropost) do |f|
f.object 是 @micropost。
我們透過一個 hash 把物件傳進 partial,value 是這個物件,key 是 partial 需要的變數名稱,就像剛剛上面我們定義的 <%= render 'shared/error_messages', object: f.object %> 所示。
換句話說,object: f.object 會建立一個叫 object 的變數,提供給 error_message partial 使用,然後我們就可以透過這個物件,自訂 error message:
app/views/shared/_error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
現在執行測試會失敗(Red):
$ bundle exec rake test
這個失敗測試提示我們要更新其他有使用 error-message partial 的 view。包括使用者註冊、重設密碼、編輯使用者資料。以下是更新 view 的程式碼:
更新使用者註冊表單中渲染錯誤訊息的方式 app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
更新編輯使用者表單中渲染錯誤訊息的方式 app/views/users/edit.html.erb
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
<div class="gravatar_edit">
<%= gravatar_for @user %>
<a href="http://gravatar.com/emails">change</a>
</div>
</div>
</div>
更新密碼重設表單中渲染錯誤訊息的方式 app/views/password_resets/edit.html.erb
<% provide(:title, 'Reset password') %>
<h1>Password reset</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user, url: password_reset_path(params[:id])) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= hidden_field_tag :email, @user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Update password", class: "btn btn-primary" %>
<% end %>
</div>
</div>
現在執行測試就會通過了(Green):
$ bundle exec rake test
此外,本節的 HTML 都應該能正確顯示了,以下是建立 micropost 的表單:

這是顯示提交表單後有一個錯誤:

這是成功建立的訊息,原本「50 microposts」更新為「51 microposts」:
