views:

21

answers:

1

I am using Ryan Bate's CanCan gem to define abilities and some basic functionality is failing. I have a Product model and Products controller where my index action looks like this:

def index
    @req_host_w_port = request.host_with_port
    @products = Product.accessible_by(current_ability, :index)
end

I get an error when I try to retrieve the products with the accessible_by method, I get this error:

Cannot determine SQL conditions or joins from block for :index Product(id: integer, secret: string, context_date: datetime, expiration_date: datetime, blurb: text, created_at: datetime, updated_at: datetime, owner_id: integer, product_type_id: integer, approved_at: datetime)

My ability class looks like this:

can :index, Product do |product|
   product && !!product.approved_at
end

This seems like a very simple example so I am surprised it is failing and wondering if I am overlooking something simple (i.e. staring at my code for too long).

I did probe a bit more and ran a simple test. If you look at the code below, one example works fine and one fails where they should actually do the same exact thing.

# This works
can :index, Product, :approved_at => nil

# This fails
can :index, Product do |product|
    product && product.approved_at.nil?
end

So the problem seems to be in the way CanCan is processing these blocks. I dove deeper into the library and found where the error was raised - in CanCan's ability class definition:

def relevant_can_definitions_for_query(action, subject)
      relevant_can_definitions(action, subject).each do |can_definition|
        if can_definition.only_block?
          raise Error, "Cannot determine SQL conditions or joins from block for #{action.inspect} #{subject.inspect}"
        end
      end
    end

So I checked out what this only_block? method is. The method returns true if a can definition has a block but no conditions which makes no sense to me because the whole point of the block is to define the conditions within the block when they are too complicated for the following syntax:

can :manage, [Account], :account_manager_id => user.id

Any insight on this issue would be great! I also filed an issue on the CanCan github page but I'm getting to the point where I may need to ditch the library. However, I understand that many people are using CanCan successfully and this is such basic functionality I think I must be doing something wrong. Especially since the git repo was updated 3 days ago and Ryan Bates mentions Rails 3 support in the README.

Thanks!

A: 

Ok so I went through the wiki and saw that accessible_by will not work when defining blocks. Seems a bit odd to have this restriction and not mention it in the README, but at least I know it's a limitation of the library and not a bug in mine or Ryan Bates' code. For those interested, the proper way to define my ability above is as follows:

# will fail    
can :index, Product do |product|
   product && product.approved_at.nil?
end

# will pass
can :index, Product
cannot :index, Product, :approved_at => nil
Tony