こんにちは。てくてーくです。
今回はRailsのsessionメソッドについて学んでいきます。
Railsチュートリアルをやっているとログイン機能の実装でsessionメソッドが出てきますが、チュートリアルの中ではサラッとした説明しかありませんでしたのでここでより理解を深めるためにまずはどこにsessionの実態があるのかというのを探していきたいと思います。
sessionメソッドは事前に定義されている
sessionメソッドは事前にRailsに定義されているようなのですが、一体どこにどうやって定義されているのでしょうか。
sessionメソッドを使用した部分から継承元をたどっていき探していきます。
まずはチュートリアルで使用されている app/helpers/sessions_helper.rb を見ていきます。
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
end
セッションヘルパーモジュールで使用されていますね。ではこのモジュールはどこで使用されるのでしょうか。
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
こちらでMix-inされていますね。
Mix-inについての説明はこちらをご覧ください。モジュールってなに?|tekute-ku
このapplication controllerクラスにはsessionメソッドについては書かれていません。
継承元のActionController::Baseを見ていきましょう。これ以降はrailsの中身になっていきます。
ActionController::Baseを見に行ってみるとsessionメソッドについては書かれていません。
.
.
module ActionController
.
.
.
class Base < Metal
しかしBaseクラスはMetalクラスを継承しているようなのでActionController::Metalを見に行ってみます。
ActionController::Metalを見ていると・・・お、ついに発見しました。sessionメソッド??です。
.
.
module ActionController
.
classMetal < AbstractController::Base
.
attr_internal :response, :request
delegate:session,to: "@_request"
.
end
.
end
delegate:session,to: "@_request"
しかしこれがsessionメソッドの定義なのでしょうか。見たことがない書き方をしており初心者にはとても手に負えなさそうなのでググってみることにしました。
delegateとは
delegate 委任
delegateとはRailsガイドによると
attr_internal :response, :request
delegate:session,to: "@_request"
attr_internal :response, :request
がなにやら関係していそうですのでこちらを調べてみたいと思います。attr_internalとは
attr_internal :response, :request
attribute・・属性 internal・・内部、内臓
attr_internalはモジュールの拡張機能の一つのようです。
あるクラスで属性を定義すると後にそのクラスのサブクラスが作られるときに名前が衝突するリスクが生じます。RailsのActive Supportではattr_internal_accessorというマクロが定義されていて内部のインスタンス名が衝突しにくいように配慮されています。
上のコードを例に例えてみましょう
classMetal
attr_internal :response, :request
end
classGold < Metal
attr_accessor :request
end
このようなコードがある場合、attr_internal があるおかげでrequestの衝突を回避しています。
このとき内部インスタンス変数の名前には冒頭にアンダースコアが追加されます。上の例だと
@_request がインスタンス変数になります。このように内部でrailsがうまいことやってくれているおかげで衝突を回避しています。
また attr_internal は attr_internal_accessor と書くみたいですが同じ意味みたいですので省略されているようです。
つまりこのコードを読み取ると
.
.
module ActionController
.
classMetal < AbstractController::Base
.
attr_internal:response,:request
delegate:session,to: "@_request"
.
end
.
end
- インスタンス変数読み取りのためのインスタンスメソッドresponseとrequestを定義している。
- インスタンス変数はそれぞれ@_response、@_request となる
- sessionメソッドがインスタンス変数@_requestに使えるようになる
@_requestがインスタンス変数だということは分かりましたが、まだ@_requestの実態がよく分かりません。
仕方ないのでsessions_controllerでraiseをしてみました。
class SessionsController < ApplicationController
def new
end
def create
raise session.inspect
#user = User.find_by(email: params[:session][:email].downcase)
#if user && user.authenticate(params[:session][:password])
# log_in user
# redirect_to user
#else
# flash.now[:danger] = 'Invalid email/password combination'
# render 'new'
#end
end
end
すると出力された中に@_requestを発見。どこにあるのかというと
ActionDispatch::Request::Sessionみたいです。早速見に行ってみましょう。
どうやら@_requestはActionDispatch::Request::Sessionのオブジェクトだったようです。
ようやく見つけました。sessionメソッドが書かれています。session.rb
まとめ
まとめてみると
Application Controllerにsessionメソッドを書くとその継承元であるActionController::MetalがdeligateしているActionDispatch::Request::Sessionクラスのオブジェクトである@_requestのsessionメソッドたちが使えるようになる、といったところでしょうか。
何気なく書いていたsessionメソッドがこんなに複雑だとは思いませんでした。正直初心者にはかなり難しくて合っているかどうかも分かりませんがお役に立てれば幸いです。
参考:Rails の session を完全に理解した、Railsガイド内部属性、Github rails、Railsガイドdeligate、リファレンスマニュアルattr
コメント