Define abilities with blocks
If your conditions are too complex to define in a hash of conditions, you can use a block to define them in Ruby.
can :update, Project do |project|
project.priority < 3
endNote that if you pass a block to a can or cannot, the block only executes if an instance of a class is passed to can? or cannot? calls.
If you define a can or cannot with a block and an object is not passed, the check will pass.
can :update, Project do |project|
false
endcan? :update, Project # returns true!Fetching Records
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:
can :update, Project, ["priority < ?", 3] do |project|
project.priority < 3
endIf you are using
load_resourceand don't supply this SQL argument, the instance variable will not be set for theindexaction since they cannot be translated to a database query.
Block Conditions with ActiveRecord Scopes
It's also 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
endThis is really useful if you have complex conditions which require joins. A couple of 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?
endIt is only recommended to use scopes if a situation is too complex for a hash condition.
