10.3 showing users list

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是行内元素(译作内嵌元素) 

  块元素和行内元素也不是一成不变的,通过定义CSSdisplay属性值可以互相转化,如: 

    测试<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.

你可能感兴趣的:(list)