【Rails】deviseのコントローラを独自にカスタマイズする方法

customize
customize

簡単に認証機能を実装することができるdeviseですが、独自にカスタマイズしたいと思うことが多々あります。
今回は、deviseのコントローラを独自にカスタマイズする方法をご紹介します。

ルーティングの設定を行う

routesファイルに、カスタマイズしたい機能のルーティングを記述します。
例えば、deviseのユーザ登録機能とログイン・ログアウト機能をカスタマイズしたい場合は、以下のように「registrations」と「sessions」のルーティングを記述します。

/config/routes.rb
devise_for :users, controllers: {
registrations: 'users/registrations',
sessions: 'users/sessions',
}
devise_for :users, controllers: { registrations: 'users/registrations', sessions: 'users/sessions', }
devise_for :users, controllers: {
  registrations: 'users/registrations',
  sessions: 'users/sessions',
}

この記述により、ユーザ登録機能(registrations)とログイン・ログアウト機能(sessions)へのアクセスがあった場合は、/app/controllers/users配下の、registrations_controller.rb、sessions_controller.rbが参照されるようになります。

カスタマイズしたい機能のみ、routesファイルに記述してください。

例1:パスワードを忘れたとき用の機能(passwords)をカスタマイズしたい時

devise_for :users, controllers: {
passwords: 'users/passwords',
}
devise_for :users, controllers: { passwords: 'users/passwords', }
devise_for :users, controllers: {
  passwords: 'users/passwords',
}

例2:ログイン・ログアウト機能(sessions)とアカウントブロック解除機能(unlocks)をカスタマイズしたい時

devise_for :users, controllers: {
sessions: 'users/sessions',
unlocks: 'users/unlocks',
}
devise_for :users, controllers: { sessions: 'users/sessions', unlocks: 'users/unlocks', }
devise_for :users, controllers: {
  sessions: 'users/sessions',
  unlocks: 'users/unlocks',
}

では、ルーティングの記述がない場合どうなるのでしょうか。仮に、以下のようにregistrations: ‘users/registrations’やsessions: ‘users/sessions’の記述をなくした場合は、gemライブラリのコントローラが参照されます。

devise_for :users, controllers: {
}
devise_for :users, controllers: { }
devise_for :users, controllers: {

}

ユーザ登録機能(registrations)のurlへアクセスがあった場合は、gemのdeviseファイルであるregistrations_controller.rbが参照されます。
gemのdeviseファイルとは、/vender/bundle/ruby/XXX/gems/devise-XXX/ 配下に格納されているファイル群です。(※ XXXは、rubyのバージョンとdeviseのバージョンが入ります。)

コントローラに独自ロジックを記述する

完全に独自ロジックにしたい場合

deviseにデフォルトに用意されている機能を、完全に自分独自のロジックに変更したい場合は、変更したいメソッドに記述された「super」の記述をコメントアウトし、独自ロジックを記述します。例えば、ユーザ登録機能(registrations)の「create」メソッドをカスタマイズするには、以下のように記述します。

/app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
super
end
# POST /resource
def create
#super
#独自ロジックを記述
end
class Users::RegistrationsController < Devise::RegistrationsController # GET /resource/sign_up def new super end # POST /resource def create #super #独自ロジックを記述 end ・ ・ ・
class Users::RegistrationsController < Devise::RegistrationsController

# GET /resource/sign_up
def new
  super
end

# POST /resource
def create
  #super
  #独自ロジックを記述
end
・
・
・

【参考】superメソッドって何?

superメソッドは、スーパークラスのメソッドを呼び出すメソッドです。superメソッドを実行すると、スーパークラス(ここでは1行目に記載されているDevise::RegistrationsController)の中で、その呼び出されたメソッドと同じメソッド名を持つメソッドを探して実行します。superメソッドが「create」メソッド内で呼ばれた場合は、スーパークラスの「create」メソッドを探して実行します。

Devise::RegistrationsControllerクラスは、/vender/bundle/ruby/XXX/gems/devise-XXX/app/controllers/devise/registrations_controller.rbファイルに記載されています。
(※XXXは、rubyのバージョンとdeviseのバージョンが入ります。)
実際に呼び出されているコードをみて見ると、同名のnewメソッドや、createメソッドが存在していることが分かります。

/vender/bundle/ruby/XXX/gems/devise-XXX/app/controllers/devise/registrations_controller.rb
class Devise::RegistrationsController < DeviseController
# GET /resource/sign_up
def new
build_resource
yield resource if block_given?
respond_with resource
end
# POST /resource
def create
build_resource(sign_up_params)
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message! :notice, :signed_up
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
class Devise::RegistrationsController < DeviseController ・ ・ ・ # GET /resource/sign_up def new build_resource yield resource if block_given? respond_with resource end # POST /resource def create build_resource(sign_up_params) resource.save yield resource if block_given? if resource.persisted? if resource.active_for_authentication? set_flash_message! :notice, :signed_up sign_up(resource_name, resource) respond_with resource, location: after_sign_up_path_for(resource) else set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}" expire_data_after_sign_in! respond_with resource, location: after_inactive_sign_up_path_for(resource) end else clean_up_passwords resource set_minimum_password_length respond_with resource end end ・ ・ ・
class Devise::RegistrationsController < DeviseController
・
・
・
  # GET /resource/sign_up
  def new
    build_resource
    yield resource if block_given?
    respond_with resource
  end

  # POST /resource
  def create
    build_resource(sign_up_params)

    resource.save
    yield resource if block_given?
    if resource.persisted?
      if resource.active_for_authentication?
        set_flash_message! :notice, :signed_up
        sign_up(resource_name, resource)
        respond_with resource, location: after_sign_up_path_for(resource)
      else
        set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
        expire_data_after_sign_in!
        respond_with resource, location: after_inactive_sign_up_path_for(resource)
      end
    else
      clean_up_passwords resource
      set_minimum_password_length
      respond_with resource
    end
  end
・
・
・

superメソッドで実際に実行されているコードは、このDevise::RegistrationsControllerクラスに記載されたnewメソッドやcreateメソッドになります。

devise機能の一部分のみを、独自ロジックにしたい場合

一部分のみを独自ロジックにしたい場合は、以下の流れで行います。

  • カスタマイズしたいメソッドをgemファイルからコピーする
  • superメソッドをコメントアウトし、コピーしてきたメソッドを呼び出すようにする
  • コピーしたメソッドを独自に修正し、カスタマイズする

/vender/bundle/~/devise/registrations_controller.rbに記載されたcreateメソッドを、/controllers/users/registrations_controller.rbにコピーします。メソッド名は、deviseのメソッドであることが分かるように、devise_createとしています。

/app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
# POST /resource
def create
super
end
def devise_create
build_resource(sign_up_params)
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message! :notice, :signed_up
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
class Users::RegistrationsController < Devise::RegistrationsController ・ ・ ・ # POST /resource def create super end def devise_create build_resource(sign_up_params) resource.save yield resource if block_given? if resource.persisted? if resource.active_for_authentication? set_flash_message! :notice, :signed_up sign_up(resource_name, resource) respond_with resource, location: after_sign_up_path_for(resource) else set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}" expire_data_after_sign_in! respond_with resource, location: after_inactive_sign_up_path_for(resource) end else clean_up_passwords resource set_minimum_password_length respond_with resource end end
class Users::RegistrationsController < Devise::RegistrationsController
・
・
・
# POST /resource
def create
  super
end


def devise_create
  build_resource(sign_up_params)

  resource.save
    yield resource if block_given?
    if resource.persisted?
      if resource.active_for_authentication?
        set_flash_message! :notice, :signed_up
        sign_up(resource_name, resource)
        respond_with resource, location: after_sign_up_path_for(resource)
      else
        set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
        expire_data_after_sign_in!
        respond_with resource, location: after_inactive_sign_up_path_for(resource)
      end
    else
      clean_up_passwords resource
      set_minimum_password_length
      respond_with resource
    end
end

次に、createメソッド内のsuperメソッドをコメントアウトし、コピーしてきたdevise_createメソッドを呼び出すようにします。呼び出されるdevise_createメソッド内に独自ロジックを記述します。

/app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
# POST /resource
def create
#super
devise_create
end
def devise_create
# 独自ロジックを記述
build_resource(sign_up_params)
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message! :notice, :signed_up
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
class Users::RegistrationsController < Devise::RegistrationsController ・ ・ ・ # POST /resource def create #super devise_create end def devise_create # 独自ロジックを記述 build_resource(sign_up_params) resource.save yield resource if block_given? if resource.persisted? if resource.active_for_authentication? set_flash_message! :notice, :signed_up sign_up(resource_name, resource) respond_with resource, location: after_sign_up_path_for(resource) else set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}" expire_data_after_sign_in! respond_with resource, location: after_inactive_sign_up_path_for(resource) end else clean_up_passwords resource set_minimum_password_length respond_with resource end end
class Users::RegistrationsController < Devise::RegistrationsController
・
・
・
# POST /resource
def create
  #super
  devise_create
end

def devise_create
  # 独自ロジックを記述
  build_resource(sign_up_params)

  resource.save
    yield resource if block_given?
    if resource.persisted?
      if resource.active_for_authentication?
        set_flash_message! :notice, :signed_up
        sign_up(resource_name, resource)
        respond_with resource, location: after_sign_up_path_for(resource)
      else
        set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
        expire_data_after_sign_in!
        respond_with resource, location: after_inactive_sign_up_path_for(resource)
      end
    else
      clean_up_passwords resource
      set_minimum_password_length
      respond_with resource
    end
end

【参考】deviseファイルを直接編集したらダメ?

deviseファイルの中身をコピーしてきて、内容をカスタマイズするなら、直接gemのdeviseファイルを編集しちゃえばいいんじゃない?と思われるかもしれません。しかし、gemのdeviseファイルは直接編集してはいけません。 確かにgemのdeviseファイルを直接編集しても、同じ結果が得られます。しかし、gemは外部ライブラリのため、bundle installやbundle updateをするとファイルが上書きされ、せっかく編集した内容が消えてしまう可能性があります。また、外部ライブラリは一般的にgit管理しないため、gitで管理しているプロジェクトでは変更をコミットすることができません。 外部ライブラリを編集したい場合は、今回のように直接編集せず、別の方法を考える必要があります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です