Users belong to an organization. Users can view all organizations, but can only view users within their organization. When a user views the organization, I have to add filter logic to the view, so @organizations.users becomes @organizations.users.select{|u| can?(:read, u)}.
Is there a way to manage this more transparently or elegantly? Is there a way to get rid of select{|u| can?(:read, u)} or move it to a DRY place?
Relevant code:
class Organization < ActiveRecord::Base
has_many :users
...
end
class Ability
...
can :read, User, :organization => @user.organization
end
organizations/show.html.erb: (current, sucky version)
<% if @organization.users.select{|u| can?(:read, u)}.any? %>
<h4>Contacts</h4>
<ul class="unstyled">
<% for user in @organization.users.select{|u| can?(:read, u)} %>
<li>
<%= link_to user, user, class: "user" %>
</li>
<% end %>
</ul>
<% end %>
I want something closer to this view instead:
organizations/show.html.erb:
<% if @organization.users.any? %>
<h4>Contacts</h4>
<ul class="unstyled">
<% for user in @organization.users %>
<li>
<%= link_to user, user, class: "user" %>
</li>
<% end %>
</ul>
<% end %>
Update:
I’m avoiding accessible_by because I have an ability definition that uses a block.
class Ability
...
can :read, User, :organization => @user.organization
can :read, User do |user|
user.is? :technical_contact
end
end
I know that accessible_by can work with blocks, but only if a SQL fragment is provided. Since my users have many roles and are stored as a bitmask, I don’t have a SQL fragment.
CanCan has built in support for this with the
accessible_bymethod that is available on your active record models.See https://github.com/ryanb/cancan/wiki/Fetching-Records for more information.