From 84f4c904b70e8a9738f12550adc5c3126c49e6bf Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Fri, 6 Aug 2010 11:03:10 -0700 Subject: [PATCH] adding :singular option to support has_one associations in load/authorize resource - closes #93 --- lib/cancan/controller_additions.rb | 3 +++ lib/cancan/controller_resource.rb | 22 ++++++++++++++++++---- spec/cancan/controller_resource_spec.rb | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/lib/cancan/controller_additions.rb b/lib/cancan/controller_additions.rb index 338e737..4106544 100644 --- a/lib/cancan/controller_additions.rb +++ b/lib/cancan/controller_additions.rb @@ -67,6 +67,9 @@ module CanCan # [:+through+] # Load this resource through another one. This should match the name of the parent instance variable. # + # [:+singular+] + # Pass +true+ if this is a singular resource through a +has_one+ association. + # # [:+parent+] # True or false depending on if the resource is considered a parent resource. This defaults to +true+ if a resource # name is given which does not match the controller. diff --git a/lib/cancan/controller_resource.rb b/lib/cancan/controller_resource.rb index b7fd98c..0c289d4 100644 --- a/lib/cancan/controller_resource.rb +++ b/lib/cancan/controller_resource.rb @@ -41,12 +41,21 @@ module CanCan def load_resource_instance if !parent? && new_actions.include?(@params[:action].to_sym) - @params[name] ? resource_base.new(@params[name]) : resource_base.new - elsif id_param - resource_base.find(id_param) + build_resource + elsif id_param || @options[:singular] + find_resource end end + def build_resource + method_name = @options[:singular] ? "build_#{name}" : "new" + resource_base.send(*[method_name, @params[name]].compact) + end + + def find_resource + @options[:singular] ? resource_base.send(name) : resource_base.find(id_param) + end + def authorization_action parent? ? :read : @params[:action].to_sym end @@ -77,8 +86,13 @@ 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 :singular option is passed it won't use the association because it needs to be handled later. def resource_base - through_resource ? through_resource.send(name.to_s.pluralize) : resource_class + if through_resource + @options[:singular] ? through_resource : through_resource.send(name.to_s.pluralize) + else + resource_class + end end # The object to load this resource through. diff --git a/spec/cancan/controller_resource_spec.rb b/spec/cancan/controller_resource_spec.rb index 9cd8c0a..fe6728a 100644 --- a/spec/cancan/controller_resource_spec.rb +++ b/spec/cancan/controller_resource_spec.rb @@ -166,6 +166,26 @@ describe CanCan::ControllerResource do @controller.instance_variable_get(:@ability).should == :some_ability end + it "should find record through has_one association with :singular option" do + @params.merge!(:action => "show") + person = Object.new + @controller.instance_variable_set(:@person, person) + stub(person).ability { :some_ability } + resource = CanCan::ControllerResource.new(@controller, :through => :person, :singular => true) + resource.load_resource + @controller.instance_variable_get(:@ability).should == :some_ability + end + + it "should build record through has_one association with :singular option" do + @params.merge!(:action => "create", :ability => :ability_attributes) + person = Object.new + @controller.instance_variable_set(:@person, person) + stub(person).build_ability(:ability_attributes) { :new_ability } + resource = CanCan::ControllerResource.new(@controller, :through => :person, :singular => true) + resource.load_resource + @controller.instance_variable_get(:@ability).should == :new_ability + end + it "should only authorize :read action on parent resource" do @params.merge!(:action => "new", :person_id => 123) stub(Person).find(123) { :some_person }