ActiveRecord@ActionController で scoped_access をいろいろな書式で
ActiveRecordを詳しく「優しいRailsの育て方」 より。
もっとシンプルにできるけど。
つい機能追加やら汎用化やらをしてしまう。
Rails 2.1.0 で動作確認。
コメントいただいたので scoped_access_filter.rb を修正しました。
使い方
基本。
class MailController < ApplicationController scoped_access Mail, :mine def mine unless user.admin? { :find => {:conditions => ["owner = ?", user]}, :create => {:owner => user } } end end def show @mail = Mail.find(params[:id]) end end
複数のモデルを対象とする。
scoped_access Mail, User, Group, :not_deleted def not_deleted(model) if model.columns.any? { |c| c.name == 'deleted_at' } { :find => {:conditions => ["NOT deleted_at IS NULL"]} } end end
条件を lambda (block) で定義する。
scoped_access Mail, lambda { {:find => {:conditions => ["updated_at < ?", Date.today]}} } scoped_access(Mail) do |model, controller| # ... end
条件を直接(Hash で)定義する。
scoped_access User, :find => {:conditions => ['admin = ?', true]}
named_scope の条件を利用する。
class User < ActiveRecord::Base named_scope :admins, :conditions => ['admin = ?', true] # ... end scoped_access User, :name => :admins
フィルタを適用(除外)するアクションを指定する。(before_filter 等と同様)
scoped_access Mail, :mine, :only => :show scoped_access User, :find => {:conditions => ['admin = ?', true]}, :except => :list
scoped_access_filter.rb
class ScopedAccessFilter def initialize(model, *args, &block) options = args.extract_options! @constraints = case when options[:method] options[:method] when options[:name] # named_scope {:find => model.send(options[:name]).proxy_options} when !options.empty? options end @constraints ||= args.pop unless args.last.is_a?(Class) and args.last < ActiveRecord::Base @constraints ||= block || :method_scoping @models = [model, args].flatten end def filter(controller) scoped_models = [] @models.each do |model| constraints = create_constraints(controller, model) next unless constraints ActiveRecord::Base.logger.debug("ScopedAccessFilter: %s.with_scope(%s)" % [model, constraints.inspect]) model.instance_eval do scoped_methods << with_scope(constraints) { current_scoped_methods } end scoped_models << model end begin yield ensure scoped_models.each do |model| model.instance_eval do scoped_methods.pop end end end end private def create_constraints(controller, model) cproc = case @constraints when Proc, Method @constraints when String, Symbol controller.method(@constraints) end return @constraints unless cproc args = case cproc.arity when 0; [] when 1; [model] else ; [model, controller] end cproc.call(*args) end end module ActionController::Filters::ClassMethods def scoped_access(model, *args, &block) options = args.extract_options! scoped_options = {} options.reject! { |k, v| (scoped_options[k] = v; true) unless [:only, :except].include? k } args << scoped_options unless scoped_options.empty? around_filter ScopedAccessFilter.new(model, *args, &block), options end end