7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

初心者がDeviseのコードを見て仕組みをふんわり理解する【registrations new】

Posted at

##はじめに
Railsチュートリアルがやっと1周終わった者です。
gemのDeviseを入れてみたら、Railsチュートリアルでの5~6章分の実装が10分ほどで終わり、驚愕しています。

なんでできたのか分からない...
そして、テストも書きたいけどどうやって書いたらいいのか分からない。
そこで「公式やソースコードを読みましょう」ということで、読んでみたけどこれまたさっぱり分からない...ながらも少しずつ理解したいので、読み込んでいきます。

読みながら自分が調べたことをまとめていきます。

・初心者だけどDeviseの仕組みを知りたい

そんな方の役に立てたら幸いです。

DeviseのGitHub
https://github.com/heartcombo/devise

##Deviseの概要
GitHubのREADMEの一番最初に、概要の説明があります。

  • Deviseは、Wardenに基づくRails向けの柔軟な認証ソリューションです。
  • ラックベースです。
  • Railsエンジンに基づく完全なMVCソリューションです。
  • 複数のモデルに同時にサインインさせることができます。
  • モジュール性の概念に基づいています。本当に必要なものだけを使用してください。

ここで出てくるWardenというのは認証のためのgemで、devise内でこれを引っ張ってきているようです。

また、10個のモジュールで構成されており、必要なものはコメントアウトを外したりしながら使ってね、ということのようです。
モジュールというかもう機能ですね。
Qiita内で表にしてくださっている方がいらっしゃったので、下記にて引用します。

|機能 | 概要|
|:-----------------|:----------------|
|database_authenticatable |サインイン時にユーザーの正当性を検証するためにパスワードをハッシュ化してDBに登録します。認証方法としてはPOSTリクエストかHTTP Basic認証が使えます。|
|registerable |登録処理を通してユーザーをサインアップします。また、ユーザーに自身のアカウントを編集したり削除できるようにします。|
|recoverable |パスワードをリセットし、それを通知します。|
|rememberable |保存されたcookieから、ユーザーを記憶するためのトークンを生成・削除します。|
|trackable |サインイン回数や、サインイン時間、IPアドレスを記録します。|
|validatable |Emailやパスワードのバリデーションを提供します。独自に定義したバリデーションを追加することもできます。|
|confirmable |メールに記載されているURLをクリックして本登録を完了する、といったよくある登録方式を提供します。また、サインイン中にアカウントが認証済みかどうかを検証します。|
|lockable |一定回数サインインを失敗するとアカウントをロックします。ロック解除にはメールによる解除か、一定時間経つと解除するといった方法があります。|
|timeoutable |一定時間活動していないアカウントのセッションを破棄します。|
|omniauthable |intridea/omniauthをサポートします。TwitterやFacebookなどの認証を追加したい場合はこれを使用します。|
引用元:[Rails] deviseの使い方(rails6版)
https://qiita.com/cigalecigales/items/16ce0a9a7e79b9c3974e

また、Deviseは、コントローラーとビュー内で使用するヘルパーを作成します。
よく使用するコマンドを予め設定したものです。

デバイスモデルが 'User'であると想定してヘルパー名は例示していますが、
デバイスモデルがユーザー以外の場合は、「_ user」を「_yourmodel(任意のモデル名)」に置き換えると、同じロジックが適用されます。

こちらもQiita内で表にしてくださっている方がいらっしゃったので引用します。

|メソッド | 用途   |
|:-----------------|:----------------|
|before_action :authenticate_user!|コントローラーに設定して、ログイン済ユーザーのみにアクセスを許可する|
|user_signed_in?|ユーザーがサインイン済かどうかを判定する|
|current_user|サインインしているユーザーを取得する|
|user_session|ユーザーのセッション情報にアクセスする|
引用元:Rails deviseで使えるようになるヘルパーメソッド一覧
https://qiita.com/tobita0000/items/866de191635e6d74e392

##registrations -- サインアップ・アカウント編集・削除
最も基本的なアカウントのCRUDはこのregistrationが担っているので、ここが分からないと応用の機能部分のコードリーディングは難しそうです。

コード全体を上から調べた範囲で記入してきますが、ちまちま区切っていくので、分かりくいかもしれません。
横にソースコードを置きながら見てもらえれば、ちょっとは分かりやすいかも...

####frozen_string_literal

devise/app/controllers/devise/registrations_controller.rb
# frozen_string_literal: true

コメントアウトされていますが、Rubyのバージョンアップに備えた1文のようです。
参考:frozen_string_literalが入って気づいた、メソッド設計の原則
https://qiita.com/jkr_2255/items/300b5db8c1f04e1e2815

####prepend_before_action

devise/app/controllers/devise/registrations_controller.rb
class Devise::RegistrationsController < DeviseController
  prepend_before_action :require_no_authentication, only: [:new, :create, :cancel]   
  prepend_before_action :authenticate_scope!, only: [:edit, :update, :destroy]
  prepend_before_action :set_minimum_password_length, only: [:new, :edit]
  • DeviseControllerを継承しています。
    ソースコードのファイルをみると、devise_controller.rbはこのモジュールだけでなく、すべてのモジュールへ引き継いでいます。
  • prepend_before_actionbefore_actionより前に実行するメソッドです。アクセスできるアクションがユーザーのログイン状態で制限されるようにしています。

####newアクション

devise/app/controllers/devise/registrations_controller.rb
 # GET /resource/sign_up
  def new
    build_resource
    yield resource if block_given?
    respond_with resource
  end
  • ログインするための最初の部分です。
  • resourceはすでにdevise_controllerで定義されています。
devise/app/controllers/devise_controller.rb
  def resource
    instance_variable_get(:"@#{resource_name}")
  end

  # Proxy to devise map name
  def resource_name
    devise_mapping.name 
  end

  alias :scope_name :resource_name
  • instance_variable_getメソッドはインスタンス変数の値を取得して返します。@user = からの定義と同じもののようです。
  • resource内に#{resource_name}という変数がありますが、その下部分で定義されています。
  • devise_mapping.nameこちらもすでにdevise_controllerで定義されてますが、引用記事をみると、別部分にヒントがありそうです。
devise/app/controllers/devise_controller.rb
  def devise_mapping 
    @devise_mapping ||= request.env["devise.mapping"]
  end

nameに注目してコードを繋げてみると、認証モデルがUserである場合は@singular = :users.to_s.tr('/', '_').singularize.to_symとみれます。singularizeは複数形を単数形に変換するメソッドで、最終的に@singular = :userとなりsingularのエイリアスがnameとなっているのでmapping.nameで:userが取得できます。
するとdefine_methodsの引数に:userが渡されauthenticate_user!が出来上がるという流れになっています。
引用元:DeviseのコードリーティングでRailsを学ぶ
https://qiita.com/irisAsh/items/513b8b58f54421b9a1a0

端的に言うと、mapping.name:userが取得できるのでそれをresource_nameにしているということでしょうか。

  • registrations_controller下部にbuild_resourceがあります。セッションを新しく作るという意味のようです。
devise/app/controllers/devise/registrations_controller.rb
 def build_resource(hash = {}) #build_resource(hash = {})の定義
    self.resource = resource_class.new_with_session(hash, session)
  end
  • 元のregistrations_controller上部へ戻りましょう。buildはほぼnewと近い役割をしています。build_resourceという1文は、データベースから取り出したユーザーのインスタンス変数とセッションを作るということになります。
  • block_given?はメソッドを実行する時にブロックが渡されていればtrueを返し、渡されていない時はfalseを返します。
    • ブロックとは

do ... end または { ... } で囲まれたコードの断片 (ブロックと呼ばれる)を後ろに付けてメソッドを呼び出すと、そのメソッドの内部からブロックを評価できます。ブロック付きメソッドを自分で定義するには yield 式を使います。
引用元:Ruby 2.7.0 リファレンスマニュアル
https://docs.ruby-lang.org/ja/latest/doc/spec=2fcall.html#block

devise/app/controllers/devise_controller.rb
  def respond_with_navigational(*args, &block)
    respond_with(*args) do |format|
      format.any(*navigational_formats, &block)
    end
  end
devise/app/controllers/devise_controller.rb
  def navigational_formats
    @navigational_formats ||= Devise.navigational_formats.select { |format|Mime::EXTENSION_LOOKUP[format.to_s] }
  end

ピンときていないので勉強して加筆・修正したいと思います。

ここまで複雑なんだなGem...たった数行のコードにいろいろなものが凝縮されていてとても勉強になりました。
newアクションしか書けなかったけど、最終的にはregistrations_controllerの各アクションのだけでも読んだ記録を残したい...!

お付き合いいただきありがとうございました。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?