diff --git a/lib/cancan.rb b/lib/cancan.rb index b320393a..02cd75e2 100644 --- a/lib/cancan.rb +++ b/lib/cancan.rb @@ -5,3 +5,4 @@ require 'cancan/active_record_additions' require 'cancan/exceptions' require 'cancan/query' +require 'cancan/inherited_resource' diff --git a/lib/cancan/controller_additions.rb b/lib/cancan/controller_additions.rb index d099a8bf..8ed89ccb 100644 --- a/lib/cancan/controller_additions.rb +++ b/lib/cancan/controller_additions.rb @@ -12,7 +12,7 @@ module ClassMethods # end # def load_and_authorize_resource(*args) - ControllerResource.add_before_filter(self, :load_and_authorize_resource, *args) + cancan_resource_class.add_before_filter(self, :load_and_authorize_resource, *args) end # Sets up a before filter which loads the model resource into an instance variable. @@ -103,7 +103,7 @@ def load_and_authorize_resource(*args) # load_resource :new => :build # def load_resource(*args) - ControllerResource.add_before_filter(self, :load_resource, *args) + cancan_resource_class.add_before_filter(self, :load_resource, *args) end # Sets up a before filter which authorizes the resource using the instance variable. @@ -156,7 +156,7 @@ def load_resource(*args) # Authorize conditions on this parent resource when instance isn't available. # def authorize_resource(*args) - ControllerResource.add_before_filter(self, :authorize_resource, *args) + cancan_resource_class.add_before_filter(self, :authorize_resource, *args) end # Add this to a controller to ensure it performs authorization through +authorized+! or +authorize_resource+ call. @@ -190,6 +190,14 @@ def skip_authorization(*args) controller.instance_variable_set(:@_authorized, true) end end + + def cancan_resource_class + if ancestors.map(&:to_s).include? "InheritedResources::Actions" + InheritedResource + else + ControllerResource + end + end end def self.included(base) diff --git a/lib/cancan/controller_resource.rb b/lib/cancan/controller_resource.rb index 27ff1de5..49c57fd1 100644 --- a/lib/cancan/controller_resource.rb +++ b/lib/cancan/controller_resource.rb @@ -26,10 +26,10 @@ def load_and_authorize_resource end def load_resource - if !resource_instance && (parent? || member_action?) - @controller.instance_variable_set("@#{instance_name}", load_resource_instance) + if parent? || member_action? + self.resource_instance ||= load_resource_instance elsif load_collection? - @controller.instance_variable_set("@#{instance_name.to_s.pluralize}", load_collection) + self.collection_instance ||= load_collection end end @@ -41,7 +41,7 @@ def parent? @options.has_key?(:parent) ? @options[:parent] : @name && @name != name_from_controller.to_sym end - private + protected def load_resource_instance if !parent? && new_actions.include?(@params[:action].to_sym) @@ -52,7 +52,6 @@ def load_resource_instance end def load_collection? - !parent? && collection_actions.include?(@params[:action].to_sym) && resource_base.respond_to?(:accessible_by) && !@controller.current_ability.has_block?(authorization_action, resource_class) end @@ -110,10 +109,22 @@ def resource_class_with_parent parent_resource ? {parent_resource => resource_class} : resource_class end + def resource_instance=(instance) + @controller.instance_variable_set("@#{instance_name}", instance) + end + def resource_instance @controller.instance_variable_get("@#{instance_name}") end + def collection_instance=(instance) + @controller.instance_variable_set("@#{instance_name.to_s.pluralize}", instance) + end + + def collection_instance + @controller.instance_variable_get("@#{instance_name.to_s.pluralize}") + end + # 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 :singleton option is passed it won't use the association because it needs to be handled later. diff --git a/lib/cancan/inherited_resource.rb b/lib/cancan/inherited_resource.rb new file mode 100644 index 00000000..bcd55fb7 --- /dev/null +++ b/lib/cancan/inherited_resource.rb @@ -0,0 +1,18 @@ +module CanCan + # For use with Inherited Resources + class InheritedResource < ControllerResource # :nodoc: + def load_resource_instance + if parent? + @controller.parent + elsif new_actions.include? @params[:action].to_sym + @controller.build_resource + else + @controller.resource + end + end + + def resource_base + @controller.end_of_association_chain + end + end +end diff --git a/spec/cancan/controller_additions_spec.rb b/spec/cancan/controller_additions_spec.rb index 8d514aa7..55d1eb7b 100644 --- a/spec/cancan/controller_additions_spec.rb +++ b/spec/cancan/controller_additions_spec.rb @@ -74,4 +74,13 @@ @controller_class.check_authorization(:some_options) }.should_not raise_error(CanCan::AuthorizationNotPerformed) end + + it "cancan_resource_class should be ControllerResource by default" do + @controller.class.cancan_resource_class.should == CanCan::ControllerResource + end + + it "cancan_resource_class should be InheritedResource when class includes InheritedResources::Actions" do + stub(@controller.class).ancestors { ["InheritedResources::Actions"] } + @controller.class.cancan_resource_class.should == CanCan::InheritedResource + end end diff --git a/spec/cancan/inherited_resource_spec.rb b/spec/cancan/inherited_resource_spec.rb new file mode 100644 index 00000000..d0bed43c --- /dev/null +++ b/spec/cancan/inherited_resource_spec.rb @@ -0,0 +1,40 @@ +require "spec_helper" + +describe CanCan::InheritedResource do + before(:each) do + @params = HashWithIndifferentAccess.new(:controller => "projects") + @controller = Object.new # simple stub for now + @ability = Ability.new(nil) + stub(@controller).params { @params } + stub(@controller).current_ability { @ability } + end + + it "show should load resource through @controller.resource" do + @params[:action] = "show" + stub(@controller).resource { :project_resource } + CanCan::InheritedResource.new(@controller).load_resource + @controller.instance_variable_get(:@project).should == :project_resource + end + + it "new should load through @controller.build_resource" do + @params[:action] = "new" + stub(@controller).build_resource { :project_resource } + CanCan::InheritedResource.new(@controller).load_resource + @controller.instance_variable_get(:@project).should == :project_resource + end + + it "index should load through @controller.parent when parent" do + @params[:action] = "index" + stub(@controller).parent { :project_resource } + CanCan::InheritedResource.new(@controller, :parent => true).load_resource + @controller.instance_variable_get(:@project).should == :project_resource + end + + it "index should load through @controller.end_of_association_chain" do + @params[:action] = "index" + stub(Project).accessible_by(@ability) { :projects } + stub(@controller).end_of_association_chain { Project } + CanCan::InheritedResource.new(@controller).load_resource + @controller.instance_variable_get(:@projects).should == :projects + end +end