Railsチュートリアル9章で大事なとこをメモっていきます。
テストコードはRspecを使っています。
ランダムトークン生成
user.rb
def User.new_token
ここ!!これで22文字のランダムな文字列を作っている。
end
user.rb
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
remember_tokenをdigestすることでハッシュ化している。ハッシュ化したremember_tokenを
regember_digestカラムに入れている
end
---------------------------------------------------------------------------------------
クッキーにuser_id, remember_tokenを入れる
sessions_helper.rb
def remember(user)
user.remember *上のremember(userモデル内)
cookies.permanent.signed[:user_id] = user.id
permanentで永続化している。
signedは署名付きクッキー。cookiesをブラウザに保存する前に安全に暗号化している。セキュリ
ティー対策
idの取り出し方:cookies.signed[:user_id]で自動で暗号が解除できる
cookies.permanent[:remember_token] = user.remember_token
end
---------------------------------------------------------------------------------------
authenticateメソッド
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
newここ!
ハッシュ化されたremember_digestとハッシュ化されていないremember_tokenが合致するかを
行っている
end
ちなみに前章ではハッシュ化するメソッドを取り扱った。
digestメソッドである。
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
ここに注目!!crateである。
newとcreateの違いがあることに気がついた。
---------------------------------------------------------------------------------------
モデルとヘルパーについて
モデルに書いたり、ヘルパーに書いたり行ったり来たりしている。なぜだろうか。
UserにまつわるメソッドはUserモデルに書かなければならない。
Sessionコントローラーで使うメソッドはSession関連に書かなければならない。
Sessionモデルは存在しない。Sessionヘルパーに書く。UserモデルはSessionじゃないので書くのはおかしい。
だからモデルとヘルパーそれぞれに書かないといけない。
---------------------------------------------------------------------------------------
テストコード
sessionやcookiesはsystem specでの書き方がわからなかったので(目に見えないから)そのため今回はrequest specとhelper specも使用した。
request spec: コントローラーにまつわるテスト
helper spec: ヘルパーにまつわるテスト
まずはモデルテスト1つ追加
user_spec.rb
it "authenticated? should return false for a user with nil digest" do
expect(@user.authenticated?('')).to be_falsy
ここfalthyじゃないので注意ね。trueはtruthyだけど。
end
追加!!複数のタブでログアウト
ログインしていないとログアウトリンクが見当たらないためsystemでは書けなかった。
visitはできない。destroyはビューないため。
サポートモジュール追加した。ログインしているかどうか判断する。
is_login?メソッド。ログインしているかどうかを判断するメソッドはヘルパーのメソッドであったため、サポートモジュールに入れて使えるようにする。
module LoggedIn
def is_login?
end
sessions_spec.rb
RSpec.describe "Sessions", type: :request do
ここ!
before do
@user = FactoryBot.create(:user)
end
# let(:user) { FactoryBot.create(:user) }
it '二つのタブでログアウト' do
delete logout_path
expect(response).to redirect_to root_path
expect(is_login?).to be_falsy
ここね。
end
request spec と helper spec
サポートモジュール(ログイン用)
module LoggedIn
ここからはsystem用
def log_in(user)
visit login_path
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
check 'Remember me on this compute'
find('input[name="commit"]').click
expect(current_path).to eq(user_path(user))
end
ここからrequest用
# request
def log_in_as(user, remember:'1')
post login_path, params:{session:
{email: user.email,password: user.password,remember_me: remember}
}
end
end
system/sessions_spec.rb
requestやhelperも system内に書いちゃっておk
RSpec.describe "Sessions", type: :request do
ここ!!
before do
@user = FactoryBot.create(:user)
end
# let(:user) { FactoryBot.create(:user) }
記事にはlet使われてるけどbeforeで全然問題ない
it 'login with remembering' do
log_in_as(@user, remember:'1') サポートモジュールで定義したやつ使う
expect(cookies[:remember_token]).not_to eq nil
end
it "login without remembering" do
log_in_as(@user, remember:'0') こっちでは0にしてチェックを入れないようにしている
expect(cookies[:remember_token]).to eq nil
end
end
RSpec.describe SessionsHelper, type: :helper do
これ文字列じゃないので注意 ここでヘルパーテストするよ〜
# SessionsHelperについてテストしますよ typeはhelperで
SessionsHelperを入れてヘルパーテストをする理由は
current_userを使いたかったから。でもこれもサポートモジュールに入れればヘルパーテストじゃなくてもいけるかと思う。
before do
@user = FactoryBot.create(:user)
remember(@user)
end
it 'current_user returns right user when session is nil' do
expect(current_user).to eq @user
expect(is_login?).to be_truthy
end
it "current_user returns nil when remember digest is wrong" do
@user.update_attribute(:remember_digest, User.digest(User.new_token))
expect(current_user).not_to eq @user
expect(is_login?).to be_falsy
end
end
以上で9章終わりです!