Sourceryでログイン機能を実装してみました。
Devise
よりカスタマイズしやすそうで、自分で実装するより手軽なので結構好印象です。
フレンドリーフォワーディングが簡単に実装できるのが良いですね。
(3月12日追記)4年以上更新されていないので、system specが使えません。
##セットアップ
Gemfile
gem 'sorcery'
terminal
$ bundle install
$ rails g sorcery:install
Userモデルとマイグレーションファイル、設定ファイルなどもろもろ生成されます。
terminal
$ rails db:migrate
モデルにバリデーションを設定
model/user.rb
class User < ApplicationRecord
authenticates_with_sorcery!
validates :email, uniqueness: true, presence: true
validates :password, presence: true
end
application_controller.rb
にbefore_actionとメソッドを定義
application_controller.rb
class ApplicationController < ActionController::Base
before_action :require_login #sorceryが作成するメソッド。ログインしてない時not_authenticatedメソッドを発火する
protected
def not_authenticated
redirect_to login_url
end
end
##新規ユーザー登録
users_controller.rb
class UsersController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to root_url
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password)
end
end
new.html.erb
<%= form_with model: @user, local: true do |f| %>
<%= f.label :email, 'メールアドレス' %>
<%= f.text_field :email %>
<%= f.label :password, 'パスワード' %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, 'パスワード確認' %>
<%= f.password_field :password_confirmation %>
<%= f.submit '登録' %>
<% end %>
route.rb
resources :users, only: %i[index create]
##ログイン
login
:見つかったユーザーでログインする
logout
:ログアウトする
redirect_back_or_to
:保存されたURLがある場合そのURLに、ない場合は指定されたURLにリダイレクトする。(フレンドリーフォワーディング)
sessions_controller.rb
class UsersController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
def index
@user = User.new
end
def create
if @user = login(params[:email], params[:password])
redirect_back_or_to(hoge_url, notice: 'ログインしました')
else
flash[:alert] = 'ログイン失敗'
render :new
end
end
def destory
logout
redirect_to(root_url, notice: 'ログアウトしました')
end
end
new.html.erb
<%= form_with url: sessions_path, local: true do |f| %>
<%= f.label :email, 'メールアドレス' %>
<%= f.text_field :email %>
<%= f.label :password, 'パスワード' %>
<%= f.password_field :password %>
<%= f.submit "ログイン" %>
<% end %>
routes.rb
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
post '/logout' => 'sessions#destroy'
##公式よりメソッドを抜粋
require_login # This is a before action
login(email, password, remember_me = false)
auto_login(user) # Login without credentials
logout
logged_in? # Available in views
current_user # Available in views
redirect_back_or_to # Use when a user tries to access a page while logged out, is asked to login, and we want to return him back to the page he originally wanted
@user.external? # Users who signed up using Facebook, Twitter, etc.
@user.active_for_authentication? # Add this method to define behaviour that will prevent selected users from signing in
@user.valid_password?('secret') # Compares 'secret' with the actual user's password, returns true if they match
User.authenticates_with_sorcery!
##テスト
4年以上更新されていないgemなので、system specには対応していないようです。(3月12日時点)
###フィーチャースペック
spec/support/authentication.rb
#sessions_urlの部分をログイン画面のURLに合わせる
#password = 'login'の部分は適当な文字列に変えてもなぜか動く
module AuthenticationForFeatureRequest
def login user, password = 'login'
user.update_attributes password: password
page.driver.post sessions_url, {email: user.email, password: password}
visit root_url
end
end
spec/support/authentication.rb
RSpec.configure do |config|
# ...
config.include AuthenticationForFeatureRequest, type: :feature
# ...
end
spec/factories/users.rb
FactoryBot.define do
factory :user do
email { 'test1@test.com' }
password { 'hogehoge' }
salt { 'salt' }
crypted_password { Sorcery::CryptoProviders::BCrypt.encrypt('hogehoge', 'salt') }
end
end
spec/features/hoge_spec.rb
RSpec.feature "hoge", type: :feature do
let(:user) { create(:user) }
it 'ユーザー情報編集' do
login(user)
click_link 'マイページ'
#...
end
end
##参考