dynamically detect which model adapter to use given a class

This commit is contained in:
Ryan Bates 2010-12-30 14:42:19 -08:00
parent cc30e838c0
commit bbb02f7c8f
9 changed files with 51 additions and 1 deletions

View File

@ -10,6 +10,8 @@ when "data_mapper"
when "mongoid"
gem "bson_ext", "~> 1.1"
gem "mongoid", "~> 2.0.0.beta.19"
else
raise "Unknown model adapter: #{ENV["MODEL_ADAPTER"]}"
end
gemspec

View File

@ -7,6 +7,7 @@ require 'cancan/query'
require 'cancan/inherited_resource'
require 'cancan/model_adapters/abstract_adapter'
require 'cancan/model_adapters/default_adapter'
require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
require 'cancan/model_adapters/data_mapper_adapter' if defined? DataMapper
require 'cancan/model_adapters/mongoid_adapter' if defined? Mongoid

View File

@ -187,7 +187,8 @@ module CanCan
end
def model_adapter(model_class, action)
ModelAdapters::ActiveRecordAdapter.new(model_class, relevant_rules_for_query(action, model_class))
adapter_class = ModelAdapters::AbstractAdapter.adapter_class(model_class)
adapter_class.new(model_class, relevant_rules_for_query(action, model_class))
end
# See ControllerAdditions#authorize! for documentation.

View File

@ -1,6 +1,20 @@
module CanCan
module ModelAdapters
class AbstractAdapter
def self.inherited(subclass)
@subclasses ||= []
@subclasses << subclass
end
def self.adapter_class(model_class)
@subclasses.detect { |subclass| subclass.for_class?(model_class) } || DefaultAdapter
end
# Used to determine if the given adapter should be used for the passed in class.
def self.for_class?(member_class)
false # override in subclass
end
def initialize(model_class, rules)
@model_class = model_class
@rules = rules

View File

@ -1,6 +1,10 @@
module CanCan
module ModelAdapters
class ActiveRecordAdapter < AbstractAdapter
def self.for_class?(model_class)
model_class <= ActiveRecord::Base
end
# Returns conditions intended to be used inside a database query. Normally you will not call this
# method directly, but instead go through ActiveRecordAdditions#accessible_by.
#

View File

@ -0,0 +1,7 @@
module CanCan
module ModelAdapters
class DefaultAdapter < AbstractAdapter
# This adapter is used when no matching adapter is found
end
end
end

View File

@ -343,6 +343,14 @@ describe CanCan::Ability do
end
end
it "should determine model adapter class by asking AbstractAdapter" do
model_class = Object.new
adapter_class = Object.new
stub(CanCan::ModelAdapters::AbstractAdapter).adapter_class(model_class) { adapter_class }
stub(adapter_class).new(model_class, []) { :adapter_instance }
@ability.model_adapter(model_class, :read).should == :adapter_instance
end
describe "unauthorized message" do
after(:each) do
I18n.backend = nil

View File

@ -33,6 +33,12 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
@comment_table = Comment.table_name
end
it "should be for only active record classes" do
CanCan::ModelAdapters::ActiveRecordAdapter.should_not be_for_class(Object)
CanCan::ModelAdapters::ActiveRecordAdapter.should be_for_class(Article)
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
end
it "should not fetch any records when no abilities are defined" do
Article.create!
Article.accessible_by(@ability).should be_empty

View File

@ -0,0 +1,7 @@
require "spec_helper"
describe CanCan::ModelAdapters::DefaultAdapter do
it "should be default for generic classes" do
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Object).should == CanCan::ModelAdapters::DefaultAdapter
end
end