2010-08-24 52 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