2

I have three model classes: PatientBoard, Medication and Customer, which relates to each other like the following:

PatientBoard

associations do 
      has_and_belongs_to_many :customers
      has_many :medications
end

Medication

associations do 
      belongs_to :patient_board
end

Customer

associations do 
      has_many :patient_boards
end

Due to the has_and_belongs_to_many relationship, my patient_board object looks like this:

{
  _id: ObjectId("5e160f235eb3607d57f59341"), 
  name: "test",
  customer_ids: [ObjectId("5db1e6f45eb36059a4d98e7f")]
}

Where the ids from all the customers who have access to that patient board will be inside the customer_ids array.

The problem

When trying to set the permissions to the user, I did it like the following for the PatientBoard:

can :manage, PatientBoard do |pb|
    pb.customer_ids.include? user.id
end

It works for the most part. But the create method still wouldn't work, so I had to insert the following line too:

can :create, PatientBoard

Now it works normally, but when I tried to do the same for the Medication it didn't work:

can :manage, Medication do |m|
    m.patient_board.customer_ids.include? user.id
end

It throws this error message:

The accessible_by call cannot be used with a block 'can' definition. The SQL cannot be determined for :index Medication

So I tried doing it like this:

can :manage, Medication, patient_board: { customers: { id: user.id } }

That way the error message isn't shown anymore, but neither are the medications. And when trying to create a new medication it throws that error:

undefined method `matches?' for #PatientBoard:0x007f663c38d0e8 Did you mean? _matches? matcher

Which I think that makes sense, since its trying to compare an String value (user.id) to an array value (customer_ids)

In resume

How can I check if the user.id is in the customer_ids array using the hash of conditions of the CanCanCan?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Lucas Vieira
  • 96
  • 2
  • 8

2 Answers2

1

So, i managed to solve the problem after checking the issue #345 on the CanCan github.

Apparently, i had to provide an sql query to match the medications, so i did it like this:

can [:manage], [Medication], ["#{user.id} in ( select customer_ids from patient_board where id = medications.patient_board.id ) )"] do |t|
   t.patient_board.customer_ids.include? user.
end
Lucas Vieira
  • 96
  • 2
  • 8
0

It looks like include? is triggering a sql query inside the can block. Try converting the customer_ids to an array:

can :manage, Medication do |m|
  m.patient_board.customer_ids.to_a.include? user.id
end
Ryenski
  • 9,582
  • 3
  • 43
  • 47