chapter 8.2
for this section, we will do this:
if a signup form has invalid submission, the app will re-render the signup page, show the error details.
1. we will do TDD again!!!
resources :users
this line in routes.rb prepare many REST urls for us.
for example, a POST request to /users is handled by the create action.
2. the create action is to use the form submission to make a new user object using User.new, try to save this user object, and then render the signup page for resubmission.
so we will write test to test this work flow, then write create action to Users controller to make it pass.
let's see the test code first:
require 'spec_helper'
describe UsersController do
render_views
.
.
.
describe "POST 'create'" do
describe "failure" do
before(:each) do
@attr = { :name => "", :email => "", :password => "",
:password_confirmation => "" }
end
it "should not create a user" do
lambda do
post :create, :user => @attr
end.should_not change(User, :count)
end
it "should have the right title" do
post :create, :user => @attr
response.should have_selector("title", :content => "Sign up")
end
it "should render the 'new' page" do
post :create, :user => @attr
response.should render_template('new')
end
end
end
end
a. response.should have_selector()
b. response.should render_template('new')
c. then let's learn two new tricks in the first example.
it "should not create a user" do
lambda do
post :create, :user => @attr
end.should_not change(User, :count)
end
this is to verify the failed create action doesn't create a user in the database.
---> should_not change(User, :count)
this will compare the User.count value before and after the excution of the former part of code.
----> the lambda is a Ruby construct, it will take a block, wrap the codes in the block into a package.
3. next, we will implement the create function.
def create
@user = User.new(params[:user])
if @user.save
# Handle a successful save.
else
@title = "Sign up"
render 'new'
end
end
when invoking the create action, our app will receive a params hash
params(:user) is
{:name => "", :email => "", .......}
params(:action)
params(:authenticity_token)
params(:controller)
the magic come from the name attr in the element of html form.
<input id="user_email" name="user[email]" size="30" type="text" />
note, if the submit fails, in the re-rendered page, some fields are pre-filled.
the reason is that,
form_for auto fills in the fields with the attr of @user object.
so
<input id="user_name" name="user[name]" size="30" type="text" value="Foo"/>
4. sign up error messages:
We want to display the error messages.
so we render a partial into the new.html.erb:
<%= render 'shared/error_messages' %>
so the partial lives in views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user rom being saved:</h2>
<p>There were problems with the following fields:</p>
<ul>
<%@user.errors.full_messages.each do |msg| %>
<li><%= msg%></li>
<% end %>
</ul>
</div>
<% end %>
from this part of code, we learned:
User.count---------> return the record count.
array.count -------> return the count of the array.
array.empty?
array.any?
b. we learn the helper method:
pluralize(@user.errors.count, "error")
c. we used id="error_explanation", so in custom.css file, there be an entry of #error_explanation
(#... to specify a css of a id)
and Rails automatically wraps the fields with errors in divs with the css class "field_with_errors"
so you can specify your own css by define
.field_with_errors
in your customed css file.
5. filtering params logging.
before rails 3, the password will be recorded in the log file, without being filtered.
Rails 3 changed to default to filter password.
it is set in config/application.rb
config.filter_parameters += [:password]
if you want to filter other fields, like "secret_code"
you can change this config:
config.filter_parameters += [:password, :secret_code]