From bbb02f7c8f78327f55ce19db91c6b7adfdf08da4 Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Thu, 30 Dec 2010 14:42:19 -0800 Subject: [PATCH] dynamically detect which model adapter to use given a class --- Gemfile | 2 ++ lib/cancan.rb | 1 + lib/cancan/ability.rb | 3 ++- lib/cancan/model_adapters/abstract_adapter.rb | 14 ++++++++++++++ lib/cancan/model_adapters/active_record_adapter.rb | 4 ++++ lib/cancan/model_adapters/default_adapter.rb | 7 +++++++ spec/cancan/ability_spec.rb | 8 ++++++++ .../model_adapters/active_record_adapter_spec.rb | 6 ++++++ spec/cancan/model_adapters/default_adapter_spec.rb | 7 +++++++ 9 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 lib/cancan/model_adapters/default_adapter.rb create mode 100644 spec/cancan/model_adapters/default_adapter_spec.rb diff --git a/Gemfile b/Gemfile index 1d736fe..1c0d3d8 100644 --- a/Gemfile +++ b/Gemfile @@ -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 diff --git a/lib/cancan.rb b/lib/cancan.rb index 4b3315c..a947157 100644 --- a/lib/cancan.rb +++ b/lib/cancan.rb @@ -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 diff --git a/lib/cancan/ability.rb b/lib/cancan/ability.rb index 0f9c321..039aa51 100644 --- a/lib/cancan/ability.rb +++ b/lib/cancan/ability.rb @@ -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. diff --git a/lib/cancan/model_adapters/abstract_adapter.rb b/lib/cancan/model_adapters/abstract_adapter.rb index 5ce0480..d350a4a 100644 --- a/lib/cancan/model_adapters/abstract_adapter.rb +++ b/lib/cancan/model_adapters/abstract_adapter.rb @@ -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 diff --git a/lib/cancan/model_adapters/active_record_adapter.rb b/lib/cancan/model_adapters/active_record_adapter.rb index 180512e..a444e2e 100644 --- a/lib/cancan/model_adapters/active_record_adapter.rb +++ b/lib/cancan/model_adapters/active_record_adapter.rb @@ -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. # diff --git a/lib/cancan/model_adapters/default_adapter.rb b/lib/cancan/model_adapters/default_adapter.rb new file mode 100644 index 0000000..d76d87f --- /dev/null +++ b/lib/cancan/model_adapters/default_adapter.rb @@ -0,0 +1,7 @@ +module CanCan + module ModelAdapters + class DefaultAdapter < AbstractAdapter + # This adapter is used when no matching adapter is found + end + end +end diff --git a/spec/cancan/ability_spec.rb b/spec/cancan/ability_spec.rb index e022182..a77311b 100644 --- a/spec/cancan/ability_spec.rb +++ b/spec/cancan/ability_spec.rb @@ -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 diff --git a/spec/cancan/model_adapters/active_record_adapter_spec.rb b/spec/cancan/model_adapters/active_record_adapter_spec.rb index d256472..edc935d 100644 --- a/spec/cancan/model_adapters/active_record_adapter_spec.rb +++ b/spec/cancan/model_adapters/active_record_adapter_spec.rb @@ -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 diff --git a/spec/cancan/model_adapters/default_adapter_spec.rb b/spec/cancan/model_adapters/default_adapter_spec.rb new file mode 100644 index 0000000..b257353 --- /dev/null +++ b/spec/cancan/model_adapters/default_adapter_spec.rb @@ -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