TL;DR
def show
@user.do_something(params[:foo])
my_redirect && return # 注目
@user.do_something_other
end
private
def my_redirect
if @user.some_flag?
redirect_to some_path(@user.some_attr)
end
end
解説
以下の要望があると考えましょう、@user.do_something(params[:foo])
で、paramの一部を処理してから、@user.some_flag?
を判断して、some_path(@user.some_attr)
に動的リダイレクトしたいです。
また、リダイレクトが実行された場合、@user.do_something_other
を実行しないことです。
特にSEO系の要件でそういうリダイレクト処理が多いと思いますね。
Guard Clauseとは
条件分岐や関数の途中、早期に脱出する手段の一つです。積極的に使うと、ソースコードの可読性が向上し、性能もアップすることが期待されています。「ガード節」、「早期復帰」など和訳があります。
Rubyの場合、Guard Clauseはループ途中のnext
やbreak
、あるいはメソッド途中明記的なreturn
などがあります。
まずは正攻法
def show
@user.do_something(params[:foo])
redirect_to some_path(@user.some_attr) && return if @user.some_flag?
@user.do_something_other
end
リダイレクトの判断ロジックがシンプルで再利用する予定がない場合、正攻法は大体対応できます。
しかし複雑なロジックでリダイレクトを組む場合、コントローラーが肥大化になりがちです。
今までの王道
before_action
に該当ロジックをHookさせると、Actionが実行する前の段階でリダイレクトが実行され、Actionの実行がSkipされます。
例として、deviseのbefore_action :authenticate_user!
が有名です。
before_action :setup_user, only: :show
before_action :my_redirect, only: :show
def show
@user.do_something_other
end
private
def setup_user
@user.do_something(params[:foo])
end
def my_redirect
if @user.some_flag?
redirect_to some_path(@user.some_attr)
end
end
ご覧のように、before_action
で対応する場合、リダイレクトがActionが実行する前の段階で実行されるので、@user.do_something(params[:foo])
もbefore_action
に出さなければいけません。
修正範囲が広くなり、ロジックが複雑な場合、返って不便になります。
今回紹介したい手法
def show
@user.do_something(params[:foo])
my_redirect && return # 注目
@user.do_something_other
end
private
def my_redirect
if @user.some_flag?
redirect_to some_path(@user.some_attr)
end
end
原理
まずmy_redirect
は、@user.do_something(params[:foo])
で実行されます。
次に@user.some_flag?
がfalse
の場合、nil
が返されて、my_redirect && return
が短絡評価の影響で飛ばされ、以降の処理が続かれます。
@user.some_flag?
がtrue
の場合、下記の内容が返されて、truthy
なので、&& return
が実行され、メソッドが脱出され、以降のロジックが実行されません。
Redirected to http://localhost:3000/some_path?foo=bar
"<html><body>You are being <a href=\"http://localhost:3000/some_path?foo=bar\">redirected</a>.</body></html>"
以上でした。