How to use CanCanCan with enum field?

2019-04-11 20:45发布

问题:

I got Article model with enum field enum status: [:pending, :done].

Here's my ability file

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new

    if user.member?
      can :read, Article.done
    end
  end
end

In view I am trying to render Article.done collection for member but nothings renders.

<% if can? :read, Article.done %> 
  <%= render partial: 'article', collection: Article.done, as: :article %>
<% end %>

Therefore I have a question: is there any possible way to work with enum in CanCanCan?

回答1:

I may be wrong, but I think that enum only creates instance methods:.

@article = Article.find params[:id]
@article.done?

I was wrong...

Scopes based on the allowed values of the enum field will be provided as well. With the above example:

Conversation.active
Conversation.archived

--

I'll delete this if it doesn't work; I would be evaluating against the hash of conditions, not the class itself:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new

    if user.member?
      can :read, Article, status: Article.statuses[:done]
    end
  end
end

Update

Several important things to consider.

Firstly, when using hash conditions:

Important: If a block or hash of conditions exist they will be ignored when checking on a class, and it will return true.

This means that you cannot call specific conditions on an "ability" whilst passing a class, it has to be called on an instance of an object.

Secondly, it seems CanCanCan has some issues evaluating enum values, which makes the following code necessary:

#app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new
    if user.member?
      can :read, Article do |article|
        article.done?
      end
    end
  end
end

You then need to pass the article instance to the can? method:

#app/views/articles/index.html.erb
<table>
    <%= render @articles %>
</table>

#app/views/articles/_article.html.erb
<% if can? :read, article %>
    <tr>
      <td>Title:</td>
      <td><%= article.title %></td>
      <td><%= article.status %></td>
    </tr>
<% end %>