Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reducing the load of ability.rb in cancan

I have a large ability file that decides what exactly users can do by searching from a table of 'Roles'. Each role corresponds to something a particular user can do, for example, being able to add a project or being able to edit the main company record.

At the moment, every controller action that runs load_and_authorize_resource goes through >30 statements like:

ability.rb (>30 times)
Role.where("user_id = ? AND role = ? AND roleable_type = ? AND roleable_id IS NULL", user.id, "delete", "task").last.present? ? (can :destroy, Task) : nil

This is a horribly inefficient solution because the server is running >30 queries before it even does anything.

The best way to do this would only be to run the queries that need running based on what the controller and view require. Is there a way to do this?

like image 590
sscirrus Avatar asked Feb 04 '12 21:02

sscirrus


1 Answers

It's partly the way you wrote the role tests. Instead of writing > 30 times:

Role.where("user_id = ? AND role = ? AND roleable_type = ? AND roleable_id IS NULL", user.id, "delete", "task").last.present? ? (can :destroy, Task) : nil

You can instead query for all of a users roles at once:

@user_roles = user.roles.all

Then individually test for each role:

can :destroy, Task if @user_roles.detect {|u| u["role"] == "delete" && u["roleable_type"]=="task" }

Because all roles are read into memory in a single query, you don't have 30 queries.

like image 164
ronalchn Avatar answered Nov 07 '22 11:11

ronalchn