【Rails】sorcery - require_login

内容

Ruby on Railsgem 'sorcery'
ライブラリーメソッドrequire_loginについて

ソースコード追跡

def require_login
  return if logged_in? 
  # ① ログインしている場合は処理中止(Guard節)

  if Config.save_return_to_url && request.get? && !request.xhr? && !request.format.json? 
  # ② request
    session[:return_to_url] = request.url
    # ③ sessionのreturn_to_urlキーにリクエストURLを格納
  end

  send(Config.not_authenticated_action)
  # ④ selfを省略している?
  # ⑤ not_authenticated_actionについて
end

① return ifによるGuard節

logged_in?がtrueの場合、
require_loginメソッドは処理されません

② request

request
Sorceryのソースコードの中のどこにも定義されていないと思いきや
Railsでデフォルトで定義されていました

③sessionのrequest_to_urlキーにリクエストURLを格納する

session[:request_to_url]がいつの間に出てきましたが、
sessionのキーの名前は自由につけれるんですね

④ selfを省略している?

self.send(Config.not_authenticated_action)

の略だと思いますが、
selfの要否の判別がいまいちできていないので、よくわかりません(= =;)

⑤ not_authenticated_actionについて

send(Config.not_authenticated_action)

のsendメソッドの引数Config.not_authenticated_actionについては
Sorcery::Controller::Configに書いてあります。

module Sorcery
  module Controller
    module Config
      class << self
        ...
        # what controller action to call for non-authenticated users.
        attr_accessor :not_authenticated_action
        ...
        
        def init!
          @defaults = {
            ...
            :@not_authenticated_action             => :not_authenticated,
            ...
          }
        end

@defaultsのキー@not_authenticated_actionの値として
:not_authenticatedが定義されています。

つまり、
require_loginの中身のsend(Config.not_authenticated_action)
実行されたとき、
Module: Sorcery::Controller::InstanceMethods#not_authenticatedが実行されます。

def not_authenticated
  redirect_to root_path
end

まとめ

Ruby on Railsgem 'sorcery'で使うことのできる
require_loginメソッドについて
・ログインしていないユーザーに対して発火します
・require_loginメソッドが発火した後、not_authenticatedメソッドが発火します。


・内部処理でsession[:request_to_url]として
 リクエストしたURLが保存してあるので、
 not_authenticatedでログインしていないとして弾かれた後も
 ログインした後、リクエストした後のURLへ遷移させる
 というような実装もできそうですね

Tips

require_loginメソッドが発火した後の処理として
not_authenticatedメソッド以外を指定することも可能です。

【Ruby】return if によるガード節

内容

Ruby, Ruby on Railsで使われる return if について

経緯

Module: Sorcery::Controller::InstanceMethods#require_login
return ifが使われており、調べてみたのでメモ・備忘録として

return ifはガード節

ガード(Guard)節とは、
不要な処理は予め判定し、後続に進ませない(ガードする)
ものです。

実際のコードで使用されている例

例として、
Module: Sorcery::Controller::InstanceMethods#require_login
を見てみましょう

def require_login
  return if logged_in?
  ...
end

require_loginメソッドでは
logged_inがtrueの場合は処理を中止します
という意味のガード節が使われています。

参考

Module: Sorcery::Controller::InstanceMethods — Documentation for sorcery (0.16.1)
【Ruby】乱用厳禁!?後置ifで書くとかえって読みづらくなるケース - Qiita
プログラム初心者脱却?ガード節って何?|ダルオ|note

【Rails】devise - パスワードなしでアカウント情報編集できるようにカスタマイズ

内容

Ruby on Railsgem 'devise'について

deviseの登録・認証を使用するモデルレコードのカラムデータの更新を
パスワードなしでできるようにカスタマイズする方法

前提

前提① : Userモデルレコードにdeviseの登録・認証機能を使用

$ rails g devise User

前提② : deviseのレコード登録コントローラのルーティング

Rails.application.routes.draw do
  devise_for :users,
    controllers: { 
      registrations: 'registrations'
    }
end

デフォルトの設定

devise本家のアカウント情報編集のデフォルト設定では
リソースのカラム更新には、パスワードを必要としています。(→ 詳細

カスタマイズ方法

リソース更新時に発火する Devise::RegistrationsController#update
で使用されている
update_resourceメソッド をオーバライドします。

update_resourceメソッドをオーバーライド

class RegistrationsController < Devise::RegistrationsController
  def update_resource(resource, params)
    resource.update_without_current_password(params)
    # update_without_current_passwordメソッドは自分で新たに作成するメソッド
  end
end

update_without_current_passwordメソッドを新たに定義する

class User < ApplicationRecord
  def update_without_current_password(params, *options)

    if params[:password].blank? && params[:password_confirmation].blank?
      params.delete(:password)
      params.delete(:password_confirmation)
    end
 # if条件はpasswordとpassword_confirmationの入力フォームが
  # 空の場合のみに処理するのを明示するため

    result = update_attributes(params, *options)
    clean_up_passwords
    result
  end
end
注意点

devise本家で定義されている
update_without_passwordメソッドを使ってオーバーライドするだけでは、
自身でパスワード変更できなくなってしまう
ので、
自分でメソッドを作成する必要があります。

上記の例では
update_without_current_passwordメソッドという名前で作成しています。

devise本家のupdate_without_passwordメソッドのソースコードからも、
フォームから送られるparamsからpasswordとpassword_confirmationを削除して
リソースを更新するのがupdate_without_passwordメソッドのため、
入力フォームにパスワード・パスワード確認を入力しても
内部処理でparamsから除外されてしまうため
パスワードを更新することができません。

リソース編集画面からpassword/password_confirmation入力フォームを削除

編集画面から
・password入力フォーム
・password_confirmation入力フォーム
を削除し、
先ほど新たに作成し定義したupdate_without_current_passwordメソッドの
「password/password_confirmationが空の場合のみ処理する」という
if条件を常に成り立つようにします

参考

heartcombo/devise: Flexible authentication solution for Rails with Warden.
Devise でユーザーがパスワードなしでアカウント情報を変更するのを許可 | EasyRamble
【Rails】deviseでユーザー編集機能を現在のパスワードなしで更新してみる。|Yuya Abe|note

【Rails】devise - deviseのデフォルト設定(リソース更新時にパスワードが必要)

内容

devise本家では
deviseの登録・認証を使用するモデルレコードのカラム更新にはパスワードを必要としています。

これをdevise本家のソースコードから追ってみます。

ソースコード追跡

Devise::RegistrationsController#update

アカウント更新時に発火するアクションです。
中身を見てみましょう

class RegistrationsController < Devise::RegistrationsController
  def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)

    resource_updated = update_resource(resource, account_update_params)  
    # レコード更新
  
    yield resource if block_given?
    ...
end

update_resourcesメソッドの返り値を
更新完了のリソース resources_updated へ格納しています。

次はupdate_resourcesについて見てみます。

Devise::RegistrationsController#update_resources

class RegistrationsController < Devise::RegistrationsController
 def update_resource(resource, params)
    resource.update_with_password(params)
  end
end

update_resouces(resource, params)メソッドは
・第1引数resourceupdate_with_passwordメソッドの使用対象オブジェクト
・第2引数paramsupdate_with_passwordメソッドに渡す引数
として、update_with_passwordメソッドを発火させます。

Devise::Models::DatabaseAuthenticatable#update_with_password

update_with_passwordメソッド
メソッドの名前の通り、パスワードが正しければリソースを更新するものでした。

まとめ

deviseを適用するモデルレコード更新時に走るDevise::RegistrationsController#updateアクションの根幹に
Devise::Models::DatabaseAuthenticatable#update_with_passwordメソッドがあるため、
デフォルトではリソース情報更新にパスワードが必要となっています。

【Ruby】deleteメソッドの注意

内容

Rubyのdeleteメソッドについて

deleteメソッドの使用対象のクラスの型によって
定義上、返り値が変わってしまうことに注意です。

deleteメソッドは色々なクラスに対して定義されている

String#delete (Ruby 3.0.0 リファレンスマニュアル)
Array#delete (Ruby 3.0.0 リファレンスマニュアル)
Hash#delete(key) (Ruby 3.0.0 リファレンスマニュアル)

deleteメソッド(Ruby)の注意点

Rubyのdeleteメソッドは、使用対象のクラスによって
・削除する要素を返すか
・削除指定した要素を削除した後の残りを返すか
が異なります。

削除する要素を返す
# class_object : クラスのオブジェクト
# delete_target :  削除する要素

class_object.delete(delete_target) # => delete_target
# 削除された要素が返り値となる
削除指定した要素を削除した後の残りを返す
# class_object : クラスオブジェクト
# remain_object : deleteメソッドで削除指定した要素の残り
# delete_target : 削除する要素
# remain_object = class_object - delete_target

class_object.delete(delete_target) #=> remain_object
# 削除した後の残りが返り値となる

返り値の違い

使用対象のクラス 返り値
Stringクラス (String#delete) 削除指定した要素を削除した後の残り
Arrayクラス (Array#delete) 削除された要素
Hashクラス (Hash#delete(key)) 削除された要素
(削除指定したkeyに対するvalue)

【Rails】Railsのassets周り(順次追加)

内容

Railsのassets管理

経緯

経緯① : assets周りの苦手意識

Railsのassets周りに関して、どうしても苦手意識があるんですよね。。。
・development環境・production環境問わず、最新のcssが反映されない
Railsアプリのデプロイのとき、assets:precompile周りで高確率で詰まる

経緯② : どれがsprocketsでどれがwebpacker?

Rails 6 からwebpackerが標準となったようですが
調べてみると
sprockets→webpackerへ一部移行、sprocketsのまま一部管理、
のような記事もあり、
・asstes管理にsprocketsを使うか?webpackerを使うか? の選択
・既存コードのasstes管理にsprocketsを使っているか?webpackerを使っているか? を見分ける
などが必要になると思い

今後の記事作成予定

Railsのassets周りのテーマも大テーマであると思うので、
記事は順次作成しようと思います
・assets pipeline
・sprockets
・webpacker
・その他
 ・パッケージマネージャ(npm, yarn ..)

【Rails】devise - deviseに関する各記事

内容

私が今まで作成したdeviseに関する記事のリンクをまとめてます
小さな項目ごとに作成しているため、まとめ記事として
(devise関連の記事を追加しましたら順次更新していきます)

deviseに関する記事リスト

deviseの基本的実装

【Rails】devise - deviseの基本実装手順

deviseのヘルパーメソッド

【Rails】devise - authenticate_user! メソッド
【Rails】devise - current_user
【Rails】devise - user_signed_in?

deviseのカスタマイズ
deviseのデフォルト設定

カスタマイズオーバーライドする際に
deviseデフォルト設定の把握が必要になります
【Rails】devise - deviseのデフォルト設定(リソース更新時にパスワードが必要)

オーバーライドによるカスタマイズ
# パスワード不要化

【Rails】devise - パスワードなしでアカウント情報編集できるようにカスタマイズ

# リダイレクト遷移先パスのカスタマイズ

【Rails】devise - ログイン・ログアウト後のリダイレクト先をカスタマイズする
【Rails】devise - アカウント作成・更新後のリダイレクト遷移先をカスタマイズする

モジュール追加

【Rails】devise - モジュールの追加方法

トラブルシューティング

【Rails】devise - current_userがログイン状態の時間切れでnilになる