Defining Abilities with Blocks
Clone this wiki locally
If your conditions are too complex to define in a hash (as shown in Defining Abilities page), you can use a block to define them in Ruby.
can :update, Project do |project| project.priority < 3 end
If the block returns true then the user has that ability, otherwise they will be denied access.
Only for Object Attributes
The block is only evaluated when an actual instance object is present. It is not evaluated when checking permissions on the class (such as in the index action). This means any conditions which are not dependent on the object attributes should be moved outside of the block.
# don't do this can :update, Project do |project| user.admin? # this won't always get called end # do this can :update, Project if user.admin?
See Checking Abilities for more information.
A block's conditions are only executable through Ruby. If you are Fetching Records using
accessible_by it will raise an exception. To fetch records from the database you need to supply an SQL string representing the condition. The SQL will go in the
WHERE clause, if you need to do joins consider using sub-queries or scopes (below).
can :update, Project, ["priority < ?", 3] do |project| project.priority < 3 end
If you are using
load_resource and don't supply this SQL argument, the instance variable will not be set for the index action since they cannot be translated to a database query.
Block Conditions with Scopes
As of CanCan 1.6 it's possible to pass a scope instead of an SQL string when using a block in an ability.
can :read, Article, Article.published do |article| article.published_at <= Time.now end
This is really useful if you have complex conditions which require
joins. A couple caveats:
- You cannot use this with multiple
candefinitions that match the same action and model since it is not possible to combine them. An exception will be raised when that is the case.
- If you use this with
cannot, the scope needs to be the inverse since it's passed directly through. For example, if you don't want someone to read discontinued products the scope will need to fetch non discontinued ones:
cannot :read, Product, Product.where(:discontinued => false) do |product| product.discontinued? end
It is only recommended to use scopes if a situation is too complex for a hash condition.
Overriding All Behavior
You can override all
can behavior by passing no arguments, this is useful when permissions are defined outside of ruby such as when defining Abilities in Database.
can do |action, subject_class, subject| # ... end
Here the block will be triggered for every
can? check, even when only a class is used in the check.