I am using the Pundit gem to role scope my application and have found some difficulties merging ActiveRecord queries. I am working with Rails 5.1.4.
See I have three models, lets say Classroom, Student and Exam with:
- Classroom has_many :students & has_many :exams, through: :students
- Student belongs_to: :classroom & has_many :exams
- Exam belongs_to: :student
Each have a policy scope with varying queries that I want to merge together to ensure a user has access to the Exam model that's within the Student scope, that's also within the Classroom scope.
Pundit let us do that by passing whatever ActiveRecord relationship from the controller to the Scope class inside a scope variable.
With that in mind, my scope classes appear as follow:
class ClassroomPolicy::Scope
def resolve
if user.reviewer?
# A reviewer can only see specific classes
scope.where(type: :exam).where(reviewer_id: user.id)
end
end
end
class StudentPolicy::Scope
def resolve
if user.reviewer?
# A reviewer can only see students from classes he's allowed in
scope.joins(:classroom).merge(policy_scope(Classroom))
end
end
end
class ExamPolicy::Scope
def resolve
if user.reviewer?
# A reviewer can only see/grade exams from students he supervised
scope.joins(:student).merge(policy_scope(Student))
end
end
end
All works well, if I do policy_scope(Exam)
, I do get all exams from students who where inside a reviewer's classroom.
The issue arises when passing an already joined query such as policy_scope(Classroom.find(params[:classroom_id]).exams)
.
ActiveRecord effectively generates a strange query which is unusable because both a_classroom.exams and our scope do a join on :student.
In addition, removing joins
from our exam scope, giving us scope.merge(policy_scope(Student))
, makes our call policy_scope(Classroom.find(params[:classroom_id]).exams)
work while breaking policy_scope(Exam)
.
Is there a way to work around this so that both use cases work ?
Or is my approach to Pundit wrong ?
Is this the limit of ActiveRecord ?
Any help regarding this would be appreciated !