Skip to content
Browse files

adding skip load and authorize behavior - closes #164

  • Loading branch information...
1 parent 71ceb83 commit 57327119a865928ecd939c09de2f25af49651511 @ryanb committed Jan 8, 2011
View
50 lib/cancan/controller_additions.rb
@@ -166,6 +166,52 @@ def authorize_resource(*args)
cancan_resource_class.add_before_filter(self, :authorize_resource, *args)
end
+ # Skip both the loading and authorization behavior of CanCan for this given controller. This is primarily
+ # useful to skip the behavior of a superclass. You can pass :only and :except options to specify which actions
+ # to skip the effects on. It will apply to all actions by default.
+ #
+ # class ProjectsController < SomeOtherController
+ # skip_load_and_authorize_resource :only => :index
+ # end
+ #
+ # You can also pass the resource name as the first argument to skip that resource.
+ def skip_load_and_authorize_resource(*args)
+ skip_load_resource(*args)
+ skip_authorize_resource(*args)
+ end
+
+ # Skip both the loading behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
+ # only do authorization on certain actions. You can pass :only and :except options to specify which actions to
+ # skip the effects on. It will apply to all actions by default.
+ #
+ # class ProjectsController < ApplicationController
+ # load_and_authorize_resource
+ # skip_load_resource :only => :index
+ # end
+ #
+ # You can also pass the resource name as the first argument to skip that resource.
+ def skip_load_resource(*args)
+ options = args.extract_options!
+ name = args.first
+ cancan_skipper[:load][name] = options
+ end
+
+ # Skip both the authorization behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
+ # only do loading on certain actions. You can pass :only and :except options to specify which actions to
+ # skip the effects on. It will apply to all actions by default.
+ #
+ # class ProjectsController < ApplicationController
+ # load_and_authorize_resource
+ # skip_authorize_resource :only => :index
+ # end
+ #
+ # You can also pass the resource name as the first argument to skip that resource.
+ def skip_authorize_resource(*args)
+ options = args.extract_options!
+ name = args.first
+ cancan_skipper[:authorize][name] = options
+ end
+
# Add this to a controller to ensure it performs authorization through +authorized+! or +authorize_resource+ call.
# If neither of these authorization methods are called, a CanCan::AuthorizationNotPerformed exception will be raised.
# This is normally added to the ApplicationController to ensure all controller actions do authorization.
@@ -209,6 +255,10 @@ def cancan_resource_class
ControllerResource
end
end
+
+ def cancan_skipper
+ @_cancan_skipper ||= {:authorize => {}, :load => {}}
+ end
end
def self.included(base)
View
27 lib/cancan/controller_resource.rb
@@ -26,21 +26,38 @@ def load_and_authorize_resource
end
def load_resource
- if load_instance?
- self.resource_instance ||= load_resource_instance
- elsif load_collection?
- self.collection_instance ||= load_collection
+ unless skip?(:load)
+ if load_instance?
+ self.resource_instance ||= load_resource_instance
+ elsif load_collection?
+ self.collection_instance ||= load_collection
+ end
end
end
def authorize_resource
- @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
+ unless skip?(:authorize)
+ @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
+ end
end
def parent?
@options.has_key?(:parent) ? @options[:parent] : @name && @name != name_from_controller.to_sym
end
+ def skip?(behavior) # This could probably use some refactoring
+ options = @controller.class.cancan_skipper[behavior][@name]
+ if options.nil?
+ false
+ elsif options == {}
+ true
+ elsif options[:except] && ![options[:except]].flatten.include?(@params[:action].to_sym)
+ true
+ elsif [options[:only]].flatten.include?(@params[:action].to_sym)
+ true
+ end
+ end
+
protected
def load_resource_instance
View
30 spec/cancan/controller_additions_spec.rb
@@ -83,4 +83,34 @@
stub(@controller.class).ancestors { ["InheritedResources::Actions"] }
@controller.class.cancan_resource_class.should == CanCan::InheritedResource
end
+
+ it "cancan_skipper should be an empty hash with :authorize and :load options and remember changes" do
+ @controller_class.cancan_skipper.should == {:authorize => {}, :load => {}}
+ @controller_class.cancan_skipper[:load] = true
+ @controller_class.cancan_skipper[:load].should == true
+ end
+
+ it "skip_authorize_resource should add itself to the cancan skipper with given model name and options" do
+ @controller_class.skip_authorize_resource(:project, :only => [:index, :show])
+ @controller_class.cancan_skipper[:authorize][:project].should == {:only => [:index, :show]}
+ @controller_class.skip_authorize_resource(:only => [:index, :show])
+ @controller_class.cancan_skipper[:authorize][nil].should == {:only => [:index, :show]}
+ @controller_class.skip_authorize_resource(:article)
+ @controller_class.cancan_skipper[:authorize][:article].should == {}
+ end
+
+ it "skip_load_resource should add itself to the cancan skipper with given model name and options" do
+ @controller_class.skip_load_resource(:project, :only => [:index, :show])
+ @controller_class.cancan_skipper[:load][:project].should == {:only => [:index, :show]}
+ @controller_class.skip_load_resource(:only => [:index, :show])
+ @controller_class.cancan_skipper[:load][nil].should == {:only => [:index, :show]}
+ @controller_class.skip_load_resource(:article)
+ @controller_class.cancan_skipper[:load][:article].should == {}
+ end
+
+ it "skip_load_and_authore_resource should add itself to the cancan skipper with given model name and options" do
+ @controller_class.skip_load_and_authorize_resource(:project, :only => [:index, :show])
+ @controller_class.cancan_skipper[:load][:project].should == {:only => [:index, :show]}
+ @controller_class.cancan_skipper[:authorize][:project].should == {:only => [:index, :show]}
+ end
end
View
52 spec/cancan/controller_resource_spec.rb
@@ -3,10 +3,12 @@
describe CanCan::ControllerResource do
before(:each) do
@params = HashWithIndifferentAccess.new(:controller => "projects")
- @controller = Object.new # simple stub for now
+ @controller_class = Class.new
+ @controller = @controller_class.new
@ability = Ability.new(nil)
stub(@controller).params { @params }
stub(@controller).current_ability { @ability }
+ stub(@controller_class).cancan_skipper { {:authorize => {}, :load => {}} }
end
it "should load the resource into an instance variable if params[:id] is specified" do
@@ -339,4 +341,52 @@
CanCan::ControllerResource.new(@controller, :nested => :project)
}.should raise_error(CanCan::ImplementationRemoved)
end
+
+ it "should skip resource behavior for :only actions in array" do
+ stub(@controller_class).cancan_skipper { {:load => {nil => {:only => [:index, :show]}}} }
+ @params.merge!(:action => "index")
+ CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
+ CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load).should be_false
+ @params.merge!(:action => "show")
+ CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
+ @params.merge!(:action => "other_action")
+ CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
+ end
+
+ it "should skip resource behavior for :only one action on resource" do
+ stub(@controller_class).cancan_skipper { {:authorize => {:project => {:only => :index}}} }
+ @params.merge!(:action => "index")
+ CanCan::ControllerResource.new(@controller).skip?(:authorize).should be_false
+ CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_true
+ @params.merge!(:action => "other_action")
+ CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_false
+ end
+
+ it "should skip resource behavior :except actions in array" do
+ stub(@controller_class).cancan_skipper { {:load => {nil => {:except => [:index, :show]}}} }
+ @params.merge!(:action => "index")
+ CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
+ @params.merge!(:action => "show")
+ CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
+ @params.merge!(:action => "other_action")
+ CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
+ CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load).should be_false
+ end
+
+ it "should skip resource behavior :except one action on resource" do
+ stub(@controller_class).cancan_skipper { {:authorize => {:project => {:except => :index}}} }
+ @params.merge!(:action => "index")
+ CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_false
+ @params.merge!(:action => "other_action")
+ CanCan::ControllerResource.new(@controller).skip?(:authorize).should be_false
+ CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_true
+ end
+
+ it "should skip loading and authorization" do
+ stub(@controller_class).cancan_skipper { {:authorize => {nil => {}}, :load => {nil => {}}} }
+ @params.merge!(:action => "new")
+ resource = CanCan::ControllerResource.new(@controller)
+ lambda { resource.load_and_authorize_resource }.should_not raise_error
+ @controller.instance_variable_get(:@project).should be_nil
+ end
end
View
4 spec/cancan/inherited_resource_spec.rb
@@ -3,10 +3,12 @@
describe CanCan::InheritedResource do
before(:each) do
@params = HashWithIndifferentAccess.new(:controller => "projects")
- @controller = Object.new # simple stub for now
+ @controller_class = Class.new
+ @controller = @controller_class.new
@ability = Ability.new(nil)
stub(@controller).params { @params }
stub(@controller).current_ability { @ability }
+ stub(@controller_class).cancan_skipper { {:authorize => {}, :load => {}} }
end
it "show should load resource through @controller.resource" do

0 comments on commit 5732711

Please sign in to comment.
Something went wrong with that request. Please try again.