railstutorial.jp 8章メモ
セッションコントローラの作成
$ rails generate controller Sessions --no-test-framework
$ rails generate integration_test authentication_pages
限定したリソースのルーティング
resources :sessions, only: [:new, :create, :destroy]
match '/signin', to: 'sessions#new', via: 'get' match '/signout', to: 'sessions#destroy', via: 'delete'
Capybara 特定のHTMLセレクタ要素(とテキスト)があるかのテスト
it { should have_selector('div.alert.alert-error', text: 'Invalid') }
Capybara 特定のHTMLリンクの有無テスト
it { should have_link('Profile', href: user_path(user)) } it { should have_link('Sign out', href: signout_path) } it { should_not have_link('Sign in', href: signin_path) }
モデル指定有無によるform_forヘルパーの書き方の違い
form_for(@user) form_for(:session, url: sessions_path)
flash
メッセージの残留
リダイレクトのときとは異なり、render
で表示したテンプレートを再描画してもリクエストと見なされない
ログイン失敗後に他のページを表示してもflashメッセージが残る
flash.now
を使う
flash.now[:error] = 'Invalid email/password combination'
すべてのヘルパーは、なにもせずともすべてのビューで使用可能だがControllerで使う場合には明示的にincludeする必要がある
include SessionsHelper
Railsセッションについて
セッションごとに生成される特別のセッションidによって不一致を検出するのでユーザIDのなりすましはできない
session[:remember_token] = user.id User.find(session[:remember_token])
ブラウザにこのbase64トークンを保存しておき、データベースにはトークンを暗号化したものを保存する方法でセッションを実装する
usersテーブルにremember_tokenを追加
$ rails generate migration add_remember_token_to_users
add_column :users, :remember_token, :string add_index :users, :remember_token
$ rake db:migrate $ rake db:migrate RAILS_ENV=test
itsメソッドはitのようにsubjectを指すのではなく、引数を指すときに使用する
# 両方同じ内容 its(:remember_token) { should_not be_blank } it { expect(@user.remember_token).not_to be_blank }
itsメソッドはもうないらしい。。。
undefined method `its' for RSpec::ExampleGroups::User::RememberToken:Class (NoMethodError)
rspec-its (https://github.com/rspec/rspec-its)に分けられたらしいので追加して解決
gem 'rspec-rails' gem 'rspec-its'
コールバックはメソッド参照で。
before_create :create_remember_token
クラスメソッド
privateキーワード以降はprivateメソッド。一段インデントを下げたほうがいい。
class User < ActiveRecord::Base before_save { self.email = email.downcase } before_create :create_remember_token def User.new_remember_token SecureRandom.urlsafe_base64 end def User.encrypt(token) Digest::SHA1.hexdigest(token.to_s) end private def create_remember_token self.remember_token = User.encrypt(User.new_remember_token) end end
Railsが提供するCookieユーティリティについて
valueとexpires(オプション)のハッシュとして扱うことができる
cookies[:remember_token] = { value: remember_token, expires: 20.years.from_now.utc } # expiresは20年後、という指定がよく使われるので、permanentというメソッドが存在する # 上のコードと同じ。 cookies.permanent[:remember_token] = remember_token
なんでpermanentはメソッドなのにハッシュ風に扱うんだろうか、、、?
20.years.from_now.utc
について
timeヘルパーはRailsによって数字のFixnumクラスに追加されている
> 10.year.from_now => Sun, 13 Oct 2024 06:08:17 UTC +00:00 > 10.weeks.ago => Mon, 04 Aug 2014 06:08:20 UTC +00:00 > 1.kilobyte => 1024 > 5.megabyte => 5242880
def sign_in(user) ... self.current_user = user end # 要素代入を定義しているcurrent_user=という関数。引数はuser。 def current_user=(user) @current_user = user end def current_user # 暗号化して、usersテーブルのremember_tokenと比べる remember_token = User.encrypt(cookies[:remember_token]) # ||= ("or equals") 代入演算子。nilガード。 # 「x = x ● y」と「x ●= 」は同じ。●が+や-と同様に、||になっているだけ。 # かつ||は左辺が真だとその場で評価終了なので、それを利用して # @current_userが未定義の場合のみ、右辺がコールされて代入される。 # つまりfind_byの実行は1度のみ。 @current_user ||= User.find_by(remember_token: remember_token) end # ログインしている = @current_userがnilでない かどうか def signed_in? !current_user.nil? end def sign_out self.current_user = nil cookies.delete(:remember_token) end
ハッシュを引数として、HTTPのDELETEリクエストを送信する
<%= link_to "Sign out", signout_path, method: "delete" %>
Railsではユーザへの直接リンクが許可されるので、以下2つは同じ
<%= link_to "Profile", current_user %> <%= link_to "Profile", user_path(current_user) %>
bootstrap-sass gemが入っているので、bootstrapのjsが使える
app/assets/javascripts/application.js
//= require jquery //= require jquery_ujs //= require bootstrap //= require turbolinks //= require_tree .
cookieの削除
cookies.delete(:remember_token)
Cucumber
高度な振る舞いのテストに使う
内部的にRspecとCapybaraを使っている
Gherkinと呼ばれる言語でテストを書く
gem
gem 'cucumber-rails', '1.4.0', :require => false gem 'database_cleaner', github: 'bmabey/database_cleaner'
ディレクトリとファイルの作成
$ rails generate cucumber:install
features/signing_in.featureにテストのシナリオを書き、 features/step_definitions/authentication_steps.rb にそれをパスするステップ定義を書く
$ bundle exec cucumber features/
もしくは
$ rake cucumber
ヘルパーメソッドとカスタムマッチャの追加
spec/support/utilities.rb
include ApplicationHelper def valid_signin(user) fill_in "Email", with: user.email fill_in "Password", with: user.password click_button "Sign in" end RSpec::Matchers.define :have_error_message do |message| match do |page| expect(page).to have_selector('div.alert.alert-error', text: message) end end