我刚刚完成了 Michael Hartl 的 Rails 教程 - 第 9 章,但我遇到了下一个问题:
- 示例数据填充器中的用户不可见?
- 管理员用户未显示“删除”链接
我的测试套件都是绿色的!
这是我的文件:
应用程序/控制器/users_controller.rb
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def index
@users = User.paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save
sign_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
def edit
end
def update
if @user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in @user
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_path, notice: "Please sign in."
end
end
def correct_user
@user = User.find(params[:id])
redirect_to(root_path) unless current_user?(@user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
应用程序/视图/用户/_user.html.erb
<li>
<%= gravatar_for user, size: 52 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete, confirm: "You sure?" %>
<% end %>
</li>
应用程序/视图/用户/index.html.erb
<%= provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<%= render @users %>
</ul>
<%= will_paginate %>
db/migrate/20130720175658_add_admin_to_users.rb
class AddAdminToUsers < ActiveRecord::Migration
def change
add_column :users, :admin, :boolean, default: false
end
end
数据库/schema.rb
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130720175658) do
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "password_digest"
t.string "remember_token"
t.boolean "admin", :default => false
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["remember_token"], :name => "index_users_on_remember_token"
end
lib/tasks/sample_data.rake
require 'faker'
namespace :db do
desc "Fill database with sample data"
task populate: :environment do
Rake::Task['db:reset'].invoke
admin = User.create!(name: "Example User",
email: "example@railstutorial.org",
password: "foobar",
password_confirmation: "foobar")
admin.toggle!(:admin)
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
规格/工厂.rb
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}@example.com"}
password "foobar"
password_confirmation "foobar"
factory :admin do
admin true
end
end
end
规格/模型/user_spec.rb
require 'spec_helper'
describe User do
before do
@user = User.new(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar")
end
subject { @user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:remember_token) }
it { should respond_to(:admin) }
it { should respond_to(:authenticate) }
it { should be_valid }
it { should_not be_admin }
describe "with admin attribute set to 'true'" do
before { @user.toggle!(:admin) }
it { should be_admin }
end
describe "when name is not present" do
before { @user.name = " " }
it { should_not be_valid }
end
describe "when email is not present" do
before { @user.email = " " }
it { should_not be_valid }
end
describe "when name is too long" do
before { @user.name = "a" * 51 }
it { should_not be_valid }
end
describe "when email format is invalid" do
invalid_addresses = %w[user@foo,com user_at_foo.org example.user@foo.]
invalid_addresses.each do |invalid_address|
before { @user.email = invalid_address }
it { should_not be_valid }
end
end
describe "when email format is valid" do
valid_addresses = %w[user@foo.com A_USER@f.b.org frst.lst@foo.jp a+b@baz.cn]
valid_addresses.each do |valid_address|
before { @user.email = valid_address }
it { should be_valid }
end
end
describe "when email address is already taken" do
before do
user_with_same_email = @user.dup
user_with_same_email.save
end
it { should_not be_valid }
end
describe "when email address is already taken" do
before do
user_with_same_email = @user.dup
user_with_same_email.email = @user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
describe "when password is not present" do
before { @user.password = @user.password_confirmation = " " }
it { should_not be_valid }
end
describe "when password doesn't match confirmation" do
before { @user.password_confirmation = "mismatch" }
it { should_not be_valid }
end
describe "when password confirmation is nil" do
before { @user.password_confirmation = nil }
it { should_not be_valid }
end
describe "with a password that's too short" do
before { @user.password = @user.password_confirmation = "a" * 5 }
it { should be_invalid }
end
describe "return value of authenticate method" do
before { @user.save }
let(:found_user) { User.find_by_email(@user.email) }
describe "with valid password" do
it { should == found_user.authenticate(@user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not == user_for_invalid_password }
specify { user_for_invalid_password.should be_false }
end
end
describe "remember token" do
before { @user.save }
its(:remember_token) { should_not be_blank }
end
end
规范/请求/authentication_pages_spec.rb
require 'spec_helper'
describe "Authentication" do
subject { page }
describe "signin page" do
before { visit signin_path }
it { should have_selector('h1', text: 'Sign in') }
it { should have_selector('title', text: 'Sign in') }
end
describe "signin" do
before { visit signin_path }
describe "with invalid information" do
before { click_button "Sign in" }
it { should have_selector('title', text: 'Sign in') }
it { should have_selector('div.alert.alert-error', text: 'Invalid') }
describe "after visiting another page" do
before { click_link "Home" }
it { should_not have_selector('div.alert.alert-error') }
end
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before do
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
it { should have_selector('title', text: user.name) }
it { should have_link('Users', href: users_path) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Settings', href: edit_user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should_not have_link('Sign in', href: signin_path) }
describe "followed by signout" do
before { click_link "Sign out" }
it { should have_link('Sign in') }
end
end
end
describe "authorization" do
describe "for non-signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "when attempting to visit a protected page" do
before do
visit edit_user_path(user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
describe "after signing in" do
it "should render the desired protected page" do
page.should have_selector('title', text: 'Edit user')
end
end
end
describe "in the Users controller" do
describe "visiting the edit page" do
before { visit edit_user_path(user) }
it { should have_selector('title', text: 'Sign in') }
end
describe "submitting to the update action" do
before { put user_path(user) }
specify { response.should redirect_to(signin_path) }
end
describe "visiting user index" do
before { visit users_path }
it { should have_selector('title', text: 'Sign in') }
end
end
end
describe "as wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
before { sign_in user }
describe "visiting Users#edit page" do
before { visit edit_user_path(wrong_user) }
it { should have_selector('title', text: full_title('')) }
end
describe "submitting a PUT request to the Users#update action" do
before { put user_path(wrong_user) }
specify { response.should redirect_to(root_path) }
end
end
describe "as non-admin user" do
let(:user) { FactoryGirl.create(:user) }
let(:non_admin) { FactoryGirl.create(:user) }
before { sign_in non_admin }
describe "submitting a DELETE request to the Users#destroy action" do
before { delete user_path(user) }
specify { response.should redirect_to(root_path) }
end
end
end
end
规范/请求/user_pages_spec.rb
require 'spec_helper'
describe "User pages" do
subject { page }
describe "index" do
before do
sign_in FactoryGirl.create(:user)
FactoryGirl.create(:user, name: "Bob", email: "bob@example.com")
FactoryGirl.create(:user, name: "Ben", email: "ben@example.com")
visit users_path
end
it { should have_selector('title', text: 'All users') }
describe "pagination" do
before(:all) { 30.times { FactoryGirl.create(:user) } }
after(:all) { User.delete_all }
it { should have_link('Next') }
it { should have_link('2') }
it "should list each user" do
User.all[0..2].each do |user|
page.should have_selector('li', text: user.name)
end
end
it { should_not have_link('delete') }
describe "as an admin user" do
let(:admin) { FactoryGirl.create(:admin) }
before do
sign_in admin
visit users_path
end
it { should have_link('delete', href: user_path(User.first)) }
it "should be able to delete another user" do
expect { click_link('delete') }.to change(User, :count).by(-1)
end
it { should_not have_link('delete', href: user_path(admin)) }
end
end
end
describe "profile page" do
let(:user) { FactoryGirl.create(:user) }
before { visit user_path(user) }
it { should have_selector('h1', text: user.name) }
it { should have_selector('title', text: user.name) }
end
describe "signup page" do
before { visit signup_path }
it { should have_selector('h1', text: 'Sign up') }
it { should have_selector('title', text: full_title('Sign up')) }
end
describe "signup" do
before { visit signup_path }
describe "with invalid information" do
it "should not create a user" do
expect { click_button "Create my account" }.not_to change(User, :count)
end
describe "error messages" do
before { click_button "Create my account" }
it { should have_selector('title', text: 'Sign up') }
it { should have_content('error') }
end
end
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
it "should create a user" do
expect do
click_button "Create my account"
end.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button "Create my account" }
let(:user) { User.find_by_email('user@example.com') }
it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
end
describe "edit" do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit edit_user_path(user)
end
describe "page" do
it { should have_selector('h1', text: "Update your profile") }
it { should have_selector('title', text: "Edit user") }
it { should have_link('change', href: 'http://gravatar.com/emails') }
end
describe "with invalid information" do
before { click_button "Save changes" }
it { should have_content('error') }
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
let(:new_name) { "New Name" }
let(:new_email) { "new@example.com" }
before do
fill_in "Name", with: new_name
fill_in "Email", with: new_email
fill_in "Password", with: user.password
fill_in "Confirm Password", with: user.password
click_button "Save changes"
end
it { should have_selector('title', text: new_name) }
it { should have_selector('div.alert.alert-success') }
it { should have_link('Sign out', :href => signout_path) }
specify { user.reload.name.should == new_name }
specify { user.reload.email.should == new_email }
end
end
end
规范/支持/实用程序.rb
def full_title(page_title)
base_title = "Ruby on Rails Tutorial Sample App"
if page_title.empty?
base_title
else
"#{base_title} | #{page_title}"
end
end
def sign_in(user)
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
# Sign in when not using Capybara as well.
cookies[:remember_token] = user.remember_token
end