Skip to content

Defining Abilities with Blocks

ryanb edited this page Oct 5, 2010 · 14 revisions

If Defining Abilities with Hashes is not flexible enough for your needs, it is possible to use a block. Here you can use any Ruby code to restrict what the user is able to access.

can :update, Project do |project|
  project.groups.include?(user.group)
end

If the block returns true then the user has that :update ability for that project, otherwise ability checking will pass through up to the next matching definition.

As of 1.4, the block is not called when checking permission on a class which means you no longer need to check for nil. This makes it consistent with Defining Abilities with Hashes.

can :read, Project do |project|
  project.publicly_available?
end
can? :read, Project # returns true without triggering block
can? :read, @project # triggers block

Also new in 1.4 is that only the object is passed into the block, never the action or class. See Upgrading to 1.4 for details.

The downside to using a block is that it cannot be used when Fetching Records. But as of 1.4 you could specify raw SQL condition in addition to a block.

can :update, Project, ['projects.id in (select gp.project_id 
                                        from groups_projects gp
                                        where gp.group_id = ?)', user.group_id] do |project|
  project.groups.include?(user.group)
end

Here that SQL will be used when fetching records with accessible_by.

If additional arguments are passed to the can? method, those arguments will be passed into the block. This is useful for passing additional information about the request.

# in controller or view
can? :read, article, request.remote_ip

# in ability
can :read, Article do |article, remote_ip|
  # ...
end

See Accessing Request Data for an alternative solution.

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