email
でAccount
を検索して返すメソッドがあるとしましょう。def account Account.find_by(email: self.email) end
このメソッドには問題があります。
呼び出しのたびに検索処理が行われてしまうことです。
解決するためにはどうするでしょうか。
解決方法はいくつかありますが、ここでは以下のようにメモ化するという選択をしたとしましょう。
def account @account ||= Account.find_by(email: self.email) end
これで初回の呼び出しのみで検索が行われ、2回目以降はインスタンス変数に設定された値が使われるようになります。
よし、完璧だ、と思ったでしょうか。
しかしこれでもまだ問題があります。
検索処理が
nil
を返す可能性です。検索処理で
nil
が返らない前提ならば、以下のように例外が発生するfind_by!
のようなメソッドを使用しておけばヒットしないケースは例外的ケースのみということで問題にはならないでしょう。def account @account ||= Account.find_by!(email: self.email) end
ここでは検索処理で
nil
が返ることがある前提で話を進めましょう。nil
が返った場合に何が問題か。検索処理で
nil
が返る場合、account
メソッドが呼ばれるたびに検索処理を行ってしまうのです。それもそのはず、Rubyの
||=
演算子は左辺が偽の場合に右辺の処理行い左辺に代入する演算子だからです。Rubyで偽の場合というのは
nil
かfalse
の場合です。Rubyのインスタンス変数は未初期化であれば
nil
として扱われます。初回の実行時はこれにより右辺が評価され、インスタンス変数に値が設定されます。
右辺が偽を返さない場合は2度目以降は左辺の評価が真となりますので右辺が評価されることなく終わりますが、右辺が偽の値を返していた場合は2度目以降でも左辺の評価が偽となり右辺の評価が行われてしまいます。
これは望んでいる挙動ではないことでしょう。
それではどうするか。
こういう場合は以下のように
instance_variable_defined?
を使用します。def account return @account if instance_variable_defined? :@account @account = Account.find_by(email: self.email) end
これならばインスタンス変数が未定義の場合(つまり初回呼び出しの場合です)は、検索処理が行われその結果が何であろうともインスタンス変数に代入されます。
2度目以降の呼び出しでは設定された値が
nil
であってもその値が返されます。つまり検索処理は1度しか行われません。
これで望んでいた動作になりました。
良かった。
勉強になった!!
返信削除ご参考になったようで幸いです。
削除