default_scopeよりaround_actionとscopingを使おう
default_scope is evil は浸透してきたが...
最近では Railsでdefault_scope使うのはやめよう! という意見が強くなりましたね。
default_scope
は
default_scope -> { where(removed: false) }
こんなふうに論理削除を実装するときに便利です。
しかし管理画面では論理削除されたものも含めて集計したいなど、細かい欲求が出てくると意外とグローバルにスコープがセットされるのは都合が悪い。しかもdefault_scopeはmodelの初期値をセットしてしまう(これだとremoved = false
)とか、解除するためにunscopedを使うとリレーションからのチェーンができない(@user.blogs.unscoped
でuserのscopeごと消える)など残念な点も多いです。
しかしながら、エンドユーザー向けの画面では論理削除させたものを間違っても絶対に出したくない という強い意志があるとき、たしかにdefault_scope
を使いたくなる気持ちにも分はあるのです。「default_scopeを使わないなら、じゃあ毎回 Lesson.active.find(x)
とか User.active.find(x)
をつけなきゃいけないの? というのもごもっともだと思います。
around_actionとscopingを使おう
こういうときにaround_action
とscoping
が便利です。
scoping
はRailsであまり活用されていないが便利なメソッドで、次のようにブロックの中限定でdefault_scope的な挙動を実現できます。
Comment.where(post_id: 1).scoping do Comment.first end # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
around_action
もbefore_action
に比べると使い所がわかりづらいフィルタですが、scoping
を組み合わせるとこのようなコードが書けます。
around_action :set_scope def set_scope Blog.where(removed: false).scoping do yield end end
非表示フラグが立ったPostモデルで考える
ブログがあり、一つ一つの記事がPost
というモデルだとします。ここにhidden
というフラグがあり、true
の場合は非公開の記事だとします。
Post.visible
はhidden=false
のPostを返すものとします。
このとき、よくやってしまうのが
def index @posts = Post.visible.order(created_at: :desc).limit(10) #=> ちゃんとvisibleをつけている end def show @post = Post.find(params[:id]) #=> visibleをつけていない! end
のように、うっかりフラグの確認を抜かしてしまうミスです。これはやりがちなうえ、秘匿性の高い情報をあっさり公開してしまう大事故につながるので絶対に避けたいところです。default_scope
を使ってhidden=false
としたくなるのも気持ちは分かります。
こういうとき、上の方法を使いましょう。 controllerを
├─application_controller.rb ├─front_controller.rb (ブログ閲覧者用の画面) ├─front │ └─posts_controller.rb ├─admin_controller.rb (ブログ管理者用の画面) ├─admin │ └─posts_controller.rb
のような構造にします。こうするとFront::PostsController
とAdmin::PostsController
ができます。
これだとFront
のほうでは常にhidden=false
が保証されればいいので、以下のようにすればいいのです。
app/views/front_controller.rb
class FrontController < ApplicationController around_action :set_scope def set_scope Post.visible.scoping do yield end end
これでFront側でhidden=true
な記事が誤って露出する心配はありません。
スコープや閲覧権限の確認は入念に。
セキュリティの話でXSSやSQLインジェクションはコードだけで判別できるのですが、アプリケーションの仕様通りの閲覧権限・スコープになっているかはビジネスロジックなので、入念に確認しないとバグや脆弱性がわかりづらいです。上の方法はある程度有効なのですが、最終的には個々のactionごとに手を抜かず確認しましょう。