Sometimes we forget that Ruby on Rails is in fact built on … Ruby. In this post, I want to explore the CanCan gem to see how it works its magic.
This post explores how the CanCan gem (v1.6.10) works internally. Lots of this applies to other gems as well.
If you’re completely unfamiliar with CanCan, the authorization gem from Ryan Bates, I highly recommend you look it up. Very briefly, this gem allows me to write code such as this:
1 2 3 |
|
How simple is that?
On the flip side, the user abilities are defined like this:
1 2 3 4 5 6 7 8 9 10 |
|
The user
param that you see passed into the initialize
method is the result of querying a global current_user
method.
It is your responsibility to create that method, and ensure it return the logged in user. Cancan handles authorization, not authentication.
In these lines of code, we allow the current_user
to :edit
an instance of the Article
model if the author_id
property on the Article matched the id
of the user.
There are lot’s of other options here, so read the well-written wiki.
Make sure you create this class, and name it Ability
. CanCan relies on that convention.
So how does the magic happen? How do we have access to this new method can?
(and cannot?
).
As is the case of any gem, when you add gem 'cancan'
to your Gemfile, and run bundle
, ruby will fetch the gem source-code if doesn’t exist on your system yet, and will load the file in the lib/GEM_NAME.rb, in this case lib/cancan.rb
.
You’ll see that this file simply requires the rest of the gem’s code, all in the lib
dir, namespaced under the cancan
dir.
The first file required is ability.rb
. This is the file that declares the CanCan::Ability
module, which as seen before will get included
in our Ability
class.
The methods defined in this module are what we can call in our Ability
file, such as can :edit, Article, author_id: user.id
.
The next file, rule.rb
, is what handles a lot of the dirty work under the hood. The methods in this file are all for internal use only. This gem has been designed so as to completely isolate code meant to be called directly (such as the can
and authorize
methods) from these methods, which are never meant to be directly referenced.
So it is with the next two required files, controller_resource.rb
and controller_additions.rb
, the former of which is the internal implemention of the latter. It is within this latter file that we see the can?
, and other methods that get called within our views and controllers.
So how do we actually have access to these methods without adding any include
or extend
calls into our model or controller classes?
The magic happens in the last few lines:
1 2 3 4 5 |
|
This checks if we’re using Action Controller, and if we are, it includes
this module into the ActionController::Base
, the module which the ApplicationController
inherits from, which in turn, is the class from which all other controllers inherit.
But to get access to the can
and other methods which are defined in CanCan::ControllerAdditions::ClassMethods
module, we need this bit of code:
1 2 3 4 |
|
This extends
all our application controllers (by way of inheriting from ActionController::Base
), and gives them the ability to respond to those method calls.
It also calls the helper_method
method passing in the :can?
, :cannot?
, and :current_ability
methods, giving our views access to those methods as well.
So that’s the short of it. Although the wiki is very helpful, the inline documentation is more verbose, so if you get stuck, that might be a good place to start. Besides, code organization is a skill hard to pickup, and I’m sure I’ve learned something perusing this codebase, written by a programmer far more skilled than me. And, best of all, Rails is that much less magical. Magic != Fun.
386 Duty Calls: What do you want me to do? LEAVE? Then they’ll keep being wrong!
All Revisions to this document are visible here