2016-01-11 7 views
0

Так что я использую рельсы вместе с CanCanCan. У меня есть пользователи, у которых есть доступ к 0 или более Проекты Каждый проект имеет или более подпроектов. У обоих проектов и подпроектов есть менеджеры Если вы являетесь менеджером проекта, вы можете увидеть все его подпроекты Если вы являетесь менеджером подпроекта, вы также можете увидеть проект (но не обязательно какие-либо другие подпроекты в рамках этого проекта)Яркая загрузка доступной области видимости в CanCanCan

class Project < ActiveRecord::Base 
     has_many :sub_projects 
     has_many :ordered_sub_projects, ->() { order('name') } 
     has_many :project_managers 
end 

class ProjectManagers < ActiveRecord::Base 
     belongs_to :user 
     belongs_to :sub_project 
end 

class Subproject < ActiveRecord::Base 
     belongs_to :project 
     has_many :subproject_managers 
end 

class SubProjectManagers < ActiveRecord::Base 
     belongs_to :user 
     belongs_to :sub_project 
end 

class Ability 
     def initialize(user) 
      can? :read, Project, { managers: user.id } 
      can? :read, Project, { sub_projects: { subproject_managers: user.id } } # To allow to see projects if you are manager of subproject 

      can? :read, SubProject, { subproject_managers: user.id } 
      can? :read, SubProject, { project: { project_managers: user.id } } 
     end 
end 

Теперь я хочу, чтобы отобразить все проекты для данного пользователя, который я могу сделать с:

Projects.all.accessible_by(ability) 

Но на моей странице обзора я также хочу добавить QuickLinks ко всем (разрешенных) подпроектов (с использованием бутстрап выпадающего списка). Первоначально я использовал эквивалент ниже, на мой взгляд код:

Projects.all.accessible_by(ability).order(:name).each do |project| 
    project.sub_projects.accessible_by(ability).order(:name).each do |sub| 
    add_link sub.name, sub 
    end 
end 

Но это вызвало огромный N + 1 проблема при отображении многих проектов. [1] Итак, теперь я перешел на.

Projects.all.accessible_by(ability). 
      preload(:ordered_subprojects).order(:name).each do |project| 
     project.ordered_subprojects.each do |sub| 
     add_link sub.name, sub if can?(:read, sub) 
     end 
end 

, который намного быстрее, но менее чист. Также я подозреваю, что может? В этом случае метод относительно медленный.

В идеале можно было бы загрузить связанный файл has_many с загруженным правильным доступом. К сожалению, я не вижу способа сделать это

[1] На самом деле это было еще хуже, потому что у каждого подпроекта также были команды, в которых похожие семанты доступа были SubProject (каждая команда имела одного или нескольких мошенников, если вы может видеть, что команда вы можете увидеть SubProject и Project). Быстрые ссылки на команды также добавляются на страницу индекса проектов.

ответ

0

Вы можете использовать технику, похожую на то, что делает активная загрузка внутри - выборки всех доступных подпроектов сразу, а затем группировка их по project_id.

Как так:

subprojects_hash = Subproject.accessible_by(ability).group_by(&:project_id) 
Projects.all.accessible_by(ability).each do |project| 
    # do smth with project 
    if subprojects_hash[project.id] 
    # and subprojects... 
    end 
end