ActiveRecord::Base#with_scopeとscoped_access

ARのfindをある条件で絞っておきたい場合。with_scopeが使える。

Rails2.1以降?にはnamed_scopeというのもあるらしいけど、今回はRails1.2で。

例えばARの論理削除プラグイン、acts_as_paranoidではdeleted_atに削除フラグを立てる。通常のfindでは削除フラグが立ってるレコードは読まないようにAliasされており、あえて削除レコードをfindしたいときはfind_with_deleted(=オリジナルのfind)を呼べばいい。など。

acts_as_paranoidのように、モデル側で常に条件を絞っておく場合、

たとえばsakuraカラムがNULLじゃないレコードしかfindしないようにするには、
縛りたいModelの中で、

  class << self
    def find_with_sakura(*args)
      with_scope(:find=>{ :conditions=>["sakura is not null",]}) do
        find_without_sakura(*args)
      end
    end
    alias_method_chain :find, :sakura
  end

のようにfindをalias_method_chainしてすり替えておく。

Railsプロジェクト全体通して一定条件を付加したい場合はこの方法でよいのかな……?
(ただし、この方法ではfind_by_xxxには適用されない←普段使わないけども)


・動的にこの条件を変更したい場合はController側でやりたいっす。
scoped_accessというプラグインを使う。

インストール

$ script/plugin install http://wota.jp/svn/rails/plugins/branches/stable/scoped_access/

例:特定グループに所属しているユーザーは常に特定グループの中からしか検索できないし、特定グループに所属しているユーザーが書いたMemoしか読めない

class ApplicationController < ActionController::Base
  scoped_access User, :restricted_user, :only => :search
  scoped_access Memo, :restricted_memo

  private

  def restricted_user
    { :find => { :conditions => ["group_id = ?", session[:group_id]] } }
  end

  def restricted_memo
    users = User.find(:all, :conditions=>["group_id = ?", session[:group_id]]
    users_id_list = users.map(&:id)
    { :find => { :conditions => { :user_id => users_id_list } } }
  end  
end

ScopedAccess::MethodScoping.new()ってやらなくても、ハッシュを返せばイイみたい。
また、scoped_accessにBlockを渡したり、Hashを渡すなど書き方は色々あるようだ、詳しくは↓へ


参考:

基本的なwith_scopeの使い方
http://wota.jp/ac/?date=20060105

scoped_accessの使用例
http://wota.jp/ac/?date=20060816

ActiveRecord@ActionController で scoped_access をいろいろな書式で
http://d.hatena.ne.jp/MillyC/20080924/1222255886