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 => "[email protected]") third = Factory(:user, :name => "sam", :email => "[email protected]") @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 => "[email protected]", :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 "[email protected]" 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 @usersrails 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.