diff --git a/lib/cancan/controller_additions.rb b/lib/cancan/controller_additions.rb index 8ed89cc..3db1f1f 100644 --- a/lib/cancan/controller_additions.rb +++ b/lib/cancan/controller_additions.rb @@ -71,6 +71,9 @@ module CanCan # [:+through+] # Load this resource through another one. This should match the name of the parent instance variable. # + # [:+shallow+] + # Pass +true+ to allow this resource to be loaded directly when parent is +nil+. Defaults to +false+. + # # [:+singleton+] # Pass +true+ if this is a singleton resource through a +has_one+ association. # diff --git a/lib/cancan/controller_resource.rb b/lib/cancan/controller_resource.rb index 6c71ef1..62435f8 100644 --- a/lib/cancan/controller_resource.rb +++ b/lib/cancan/controller_resource.rb @@ -127,10 +127,17 @@ module CanCan # The object that methods (such as "find", "new" or "build") are called on. # If the :through option is passed it will go through an association on that instance. + # If the :shallow option is passed it will use the resource_class if there's no parent # If the :singleton option is passed it won't use the association because it needs to be handled later. def resource_base - if parent_resource - @options[:singleton] ? parent_resource : parent_resource.send(name.to_s.pluralize) + if @options[:through] + if parent_resource + @options[:singleton] ? parent_resource : parent_resource.send(name.to_s.pluralize) + elsif @options[:shallow] + resource_class + else + raise AccessDenied # maybe this should be a record not found error instead? + end else resource_class end diff --git a/spec/cancan/controller_resource_spec.rb b/spec/cancan/controller_resource_spec.rb index 7e490ca..c8bb86c 100644 --- a/spec/cancan/controller_resource_spec.rb +++ b/spec/cancan/controller_resource_spec.rb @@ -174,12 +174,22 @@ describe CanCan::ControllerResource do @controller.instance_variable_get(:@project).should == :some_project end - it "should not load through parent resource if instance isn't loaded" do + it "should not load through parent resource if instance isn't loaded when shallow" do + @params.merge!(:action => "show", :id => 123) + stub(Project).find(123) { :some_project } + resource = CanCan::ControllerResource.new(@controller, :through => :category, :shallow => true) + resource.load_resource + @controller.instance_variable_get(:@project).should == :some_project + end + + it "should raise AccessDenied when attempting to load resource through nil" do @params.merge!(:action => "show", :id => 123) stub(Project).find(123) { :some_project } resource = CanCan::ControllerResource.new(@controller, :through => :category) - resource.load_resource - @controller.instance_variable_get(:@project).should == :some_project + lambda { + resource.load_resource + }.should raise_error(CanCan::AccessDenied) + @controller.instance_variable_get(:@project).should be_nil end it "should authorize nested resource through parent association on index action" do