使用块来定义CanCan中的异能会引发异常

时间:2010-08-24 13:45:33

标签: ruby-on-rails cancan

我正在使用Ryan Bate的CanCan gem定义能力,一些基本功能失败。我有一个产品型号和产品控制器,我的索引操作如下所示:

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处理这些块的方式。我更深入地进入库并发现错误的位置 - 在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的支持。

谢谢!

3 个答案:

答案 0 :(得分:6)

好的,所以我浏览了wiki,看到accessible_by在定义块时不起作用。看起来有点奇怪有这个限制并且在README中没有提到它,但至少我知道它是库的限制而不是我的或者Ryan Bates的代码中的错误。对于那些感兴趣的人,上面定义我的能力的正确方法如下:

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

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

答案 1 :(得分:3)

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

作为一种解决方法,我只是做我的发现,因为我通常会在没有 CanCan的情况下执行,然后我使用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, _)}

答案 2 :(得分:0)

我提交了一张票,将其添加到文档中:

https://github.com/ryanb/cancan/issues/issue/276