railsのgem deviseのログインページを開くコントローラを読解してみた

rails読解シリーズ、第４回はdeviseのコントローラを読解してみました。
いつも深入りしすぎて、全体像が見えづらくなっているので今回はあっさりめに読んでみました。

ログインページを開くための、session#newアクションを見ていきます

```ruby:session#new
def new
  self.resource = resource_class.new(sign_in_params)
  clean_up_passwords(resource)
  yield resource if block_given?
  respond_with(resource, serialize_options(resource))
end
```

```ruby:2行目
self.resource = resource_class.new(sign_in_params)
```
resource_classによってdeviseを適用しているモデル、例えばUserモデルが呼ばれます。
sign_in_paramsによりストロングパラメータを通した値が、Userモデルに格納されます。

resource_classメソッドの中の動作を見たものの、いまいちなぜ、Userモデルが呼び出されるのかは理解しきれませんでした😅
resource_classメソッドを定義している理由はおそらく、deviseの適用モデルを柔軟に変えることができるようにするためかと推測しています。

```ruby:3行目
clean_up_passwords(resource)
```
clean_up_passwordsメソッドにより、resourceに格納されたUserモデルのpassword、password_confirmation属性を空にする(nilを代入する)

```ruby:4行目
yield resource if block_given?
```
コントローラにブロックが渡された場合はyield resourceが実行される。
これはdeviseのデフォルトコントローラを再利用しつつ、追加で機能を追加する場合に機能する。
deviseコントローラをカスタマイズするため、のgenerateコマンドをターミナルで実行すると生成されるコントローラは、

```ruby:カスタマイズ用に生成されるコントローラ
def new
  super
end
```
のように生成される。
このときsuperにブロックを引数と渡すことで、ブロック内の動作をdeviseのコントローラ動作に追加することができる。

```ruby:deviseのデフォルトの動作を残しつつ、カスタマイズする方法
def new
  super { |resource| ... }
end
```
逆にdeviseのデフォルトコントローラ側ではsuperメソッドによってブロックが渡されていないか確認し、渡されている場合はyield resourceによってブロックのコードを実行する。

```ruby:5行目
  respond_with(resource, serialize_options(resource))
```
respond_withメソッドは、[こちら](https://apidock.com/rails/v4.0.2/ActionController/MimeResponds/respond_with)によると、httpレスポンスを生成するようです。

クライアント側が指定したmimetypeに応じて、レスポンスは生成できます。
mimetypeとはデータの形式のことで、HTML形式やjson形式などが含まれます。
中のコードは見ていませんが、第２引数のserialize_options(resource)でmimetypeの指定ができるようになっているのかと思われます。

```ruby:ソースコード
# File actionpack/lib/action_controller/metal/mime_responds.rb, line 323
    def respond_with(*resources, &block)
      raise "In order to use respond_with, first you need to declare the formats your "              "controller responds to in the class level" if self.class.mimes_for_respond_to.empty?

      if collector = retrieve_collector_from_mimes(&block)
        options = resources.size == 1 ? {} : resources.extract_options!
        options[:default_response] = collector.response
        (options.delete(:responder) || self.class.responder).call(self, resources, options)
      end
    end
```
中身の動きに関しては[respond_toに関する記事](https://dev.classmethod.jp/articles/rails-respond_to/)が参考になりそう。

## まとめ
収穫はdeviseのデフォルトコントローラに上書き、ではなく追加する方法がわかったことですかね。
かなりシンプルな動きです。
どちらかというとcurrent_userとかヘルパーメソッドの定義のほうが気になります。

短いですが今回は以上です。


## おまけ
resource_classメソッドについて、わかるところまで。
ほぼメモ書きです。

```ruby
class DeviseController < Devise.parent_controller.constantize

def resource_class
  devise_mapping.to
end

def devise_mapping
  @devise_mapping ||= request.env["devise.mapping"]
end
```
@devise_mappingにはMappingモデルのインスタンスが代入されている。
Mappingモデルにはtoメソッドが定義されている。
toメソッドはDeviseMappingモデルのklass属性の値を呼び出す。
klass属性の中にはUserモデルが格納されている。

```ruby:Mappingモデル
class Mapping #:nodoc:
  attr_reader :singular, :scoped_path, :path, :controllers, :path_names,
              :class_name, :sign_out_via, :format, :used_routes, :used_helpers,
              :failure_app, :router_name
              
def to
  @klass.get
end
```

このとき@devise_mappingにどのようにして、Mappingモデルの値が渡されているのか、さらにUserモデルの情報が渡されているのか追いきれませんでした。