1. git checkout -b updating-users
2. in this chapter, we will make you can update user profile.
a. we will use a "edit" action to render a view to edit user.
b. we will use "update" action and a "PUT" request to update user profile
c. we need to make sure only current user can update their information. this need a "before_filter"
3. edit form, we will start from TDD again!!
describe UserController do
describe "get 'edit'" do
before(:each) do
@user = Factory(:user)
test_sign_in(@user)
end
it "should be success" do
get :edit, :id => @user
response.should be_success
end
it "should have the right title" do
get :edit, :id => @user
response.should have_selector("title", :content => "Edit user")
end
it "should have the link to gravatar" do
get :edit, :id => @user
gravatar_url = "http://gravatar.com/emails"
response.should have_selector("a", :href => gravatar_url, :content => "change")
end
end
end
4. now it is time to write the view code:
<h1>Edit User</h1>
<%= form_for @user do |f| %>
<%= render "shared/error_messages", :object => f.object %>
<div class="field">
<%= f.label :name %> <br />
<%= f.text_field :name %> <br />
<%= f.label :email %> <br />
<%= f.text_field :email %> <br />
<%= f.label :password %> <br />
<%= f.password_field :password %> <br />
<%= f.label :password_confirmation, "Confirmation" %><br />
<%= f.password_field :password_confirmation %> <br />
</div>
<div class="action">
<%= f.submit "Update"%>
</div>
<% end %>
<div>
<%= gravatar_for @user %>
<a href="http://gravatar.com/emails">Change</a>
</div>
you can find we passed a object param when rendering partial .
this is common because a real partial should not rely on the fact that there is a @user object.
this is especially useful when composing a form,
<%= render 'shared/error_messages', :object => f.object %>
this create a var called object in the partial,
now we need to re-write the _error_messages partial:
<% if object.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(object.errors.count, "error") %>
prohibited this <%= object.class.to_s.underscore.humanize.downcase %>
from being saved:</h2>
<p>There were problems with the following fields:</p>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
we can learn two helper method here:
"ActiveRecord".underscore ======> active_record (lower case and add _ )
"active_record".humanize =======> Active record (capitalize, and replace _ with space.)
5. next, let's look at the html gened by the form:
<form action="/users/1" class="edit_user" id="edit_user_1" method="post">
<input name="_method" type="hidden" value="put" />
. . . </form>
note this hidden line:
since web browser can't natively send "PUT" request, rails fake it with a post request, and a hidden input field.
6. there is another magic that you may wondering,
we use the same code for edit form and new form, but why the html generated are different?
for new action, rails use a post method, and for edit action, rails use a put method.
the answer is simple and trikey, rails will run
@user.new_record?
to judge if this record is a new one, or already exist in database.
so rails will know to use a put request or post request, clever?? cool!
7. next is the test for update success and update failure.
describe "PUT 'update'" do
before(:each) do
@user = Factory(:user)
test_sign_in(@user)
end
describe "update failure" do
before(:each) do
@attr = {:name => "", :email => "", :password => "", :password_confirmation => "" }
end
it "should render the edit page" do
put :update, :id => @user, :user => @attr
response.should render_template('edit')
end
it "should have the right title" do
put :update, :id => @user, :user => @attr
response.should have_selector("title", :content => "Edit user")
end
end
describe "update success" do
before(:each) do
@attr = { :name => "New Name", :email => "user@example.org",
:password => "barbaz", :password_confirmation => "barbaz" }
end
it "should redirect to user show page" do
put :update, :id => @user, :user => @attr
response.should redirect_to user_path(@user)
end
it "should change user's attrs" do
put :update, :id => @user, :user => @attr
@user.reload
@user.name.should == @attr[:name]
@user.email.should == @attr[:email]
end
it "should have a flash message" do
put :update, :id => @user, :user => @attr
flash[:success].should =~ /updated/i
end
end
end
one thing to note:
@user.reload ========> this will reload the @user content from database.
8. next, we will implement the update method in the controller:
def update
@user = User.find_by_id(params[:id])
if @user.update_attributes(params[:user])
flash[:success] = "Profile updated"
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end