Hello, CanCanCan 3.0

All the changes in version 3.0 of the most used and ❤️ authorization framework in Ruby On Rails

Alessandro Rodi
4 min readApr 3, 2019

Hello everyone,
CanCanCan 3.0 is here! 🎉
After almost two years from the release of version 2.0, the next major version is ready and it comes with some new exciting features.

Breaking changes

Let’s start with the bad news: is a major version, and therefore there will be some breaking changes.

Defining abilities without a subject is not allowed anymore

For example, can :dashboard is not going to be accepted anymore and will raise an exception.
All these kind of rules need to be re-thought in terms of can :action, subject. can :read, :dashboard for example.

Eager loading is not automatic anymore

If you relied on CanCanCan to avoid N+1 queries, this will not be the case anymore.
From now on, all necessary includes, preload or eager_load need to be explicitly written.
We strongly suggest having bullet gem installed to identify your possible N+1 issues.
The preloading of associations should have never been the purpose of an authorization gem, but this was necessary because of the lack of a left_joins method in ActiveRecord before version 5.

Use of distinct.

The uniqueness of the results is guaranteed by using the distinct clause in the final query.
This may cause issues with some existing queries when using clauses like group by or order on associations.
Adding a custom select may be necessary in such cases.

Ability#merge.

When merging different Ability files, the aliases are now get merged as well. This might cause some incompatibility issues.

Enough with the breaking changes! CanCanCan release candidate has already been tested successfully on over many major projects, and we believe your upgrade will be smooth and you will be able to start benefit from all the improvements.

New features

Support for Rails 6.0

Although still in Beta, we worked on making CanCanCan already compatible with Rails 6.0 so that when you will migrate you won’t be stuck by us. 💪

Attribute level rules

This is a major feature that developers wanted for a long time. Thanks to the work of Tad Thorley this is finally here! Let’s see together how to define and use attribute-level rules.

Given you want users to only read a user first name and last name you can define:

can :read, User, :first_name, :last_name

and check it with:

can? :read, @user, :first_name

You can also ask for all the allowed attributes:

current_ability.permitted_attributes(:read, @user)
#=> [:first_name, :last_name]

Better support for enums

You can now use symbols when defining your rules over enum attributes.

# version 2.x
can :read, Shape, color: Shape.colors[:green]
# version 3.x
can :read, Shape, color: :green

The Rules Compressor

CanCanCan best practice is to give incremental permissions. By default, the user is not allowed to perform any action, and we can increasingly add permissions depending on the user role. An example could look like:

can :read, Post, public: trueif user.present?
can :read, Post, user: { id: user.id }
if user.admin?
can :read, Post
end
end

One of the key features of CanCanCan is the ability to use your rules definition to check permissions on a single instance (can? :read, @post) and list all the resources that the user can access (Post.accessible_by(current_ability)). The following query was generated in CanCanCan 2.x.

# not logged in
SELECT *
FROM posts
WHERE posts.public = true
# logged in
SELECT *
FROM posts
LEFT JOIN users on posts.user_id = users.id
WHERE posts.public = true OR users.id = ?
# admin
SELECT *
FROM posts
LEFT JOIN users on posts.user_id = users.id
WHERE posts.public = true OR users.id = ? OR 1 = 1

CanCanCan 3.0 is able to recognise rules that are not needed and change the last query into:

# admin
SELECT *
FROM posts;

this is a very simple example of the optimizations that CanCanCan will do, and this will save a lot of time for complex rules definitions.
Compressing rules means unnecessary JOIN and WHERE conditions will be skipped and is one of the improvements we did for the fetching of records.

Tests run on SQLite and Postgres

We now extensively run all our tests on both SQLite and Postgres.

and much more…

These are only the main, new, interesting features introduced in version 3.0. We have now better support for tables references twice, through associations, translations, and we also updated our README and documentation to be more developer friendly. Please look at the CHANGELOG for a complete list of the changes.

In the next versions, we will improve the syntax for rules definition and optimise accessible_by even further. We will also have better support for big projects where abilities are split into different files.

Thanks again to Renuo for supporting this project. Having you as a backer makes it really solid and trustworthy. 🙇
Thanks to all the contributors that made it possible to version 3.0. 💪
Thanks to all the developers that keep using and supporting the project, is really cool to keep moving forward with all of you.
Please star the project on Github if you haven’t done it yet 😊

--

--

Alessandro Rodi

Open Source Software Engineer at Renuo AG. Located in Zürich. I do stuff. Sometimes.