From cd217eb9cf8aafe738792581ddfd6ece1eece181 Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Sun, 13 Dec 2009 11:39:02 -0800 Subject: [PATCH] adding :nested option for load_resource - closes #10 --- CHANGELOG.rdoc | 2 ++ lib/cancan/controller_additions.rb | 7 ++++- lib/cancan/resource_authorization.rb | 35 ++++++++++++++++++++-- spec/cancan/resource_authorization_spec.rb | 22 +++++++++++++- spec/spec_helper.rb | 4 +++ 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index e13ce22..4f313a0 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -1,3 +1,5 @@ +* Adding :nested option to load resource method - see issue #10 + * Pass :only and :except options to before filters for load/authorize resource methods. * Adding :collection and :new options to load_resource method so we can specify behavior of additional actions if needed. diff --git a/lib/cancan/controller_additions.rb b/lib/cancan/controller_additions.rb index cb7e7c3..676913d 100644 --- a/lib/cancan/controller_additions.rb +++ b/lib/cancan/controller_additions.rb @@ -35,7 +35,12 @@ module CanCan # # [:+except+] # Does not apply before filter to given actions. - # + # + # [:+nested+] + # Specify which resource this is nested under. + # + # load_resource :nested => :author + # # [:+collection+] # Specify which actions are resource collection actions in addition to :+index+. This # is usually not necessary because it will try to guess depending on if an :+id+ diff --git a/lib/cancan/resource_authorization.rb b/lib/cancan/resource_authorization.rb index fad7a82..01d2e4b 100644 --- a/lib/cancan/resource_authorization.rb +++ b/lib/cancan/resource_authorization.rb @@ -14,11 +14,20 @@ module CanCan end def load_resource + load_parent if @options[:nested] unless collection_actions.include? params[:action].to_sym if new_actions.include? params[:action].to_sym - self.model_instance = model_class.new(params[model_name.to_sym]) - else - self.model_instance = model_class.find(params[:id]) if params[:id] + if parent_instance + self.model_instance = parent_instance.send(model_name.pluralize).build(params[model_name.to_sym]) + else + self.model_instance = model_class.new(params[model_name.to_sym]) + end + elsif params[:id] + if parent_instance + self.model_instance = parent_instance.send(model_name.pluralize).find(params[:id]) + else + self.model_instance = model_class.find(params[:id]) + end end end end @@ -37,6 +46,18 @@ module CanCan model_name.camelcase.constantize end + def load_parent + self.parent_instance = parent_class.find(parent_id) + end + + def parent_class + @options[:nested].to_s.camelcase.constantize + end + + def parent_id + @params["#{@options[:nested]}_id".to_sym] + end + def model_instance @controller.instance_variable_get("@#{model_name}") end @@ -45,6 +66,14 @@ module CanCan @controller.instance_variable_set("@#{model_name}", instance) end + def parent_instance + @controller.instance_variable_get("@#{@options[:nested]}") + end + + def parent_instance=(instance) + @controller.instance_variable_set("@#{@options[:nested]}", instance) + end + def collection_actions [:index] + [@options[:collection]].flatten end diff --git a/spec/cancan/resource_authorization_spec.rb b/spec/cancan/resource_authorization_spec.rb index 3148d81..ccda257 100644 --- a/spec/cancan/resource_authorization_spec.rb +++ b/spec/cancan/resource_authorization_spec.rb @@ -78,8 +78,28 @@ describe CanCan::ResourceAuthorization do end it "should not try to load resource for other action if params[:id] is undefined" do - authorization = CanCan::ResourceAuthorization.new(@controller, {:controller => "abilities", :action => "list"}) + authorization = CanCan::ResourceAuthorization.new(@controller, :controller => "abilities", :action => "list") authorization.load_resource @controller.instance_variable_get(:@ability).should be_nil end + + it "should load nested resource and fetch other resource through the association" do + person = Object.new + stub(Person).find(456) { person } + stub(person).abilities.stub!.find(123) { :some_ability } + authorization = CanCan::ResourceAuthorization.new(@controller, {:controller => "abilities", :action => "show", :id => 123, :person_id => 456}, {:nested => :person}) + authorization.load_resource + @controller.instance_variable_get(:@person).should == person + @controller.instance_variable_get(:@ability).should == :some_ability + end + + it "should load nested resource and fetch build resource through the association" do + person = Object.new + stub(Person).find(456) { person } + stub(person).abilities.stub!.build({:foo => :bar}) { :some_ability } + authorization = CanCan::ResourceAuthorization.new(@controller, {:controller => "abilities", :action => "new", :person_id => 456, :ability => {:foo => :bar}}, {:nested => :person}) + authorization.load_resource + @controller.instance_variable_get(:@person).should == person + @controller.instance_variable_get(:@ability).should == :some_ability + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8e7e8aa..72a56f5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,3 +16,7 @@ class Ability def initialize(user) end end + +# this class helps out in testing nesting +class Person +end