2010-08-24 18 views
3

我使用Ryan Bate的CanCan gem来定义功能,并且一些基本功能失败。我有产品型号和产品控制在我的索引操作是这样的:在CanCan中使用块定义能力引发异常

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

当我尝试用accessible_by方法来获取产品我得到一个错误,我得到这个错误:

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) 

我的能力类看起来是这样的:

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

这似乎是一个很简单的例子,所以我很惊讶它是失败的,不知道如果我可以俯瞰简单的东西(即盯着我的代码太久了)。

我确实探测了一下,并进行了一个简单的测试。如果你看下面的代码,一个例子工作正常,一个失败,他们应该实际上做同样的事情。

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

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

所以问题似乎是CanCan处理这些块的方式。我一头深入到图书馆,发现那里的错误被提出 - 在康康舞的能力类的定义:

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 

所以我检查了这only_block?方法是什么。如果can定义有一个块但没有条件对我没有意义,则该方法返回true,因为该块的整个点将定义该块内的条件,因为它们对于以下语法来说过于复杂:

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

对这个问题的任何见解都会很棒!我还在CanCan github页面上提出了一个问题,但是我正要到可能需要丢弃图书馆的地步。不过,据我所知,很多人都成功地使用了CanCan,并且这是基本的功能,我认为我必须做出错误的事情。特别是自从git repo在3天前更新之后,Ryan Bates在自述文件中提到了Rails 3的支持。

谢谢!

回答

6

好的,所以我浏览了wiki并看到accessible_by定义块时不起作用。似乎有点奇怪有这个限制,并没有在自述文件中提及它,但至少我知道这是图书馆的限制,而不是我的或瑞安贝茨的代码中的错误。对于那些有兴趣,要定义上面我的能力的正确方法如下:

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

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

我碰到的这个问题时,有时你只能通过块(Ruby代码)表达能力/权限,并且不能将其表示为SQL条件或命名范围。

作为一种变通方法,我只是尽我的发现,因为我通常会做没有惨惨,然后我筛选定下使用select!只包含的记录的子集,该用户实际上有权查看:

# Don't use accessible_by 
@blog_posts = BlogPost.where(...) 
@blog_posts.select! {|blog_post| can?(:read, blog_post)} 

或者更一般地:

@objects = Object.all 
@objects.select! {|_| can?(:read, _)} 
+0

不错的解决方法,我将现在使用这一个。 – sevenseacat 2012-03-15 02:23:02