in this chapter, we will do user list, i.e. the index action of users controller.
at the same time, we will study pagination, and how to populate many sample data into database.
1. we will make sure sign-in user can see all users list.
non-signed-in user can only see users show page, not the all users list.
so we will write the following test to follow our lovely TDD.
describe "GET 'index'" do
describe "for non-signed-in users" do
it "should deny access" do
get :index
response.should redirect_to signin_path
flash[:notice].should =~ /sign in/i
end
end
describe "for signed-in users" do
before(:each) do
@user = test_sign_in(Factory(:user))
second = Factory(:user, :name => "bob", :email => "another@example.com")
third = Factory(:user, :name => "sam", :email => "another@example.net")
@user = [@user, second, third]
end
it "should be success" do
get :index
response.should be_success
end
it "should have the right title" do
get :index
response.should have_selector("title", :content => "All users")
end
it "should have an element for each user" do
get :index
@users.each do |user|
response.should have_selector("li", :content => user.name)
end
end
end
end
We used Factory to generate 3 user object into database.
2. here is the code of index action:
def index
@title = "All users"
@users = User.all
end
we also add :index to the before filter to make sure it need user to sign in first.
3. now, it is time to implement the index view:
<h1>All users</h1>
<ul class="users">
<% @users.each do |user|%>
<li>
<%= gravatar_for user, :size => 30 %>
<%= link_to user.name, user%>
</li>
<% end %>
</ul>
4. now we will add more sample data into database.
a. we need to add a gem to gemfile's development group.
gem 'faker', '0.3.1'
below is the code in lib/tasks
sample_data.rake
namespace :db do
desc "Fill database with sample data"
task :populate => :environment do
Rake::Task['db:reset'].invoke
User.create!(:name => "Example User",
:email => "example@railstutorial.org",
:password => "foobar",
:password_confirmation => "foobar")
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}@railstutorial.org"
password = "password"
User.create!(:name => name,
:email => email,
:password => password,
:password_confirmation => password)
end
end
end
this defines a db:populate task.
it firstly reset the dev database,
task :populate => :environment, make sure rake can use local rails env, so that it can use User.create method.
next, we can run
rake db:populate
5. pagination:
we will use the most simple and robust will_paginate gem to do this job.
add this gem to your gemfile
gem 'will_paginate', '3.0.pre2'
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<% @users.each do |user| %>
<li>
<%= gravatar_for user, :size => 30 %>
<%= link_to user.name, user %>
</li>
<% end %>
</ul>
<%= will_paginate %>
will_paginate method is miracle, it will find the @users object, then display links to other pages.
but this part of code is not working yet, as the @users is from User.all, which is an array, but will_paginate require a WillPaginate:Collection
luckily, will_paginate provide a method to get Collection object from a class name.
User.paginate(:page => 1)
this method will pull a chunk of users out from database at a time, based on page param, 30 by default,
for example, if page = 1, will pull 1-30
if page=2, will pull 31-60
...
6. next, we will test pagination:
(becaue we need to know how pagination works before testing it, so we implemented it first.)
we also need more then 30 users being populated into test database, so we will use Factory to do this.
Factory.define :user do |user|
user.name "Michael Hartl"
user.email "mhartl@example.com"
user.password "foobar"
user.password_confirmation "foobar"
end
Factory.sequence :email do |n|
"person-#{n}@example.com"
end
this make use can use
Factory(:user, :email => Factory.next(:email))
to get new emails.
require 'spec_helper'
describe "UsersController" do
render_views
describe "GET 'index'" do
.
.
.
describe "for signed-in users" do
before(:each) do
.
.
.
@users = [@user, second, third]
30.times do
@users << Factory(:user, :email => Factory.next(:email))
end
end
.
.
.
it "should have an element for each user" do
get :index
@users[0..2].each do |user|
response.should have_selector("li", :content => user.name)
end
end
it "should paginate users" do
get :index
response.should have_selector("div.pagination")
response.should have_selector("span.disabled", :content => "Previous")
response.should have_selector("a", :href => "/users?page=2",
:content => "2")
response.should have_selector("a", :href => "/users?page=2",
:content => "Next")
end
end
end
.
.
.
end
note
>> a = [1, 2, 5]
>> a << 17
>> a << 42 << 1337
so << operator to a array can be chained.
7.
the different of span and div
DIV 和 SPAN 元素最大的特点是默认都没有对元素内的对象进行任何格式化渲染。主要用于应用样式表。两者最明显的区别在于DIV是块元素,而SPAN是行内元素(也译作内嵌元素)。
块元素和行内元素也不是一成不变的,通过定义CSS的display属性值可以互相转化,如:
测试<div style="display:inline">紧跟前面的"测试"显示</div><span style="display:block">这里会另起一行显示</span>
8. partial refactoring.
as we have constitue test codes, so we are confident that our refactoring will not break the app.
9. we can use a render partial to replace the li part for displaying each user.
<%= render user %>
so rails is smart enough to deduce to put _user.html.erb partial here.
<li>
<%= gravatar_for user, :size => 30 %>
<%= link_to user.name, user %>
</li>
and further more, we can just use:
render @users
rails is so smart that he know he should iterate the element in @users, each invode the partial once. and inside the partial, you already have a user object for use there.