Ruby on Rails Tutorial (15)
継続は 9.2.3 ってことで良いのかどうか。
9.2.3 Friendly forwarding
むむ、以下な試験が追加とのこと。
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
signin したら edit なナニに遷移、なのか。
遷移云々なヘルパメソドを app/helpers/sessions_helper.rb に定義とのこと。
def redirect_back_or(default) redirect_to(session[:return_to] || default) session.delete(:return_to) end def store_location session[:return_to] = request.url end
で、この store_location を users_controller.rb で云々。
class UsersController < ApplicationController before_filter :signed_in_user, only: [:edit, :update] before_filter :correct_user, only: [:edit, :update]
before_filter 追加してその手続きも定義。
private def signed_in_user unless signed_in? store_location redirect_to signin_url, notice: "Please sign in." end end def correct_user @user = User.find(params[:id]) redirect_to(root_path) unless current_user?(@user) end
edit アクションも空にするのかどうか。edit は既に空ですね。あ、追加ではなくて修正でした。とほほ。修正は signed_in_user メソドでした。あとは sessions_controller.rb の create アクションに redirect_back_or を仕込むのか。
def create user = User.find_by_email(params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) sign_in user redirect_back_or user else flash.now[:error] = 'Invalid email/password combination' render 'new' end end
redirect_to を redirect_back_or に置き換え。試験 green 確認。
9.3 Showing all users
index なアクション追加とのこと。なかなかに先は長い。のでどんどんスルーで進めてしまうかどうか。
9.3.1 User index
とりあえず signin してないユーザが Users controller にアクセスしたら sign in な画面に遷移してね、な試験が追加となっています。
describe "for non-signed-in users" do . . . describe "in the Users controller" do . . . describe "visiting the user index" do before { visit users_path } it { should have_selector('title', text: 'Sign in') } end end
で、Users controller に index なアクション追加か。ちなみに空になってます。で、user_pages_spec.rb に index アクションな試験追加。
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') } it { should have_selector('h1', text: 'All users') } it "should list each user" do User.all.each do |user| page.should have_selector('li', text: user.name) end end end
ユーザ一覧がリスト表示、な確認になってますね。Users controller の index は User.all なソレを、な形に修正。before_filter も修正か。
class UsersController < ApplicationController before_filter :signed_in_user, only: [:index, :edit, :update]
index アクションが以下。
def index @users = User.all end
view も用意。
app/views/users/index.html.erb
<% provide(:title, 'All users') %> <h1>All users</h1> <ul class="users"> <% @users.each do |user| %> <li> <%= gravatar_for user, size: 52 %> <%= link_to user.name, user %> </li> <% end %> </ul>
新規作成でした。あと custom.css.scss も情報追加。で、authentication_pages_spec.rb の with valid information な試験について
describe "with valid information" do let(:user) { FactoryGirl.create(:user) } before { sign_in user } it { should have_selector('title', text: user.name) } i { 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) }
- before の中身を修正
- Users なリンクの確認追加
してます。で、ヘッダも修正して試験実行なのか。
<header class="navbar navbar-fixed-top navbar-inverse"> <div class="navbar-inner"> <div class="container"> <%= link_to "sample app", root_path, id: "logo" %> <nav> <ul class="nav pull-right"> <li><%= link_to "Home", root_path %></li> <li><%= link_to "Help", help_path %></li> <% if signed_in? %> <li><%= link_to "Users", users_path %></li>
Uses なリンク修正ですね。つうか試験 red だorz
- rspec ./spec/requests/authentication_pages_spec.rb:69 # Authentication authorization for non-signed-in users in the Users controller visiting the user index
- rspec ./spec/requests/user_pages_spec.rb:15 # User pages index
- rspec ./spec/requests/user_pages_spec.rb:18 # User pages index should list each user
- rspec ./spec/requests/user_pages_spec.rb:16 # User pages index
諸々確認。つうかおかしいな。最後の三つはパスしないといけないはずなんですが。
と思ったら gravatar_for の引数が云々と言われてますね。以下にナニがありますね。
そのまま盛り込み。これで user_pages_spec.rb な試験は green です。もう一つの authentication_pages.rb 方面ですが users_controller.rb に盛り込み漏れあり。
before_filter :signed_in_user, only: [:index, :edit, :update]
を忘れておりました。これで試験 green かな。ボリューム多くてうっかりな漏れがありますね。とほほ杉。
9.3.2 Sample users
Faker という Gem を追加とのこと。なんか凄そなナニですが。
source 'https://rubygems.org' gem 'rails', '3.2.11' gem 'bootstrap-sass', '2.1' gem 'bcrypt-ruby', '3.0.1' gem 'faker', '1.0.1'
盛り込んで bundle install しておきます。次に lib/tasks というディレクトリに sample user を云々するソレを投入する模様。
lib/tasks/sample_data.rake
namespace :db do desc "Fill database with sample data" task populate: :environment do 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
で、以下の操作で 100 個の sample user が、とあります。
$ bundle exec rake db:reset $ bundle exec rake db:populate $ bundle exec rake db:test:prepare
ヤッてみます。test なソレでないと、なのかどうか。
$ RAILS_ENV=test ./script/rails c Rack::File headers parameter replaces cache_control after Rack 1.5. Loading test environment (Rails 3.2.11) irb(main):001:0> User.all.count User Load (0.1ms) SELECT "users".* FROM "users" => 0
あら?
$ ls db -l total 60 -rw-r--r-- 1 rms rms 34816 Jan 27 11:55 development.sqlite3 drwxrwxr-x 2 rms rms 4096 Jan 25 16:40 migrate -rw-r--r-- 1 rms rms 0 Jan 21 15:40 production.sqlite3 -rw-rw-r-- 1 rms rms 1224 Jan 26 08:24 schema.rb -rw-rw-r-- 1 rms rms 343 Jan 16 08:16 seeds.rb -rw-r--r-- 1 rms rms 7168 Jan 27 11:56 test.sqlite3
development なのか。
$ RAILS_ENV=development ./script/rails c Loading development environment (Rails 3.2.11) irb(main):001:0> User.all.count User Load (0.9ms) SELECT "users".* FROM "users" => 100
成程。
9.3.3 Pagination
Gemfile に以下を追加とのこと。
source 'https://rubygems.org' gem 'rails', '3.2.11' gem 'bootstrap-sass', '2.1' gem 'bcrypt-ruby', '3.0.1' gem 'faker', '1.0.1' gem 'will_paginate', '3.0.3' gem 'bootstrap-will_paginate', '0.0.6'
で、bundle install しておきます。どんどん盛り込みます。まず spec/factories.rb が以下とのこと。
FactoryGirl.define do factory :user do sequence(:name) { |n| "Person #{n}" } sequence(:email) { |n| "person_#{n}@example.com"} password "foobar" password_confirmation "foobar" end end
ちなみに修正前は以下。
FactoryGirl.define do factory :user do |u| u.name { "Michael Hartl" } u.email { "michael@example.com" } u.password { "foobar" } u.password_confirmation { "foobar" } end end
確認は別途、ということにて。次に spec/requests/user_pages_spec.rb が以下とのこと。
require 'spec_helper' describe "User pages" do subject { page } describe "index" do let(:user) { FactoryGirl.create(:user) } before(:each) do sign_in user visit users_path end it { should have_selector('title', text: 'All users') } it { should have_selector('h1', text: 'All users') } describe "pagination" do before(:all) { 30.times { FactoryGirl.create(:user) } } after(:all) { User.delete_all } it { should have_selector('div.pagination') } it "should list each user" do User.paginate(page: 1).each do |user| page.should have_selector('li', text: user.name) end end end end
相当ざくっと修正ですね。つうことは app/views/users/index.html.erb も修正か。
<% provide(:title, 'All users') %> <h1>All users</h1> <%= will_paginate %> <ul class="users"> <% @users.each do |user| %> <li> <%= gravatar_for user, size: 52 %> <%= link_to user.name, user %> </li> <% end %> </ul> <%= will_paginate %>
ざくっと置換。controller も修正。app/controllers/users_controller.rb を以下に云々。index アクションですね。
def index @users = User.paginate(page: params[:page]) end
paginate というメソド呼び出しか。これで試験実行して green 確認しております。
9.3.4 Partial refactoring
refactor とは言え、作法を知らんとアレですよね。とりあえず順に盛り込んでみます。
まず li なソレを云々とあります。app/views/users/index.html.erb をとのこと。
<% provide(:title, 'All users') %> <h1>All users</h1> <%= will_paginate %> <ul class="users"> <% @users.each do |user| %> <%= render user %> <% end %> </ul> <%= will_paginate %>
修正対象は以下なあたりなのか。
<li> <%= gravatar_for user, size: 52 %> <%= link_to user.name, user %> </li>
あ、partial にするんだ。app/views/users/_user.html.erb 追加とのこと。
<li> <%= gravatar_for user, size: 52 %> <%= link_to user.name, user %> </li>
しかもこんな書き方ができるってアレ。
<% provide(:title, 'All users') %> <h1>All users</h1> <%= will_paginate %> <ul class="users"> <%= render @users %> </ul> <%= will_paginate %>
盛り込んで動作確認してみます。これはすばらなナニですね。
9.4 Deleting users
REST な action として未実装なのが destroy とのこと。とりあえず権限持ってるユーザのみがこの操作を云々できること、らしい。当り前ですね。
9.4.1 Administrative users
admin? なナニが云々なのかどうか。とりあえず spec/models/user_spec.rb を以下にとのこと。
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 do @user.save! @user.toggle!(:admin) end it { should be_admin } end
あら、なんとなく直感的に微妙なんですが気のせい?
とりあえず現状の実装を見るに
- admin というカラムが追加なのか
- be_admin て何だろ
- デフォでは admin ではない、って意図かな
- おそらく admin な role は新規なので上記の試験が追加されたと類推
とりあえず盛り込み完了。あと migration もナニですね。
$ rails generate migration add_admin_to_users admin:boolean invoke active_record create db/migrate/20130127103529_add_admin_to_users.rb
で、生成されたソレを以下に。
class AddAdminToUsers < ActiveRecord::Migration def change add_column :users, :admin, :boolean, default: false end end
で、rake db:migrate 実行。
$ bundle exec rake db:migrate $ bundle exec rake db:test:prepare
なんか試験 green のためのナニが云々とあるんですがとりあえず実行したんですがパスしたな。微妙だけどスルーしよう。あと以下を云々とのこと。
lib/tasks/sample_data.rake
namespace :db do desc "Fill database with sample data" task populate: :environment do admin = User.create!(name: "Example User", email: "example@railstutorial.org", password: "foobar", password_confirmation: "foobar") admin.toggle!(:admin)
で、以下を実行。
$ bundle exec rake db:reset $ bundle exec rake db:populate $ bundle exec rake db:test:prepare
とりあえず toggle!(:admin) はスルーで次に。
9.4.2 The destroy action
final step とか書いてあってアレ。とりあえず spec/factories.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
で、delete なソレを云々する試験を追加なのか。
spec/requests/user_pages_spec.rb
require 'spec_helper' describe "User pages" do subject { page } describe "index" do let(:user) { FactoryGirl.create(:user) } before do sign_in user visit users_path end it { should have_selector('title', text: 'All users') } it { should have_selector('h1', text: 'All users') } ||< あ、ここは同じですね。before の部分が違いましたね。 あるいは以下? >|| describe "delete links" do 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
ええと、app/views/users/_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, data: { confirm: "You sure?" } %> <% end %> </li>
条件分岐が追加なのか。admin なら削除なリンクが云々。あとは controller 側で
before_filter :signed_in_user, only: [:index, :edit, :update]
を
before_filter :signed_in_user, only: [:index, :edit, :update, :destroy]
にしとくのか。あ、あと destroy なアクションにも手を入れるのかな。手を入れるというか追加でした。
def destroy User.find(params[:id]).destroy flash[:success] = "User destroyed." redirect_to users_url end
User.find().destroy なナニが云々とか。あと試験に admin ではないソレが云々とのこと。
spec/requests/authentication_pages_spec.rb
describe "Authentication" do . . . describe "authorization" do . . . 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
う、最後のソレが分かりづらい。スルーしすぎ感満点。
Users controller に以下を、とのこと。
class UsersController < ApplicationController before_filter :signed_in_user, only: [:index, :edit, :update, :destroy] before_filter :correct_user, only: [:edit, :update] before_filter :admin_user, only: :destroy
admin_user 手続きが以下。
def admin_user redirect_to(root_path) unless current_user.admin? end
うーん、きちんと読めてない感満点なカンジですね。とりあえず試験実行して green なのは確認できました。
と思ったら
以下なナニ。
1) User pages delete links as an admin user Failure/Error: it { should have_link('delete', href: user_path(User.first)) } expected link "delete" to return something # ./spec/requests/user_pages_spec.rb:140:in `block (4 levels) in <top (required)>' 2) User pages delete links as an admin user should be able to delete another user Failure/Error: expect { click_link('delete') }.to change(User, :count).by(-1) Capybara::ElementNotFound: no link with title, id or text 'delete' found # (eval):2:in `click_link' # ./spec/requests/user_pages_spec.rb:142:in `block (5 levels) in <top (required)>' # ./spec/requests/user_pages_spec.rb:142:in `block (4 levels) in <top (required)>'
ちょっとアタマを冷やす方向にて。