Railsチュートリアル11章リスト11.40にて
以下のコードがあった。
def show
@user = User.find(params[:id])
redirect_to root_url and return unless @user.activated?
end
このコードの中のand return unless
の挙動が分からなかったので、調べて考えたことをまとめておく。
and return
について
まずはand return
について見てみよう。Railsガイドにて以下の解説と参考コードがあった。
Rails開発をやっていれば、一度は "Can only render or redirect once per action" エラーに遭遇したことがあるでしょう。いまいましいエラーですが、修正は比較的簡単です。このエラーはほとんどの場合、開発者がrenderメソッドの基本的な動作を誤って理解していることが原因です。
def show
@book = Book.find(params[:id])
if @book.special?
render action: "special_show"
end
render action: "regular_show"
end
上のコードでは、@book.special?
がtrue
の時、if
文内のrender
とその外のrender
も実行されてしまい、二重レンダリングというエラーになる。そこで、and return
を使って明示的にメソッドを抜けることで二重レンダリングを避ける。
def show
@book = Book.find(params[:id])
if @book.special?
render action: "special_show" and return
end
render action: "regular_show"
end
挙動としては、render
やredirect_to
の戻り値がtrue
なので、and
の後ろのreturn
が実行されるということだ。
なお、&& return
を使うとこの場合正しく動作しない。というのも、&&
は優先度が高いのでrender action: ("special_show" && return)
ということになるからだろう。一方のand
の優先度は超低い。and return
はこのand
の優先度の低さを上手に利用している。
and return unless
について
and return
の働きとand
の優先度について書いたので、あとはそれを組み合わせるだけだ。もう一度最初のコードを見てみよう。
def show
@user = User.find(params[:id])
redirect_to root_url and return unless @user.activated?
end
and
の優先順位は低いので、redirect_to root_url
が先に評価されるので、and
が評価するのはredirect_to root_url
とreturn
になる。()でくくってやると以下のようになる。
(redirect_to root_url) and (return)
あとは、上のカタマリがunlessの後置修飾がついているだけだ。つまり、redirect_to root_url and return unless @user.activated?
は以下と同じになる。
unless @user.activated?
redirect_to root_url and return
end
おわり
and return unless
というカタマリで見てしまったので挙動が見えず分からなくなってしまったが、ひとつずつ分解して見てみるとそこまで恐ろしいものでもなかった。「巨大で恐ろしい相手でも、ひとつひとつは取るに足らないものだぞ」と人生の教訓をプログラミングが教えてくれた。