disclaimer: for the sake of simplicity, I omit some calls to short inner methods intentionally. The full chain of calling can be obtained by following load_and_authorize_resource
method definition and so forth.
As stated in documentation, load_and_authorize_resource
sets up a before_filter
...
# cancan/lib/cancan/controller_additions.rb
def load_and_authorize_resource(*args)
cancan_resource_class.add_before_filter(self, :load_and_authorize_resource, *args)
end
...which calls two methods: load_resource
and authorize_resource
.
# cancan/lib/cancan/controller_resource.rb
def load_and_authorize_resource
load_resource
authorize_resource
end
To get the idea of their behaviour we're going to look at both of them closely.
Based on params
hash which was passed to your controller action, load_resource
makes a decision on whether it should obtain a new instance of a class (e.g. Post.new
) or find
a particular instance based on params[:id]
(e.g. Post.find(params[:id])
). That instance (or a collection of instances for actions like index
) is assigned to corresponding instance variable of your controller action.
# cancan/lib/cancan/controller_resource.rb
def load_resource
unless skip?(:load)
if load_instance?
# here you have obtained your object, e.g. Post with id=5
# and placed it into cancan resource_instance variable.
# it has automatically set up @post instance variable for you
# in your action
self.resource_instance ||= load_resource_instance
elsif load_collection?
self.collection_instance ||= load_collection
end
end
end
Later on, authorize_resource
gets called. Its inner logics syntax should be familiar to you: checking abilities by hands looks just the same as what happens inside of this method. Basically you take a resource_instance
obtained at the previous step, params[:action]
which is the name of a current action, and check if particular action can be accessed for given object(s).
# cancan/lib/cancan/controller_resource.rb
def authorize_resource
unless skip?(:authorize)
# similar to what happens when you call authorize!(:show, @post)
@controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
end
end
As long as raising exceptions inside of before_filter
stops controller action from being executed, failing to pass authorization here gets you redirected to your application's home url, shown 500 error page or whatever behaviour you defined for CanCan::AccessDenied
handling.
On the other hand, in case you've passed authorization successfully, your action code gets executed. Now you've got access to instance variable (e.g. @post
) which has been set up by CanCan
at load_resource
step.