Senjai
Senjai

Reputation: 1816

CanCan, what does passing a block to the can method in Ability#initialize do?

This is my Ability.rb file:

class Ability
  include CanCan::Ability

  def initialize(user)
    user.permissions.each do |permission|
      can permission.action.to_sym,
          permission.thing_type.constantize {|thing| thing.nil? || permission.thing.nil? || permission.thing_id == thing.id}
    end
  end
end

thing is a polymorphic association. I'm trying to understand how passing a block to can works. I've searched throughout the wiki for CanCan but haven't been able to find an explanation.

Upvotes: 1

Views: 1741

Answers (1)

Frederick Cheung
Frederick Cheung

Reputation: 84172

Passing a block to cancan allows you to implement more complicated permission checks that depend on the state of the object itself.

When it's just attributes on the object you want to check then you don't need a block:

can :read, Project, :active => true

allows a user to only read active projects. If you need to call project's editable method then you could instead do

can :read, Project do |project|
  project.editable?
end

At the point that cancan checks whether you can read a particular project (ie when the before_filter fires or you call `can? :read, some_project) then the block gets called

There's a page about this on the wiki: Defining abilities with blocks.

In your case it looks like the intent is that the permission object can either grant access to a whole class (if thing_type is set but thing_id is null) or to a specific instance of the class.

However the code you've posted doesn't actually do this. Of the 2 ways of passing a block, {} binds more tightly than do...end so the block isn't passed to can at all. It is instead passed to constantize, which ignores it. You could disambiguate this by using parentheses, or by using do...end

Upvotes: 1

Related Questions