Reputation: 2195
I'm trying to give access where an association exists using cancan.
It needs to work with both object attributes and accessible_by selection (so I can't use a block, unless someone tells me how I get around the error when I do that).
I've come up with a brutal hack.
lead.rb
has_many :recordings
has_many :emails
ability.rb
can :manage, Lead
can :manage, Recording, :lead_id => (1..Lead.pluck(:id).max).to_a
can :manage, Email, :lead_id => (1..Lead.pluck(:id).max).to_a
What I mean is lead_id is not null...
Is there any way I can do that without creating a 100,000 item array every time?
Extra info: It's worth noting that these are not the only controls in place on the Recording and Emails models, so it's important that I can add the extra permissions rather than reset and express them negatively.
Upvotes: 1
Views: 3096
Reputation: 3489
I had this need, and here's a solution that is probably much faster. You don't have to hit the database with a pluck
, and there's no reason to change a range to an array, as checking the range works fine (I'm not sure if this uses a range internally in the library, but ideally it would).
can :manage, [Recording, Email], :lead_id => (1..(2**31-1))
I'm using cancancan
.
Upvotes: 0
Reputation: 28305
There are two ways to achieve this:
This is the recommended approach. Use this, unless you have a good reason not to.
The idea here is to combine a cannot
ability, in conjunction with can
:
can :manage, Recording
cannot :manage, Recording, lead_id: nil
Note that the order is important here, so that the rule overriding is correct.
By defining the ability with a block, you can form more complicated queries.
A simple implementation would be as follows:
can :manage, Recording do |recording|
!recording.lead_id.nil?
end
However, in order to combine this ability with others, you must also specify the SQL conditions when fetching records. This additional SQL controls load_resource
actions, such as index
:
can :manage, Recording, ["lead_id IS NOT NULL"] do |recording|
!recording.lead_id.nil?
end
In order to keep this logic DRY, you could also consider defining your permissions in a block such as:
[Recording, Email].each do |model|
can :manage, model
cannot :manage, model, lead_id: nil
end
Upvotes: 6