resourcify rails gema cancancan ruby-on-rails ruby scope pundit

ruby on rails - gema - Rails 4-Pundit-política de alcance para el índice



resourcify rails (3)

Estoy tratando de aprender a usar Pundit con mi aplicación Rails 4.

Tengo los siguientes modelos:

class User < ActiveRecord::Base has_one :profile has_many :eois end class Profile < ActiveRecord::Base belongs_to :user has_many :projects, dependent: :destroy end class Project < ActiveRecord::Base belongs_to :profile has_many :eois end class Eoi < ActiveRecord::Base belongs_to :project belongs_to :user end

Tengo una EoiPolicy ámbito con:

class EoiPolicy < ApplicationPolicy class Scope attr_reader :user, :scope def initialize(user, scope) @user = user @scope = scope end def resolve if user.profile.project.id == @eoi.project_id? scope.where(project_id: @user.profile.project.id) elsif user.id == eoi.user_id? scope.where(user_id: user.id) else nil end end end def index? user.profile.project.id == @eoi.project_id? or user.id == eoi.user_id? end def new? true end def show? user.profile.project.id == @eoi.project_id? or user.id == eoi.user_id? end def edit? user.id == eoi.user.id? end def create? true end def update? user.id == eoi.user.id? end def destroy? user.id == eoi.user.id? end end

En mi EoisController , he tratado de usar el alcance con:

def index # @eois = @project.eois @eois = policy_scope(Eoi) # @eois = Eois.find_by_project_id(params[:project_id]) end

Luego, en mi view/eois/index , he tratado de mostrar el índice con:

<% policy_scope(@user.eois).each do |group| %>

No puedo hacer que esto funcione. El mensaje de error resalta esta línea de mi método de alcance en la política:

if user.profile.project.id == @eoi.project_id?

Para mí, esto parece correcto, aunque aún estoy tratando de resolver esto. ¿Alguien puede ver lo que debe suceder para que esto funcione, de modo que si el usuario es el usuario, quién es el perfil propietario del proyecto relevante, todas las funciones relacionadas con ese proyecto están visibles?

De lo contrario, si el usuario es el usuario que creó el eoi, entonces ¿todas las eois que han creado son visibles?

El mensaje de error dice:

undefined method `project'' for #<Profile:0x007fa03f3faf48> Did you mean? projects projects=

Me pregunto si eso se debe a que un índice tendrá muchos registros, ¿necesita mostrar algo diferente en la política para reconocer la pluralidad?

También he intentado reemplazar esa línea con:

if @eoi.project_id == @user.profile.project.id?

aunque eso también es incorrecto y da

undefined method `project_id'' for nil:NilClass Did you mean? object_id

También intenté hacer el alcance:

def resolve # cant figure what is wrong with this if eoi.project_id == user.profile.project.id? scope.where(project_id: @user.profile.project.id) else nil end end

pero eso también es incorrecto y da este error:

undefined local variable or method `eoi'' for #<EoiPolicy::Scope:0x007ffb505784f8>

También probé:

def resolve # cant figure what is wrong with this if @eoi.project_id == user.profile.project.id? or Eoi.project_id == user.profile.project.id? scope.where(project_id: @user.profile.project.id) elsif user.id == eoi.user_id? scope.where(user_id: user.id) else nil end end end def index? user.profile.project.id == Eoi.project_id? or user.id == Eoi.user_id? end

pero ese intento da este mensaje de error:

undefined method `project_id'' for nil:NilClass Did you mean? object_id

PENSAMIENTO ACTUAL

Creo que necesito pasar más que usuario y alcance al método de alcance. Si también puedo pasar el proyecto, entonces puedo hacer que el alcance sea referible al proyecto al que se relaciona la EoI.

Si pudiera hacer que esto funcionara, entonces tal vez podría hacer que el método de alcance funcione para la vista de índice en el controlador:

class Scope attr_reader :user, :scope def initialize(user, scope, project) @user = user @scope = scope @project = project end end

luego en el controlador:

def index # @eois = @project.eois @eois = policy_scope(Eoi, @project) # authorize @eois # @eois = Eois.find_by_project_id(params[:project_id]) end

Esto no funciona, cuando intento recibo un error que dice que la política

wrong number of arguments (given 2, expected 1)

¡Por favor ayuda!

PRÓXIMO INTENTO

Mi próximo intento es tratar de tomar las sugerencias de [este] problema de Pundit e implementar esa idea de cómo obtener el alcance correcto para un usuario en particular.

En mi política de Eoi, cambié el método de resolución a:

class Scope attr_reader :user, :scope def initialize(user, scope) #project @user = user @scope = scope # @project = project end def resolve # if Eoi.project_id == user.profile.project.id? or Eoi.project_id == user.profile.project.id? if user.id == eoi.projects.profile.user.map(&:id) scope.joins(eois: :projects).where(project_id: user.profile.projects.map(&:id)).empty? # if scope.eoi.project_id == user.profile.projects.map(&:id) # scope.where(project_id: user.profile.projects.map(&:id)).empty? # scope.where(project_id: user.profile.project.id) # elsif user.id == eoi.user_id? # scope.where(user_id: user.id) else # nil end end end

Luego, en mi acción de índice de controlador eoi, probé esto:

def index # @eois = @project.eois # @eois = policy_scope(Eoi, @project) policy_scope(Eoi).where(project_id: params[:project_id]) # authorize @eois # @eois = Eois.find_by_project_id(params[:project_id]) end

Eso tampoco funciona. El mensaje de error para este intento dice:

undefined local variable or method `eoi'' for #<EoiPolicy::Scope:0x007f98677c9cf8>

Estoy sin ideas para probar. ¿Alguien puede ver la forma de darle al alcance las entradas correctas para configurar esto?

OBSERVACIÓN He notado que muchos de los repos en github que usan Pundit con alcances también incluyen un método como este:

def scope Pundit.policy_scope!(user, record.class) end

Ese método es adicional a la clase Scope y no se muestra en los documentos de gemas Pundit. Si es necesario incluir, ¿qué hace? 1

VOLVER A ESCRIBIR

Ahora he examinado más de 200 repositorios en github para conocer cómo se supone que debo escribir una política para cumplir mis objetivos. No tengo ideas sobre cómo usar Pundit como estaba previsto.

He cambiado mi configuración por completo para tratar de solucionar los bits que no puedo entender. Ahora tengo:

Controlador Eois

class EoisController < ApplicationController def index @eois = Eoi.by_user_id(current_user.id) end end

Proyectos :: Controlador Eois

module Projects class EoisController < ApplicationController before_action :get_project before_action :set_eoi, only: [:edit, :update, :destroy] # after_action :verify_authorized def index @eois = Project.by_user_id(current_user.id).find_by(id: params[:project_id]).try(:eois) || [] end def show @eoi = Eoi.find(params[:id]) authorize @eoi end def set_eoi @eoi = EoiPolicy::Scope.new(current_user, params[:project_id]).resolve.find(params[:id]) end def get_project @project = Project.find(params[:project_id]) end

Política Eoi (para decidir cuándo mostrar todas las eois hechas por un usuario)

class EoiPolicy < ApplicationPolicy class Scope attr_reader :user, :scope def initialize(user, scope) @user = user @scope = scope end def resolve if scope.present? Eoi.by_user_id(user.id) # end else [] end end end def index? user.profile.project.id == Eoi.project_id? or user.id == Eoi.user_id? end def new? true end def show? record.user_id == user.id || user.profile.project_id == record.project_id # user.profile.project.id == @eoi.project_id? or user.id == eoi.user_id? end def edit? user.id == eoi.user.id? end def create? true end def update? user.id == eoi.user.id? end def destroy? user.id == eoi.user.id? end end

Rutas

resources :eois resources :projects do member do resources :eois, controller: ''projects/eois end

Cuando quiero mostrar EoIs que se envían en relación con un proyecto, utilizo la Política de Eoi de Proyectos y cuando quiero mostrar la EO que ha creado un usuario, utilizo la Política de Eoi - sin alcances.

Me encantaría resolver esto para poder usar esta gema de la manera en que está destinada. El consejo sería muy apreciado. Estoy seguro de que este intento no es para lo que se sirve el Pundit, pero no puedo entender cómo usar esta gema como se muestra en los documentos.

No puedo usar policy_scope porque necesito pasar el parámetro project_id en la acción de índice para la acción del índice del controlador eoi del proyecto.

SUGERENCIA PaReeOhNos

Mi intento de tratar de implementar la sugerencia de PareeOhNos se establece a continuación. No estoy seguro de entenderlo correctamente porque eois siempre tendrá una identificación de proyecto y una identificación de usuario, pero tal vez no entiendo qué está haciendo el método load_parent.

En mi controlador Eois, tengo:

class EoisController < ApplicationController before_action :load_parent before_action :load_eoi, only: [:show, :edit, :update, :destroy] def index authorize @parent @eois = EoiPolicy::Scope.new(current_user, @parent).resolve end def show end # GET /eois/new def new @project = Project.find(params[:project_id]) @eoi = @project.eois.build @contribute = params[:contribute] || false @participate = params[:participate] || false @partner = params[:partner] || false @grant = params[:grant] || false @invest = params[:invest] || false end # GET /eois/1/edit def edit end # POST /eois # POST /eois.json def create @eoi = Project.find(params[:project_id]).eois.build(eoi_params) @eoi.user_id = @current_user.id respond_to do |format| if @eoi.save format.html { redirect_to Project.find(params[:project_id]), notice: ''Eoi was successfully created.'' } format.json { render :show, status: :created, location: @project } else format.html { render :new } format.json { render json: @eoi.errors, status: :unprocessable_entity } end end end # PATCH/PUT /eois/1 # PATCH/PUT /eois/1.json def update respond_to do |format| if @eoi.update(eoi_params) format.html { redirect_to @project, notice: ''Eoi was successfully updated.'' } format.json { render :show, status: :ok, location: @eoi } else format.html { render :edit } format.json { render json: @eoi.errors, status: :unprocessable_entity } end end end # DELETE /eois/1 # DELETE /eois/1.json def destroy @eoi.destroy respond_to do |format| format.html { redirect_to @project, notice: ''Eoi was successfully destroyed.'' } format.json { head :no_content } end end private def load_parent # @parent = (params[:project_id] ? Project.find(params[:project_id] : current_user) @parent = params[:project_id] ? Project.find(params[:project_id]) : current_user end def load_eoi @eoi = Eoi.find(params[:id]) authorize @eoi end

En mi política de Eoi, tengo:

class EoiPolicy < ApplicationPolicy class Scope attr_reader :user, :scope def initialize(user, scope) @user = user @scope = scope end def resolve if scope.is_a?(User) Eoi.where(user_id: scope.id) elsif scope.is_a?(Project) Eoi.where(project_id: scope.id) else [] end end end def index? record.is_a?(User) || user.profile.project.id == record.project_id end def new? true end def show? record.user_id == user.id || user.profile.project_id == record.project_id end def edit? user.id == eoi.user.id? end def create? true end def update? user.id == eoi.user.id? end def destroy? user.id == eoi.user.id? end end

En mi routes.rb, tengo:

resources :projects do member do resources :eois, shallow: true resources :eois, only: [:index]

En mi eois / index, tengo:

<% @eois.sort_by(&:created_at).in_groups_of(2) do |group| %> <% group.compact.each do |eoi| %> <h4><%= link_to eoi.user.full_name %></h4> <%= link_to ''VIEW DETAILS'', eoi_path(eoi), :class=>"portfolio-item-view" %> <% end %> <% end %>

En mi eois / show, tengo:

"test"

Cuando intento todo esto, carga la página eois / index. Cuando trato de mostrar una página específica de eoi, aparece un error que dice:

wrong number of arguments (given 2, expected 0)

el mensaje de error apunta a autorizar la línea @eoi del controlador:

def load_eoi @eoi = Eoi.find(params[:id]) authorize @eoi end

El mismo error surge si puse autorize @eoi en la acción show en lugar del método load eoi.

LA POLÍTICA DE APLICACIÓN TIENE

class ApplicationPolicy attr_reader :user, :scope class Scope def initialize(user, scope) #byebug @user = user # record = record @scope = scope end def resolve scope end end def index? false end def show? scope.where(:id => record.id).exists? end def create? false end def new? create? end def update? false end def edit? update? end def destroy? false end def scope Pundit.policy_scope!(user, record.class) end

PRÓXIMO INTENTO

Tomando la sugerencia de PaReeOhNos (copiada arriba), he tratado de adaptarla un poco para que se ajuste mejor a mis casos de uso.

Ahora tengo:

Controlador Eoi

class EoisController < ApplicationController # before_action :get_project # before_action :set_eoi, only: [:show, :edit, :update, :destroy] before_action :load_parent before_action :load_eoi, only: [:show, :edit, :update, :destroy] # GET /eois # GET /eois.json # def index # @eois = @project.eois # # @eois = Eois.find_by_project_id(params[:project_id]) # end def index # authorize @parent @eois = policy_scope(Eoi.where(project_id: params[:project_id])) # @eois = EoiPolicy::Scope.new(current_user, @parent).resolve end # GET /eois/1 # GET /eois/1.json def show end # GET /eois/new def new @project = Project.find(params[:project_id]) @eoi = @project.eois.build @contribute = params[:contribute] || false @participate = params[:participate] || false @partner = params[:partner] || false @grant = params[:grant] || false @invest = params[:invest] || false end # GET /eois/1/edit def edit end # POST /eois # POST /eois.json def create @eoi = Project.find(params[:project_id]).eois.build(eoi_params) @eoi.user_id = @current_user.id respond_to do |format| if @eoi.save format.html { redirect_to Project.find(params[:project_id]), notice: ''Eoi was successfully created.'' } format.json { render :show, status: :created, location: @project } else format.html { render :new } format.json { render json: @eoi.errors, status: :unprocessable_entity } end end end # PATCH/PUT /eois/1 # PATCH/PUT /eois/1.json def update respond_to do |format| if @eoi.update(eoi_params) format.html { redirect_to @project, notice: ''Eoi was successfully updated.'' } format.json { render :show, status: :ok, location: @eoi } else format.html { render :edit } format.json { render json: @eoi.errors, status: :unprocessable_entity } end end end # DELETE /eois/1 # DELETE /eois/1.json def destroy @eoi.destroy respond_to do |format| format.html { redirect_to @project, notice: ''Eoi was successfully destroyed.'' } format.json { head :no_content } end end private def load_parent # @parent = (params[:project_id] ? Project.find(params[:project_id] : current_user) @parent = params[:project_id] ? Project.find(params[:project_id]) : current_user end def load_eoi @eoi = Eoi.find(params[:id]) # authorize @eoi end

Política de Eoi

class EoiPolicy < ApplicationPolicy class Scope attr_reader :user, :scope def initialize(user, scope) @user = user @scope = scope end def resolve # since we send the scoped eois from controller, we can pick # any eoi and get its project id # check if the current user is the owner of the project # if (user.profile.projects.map(&:id).include?(project_id)) # # user is the owner of the project, get all the eois # scope.all # end # #not the owner , then get only the eois created by the user # scope.where(user_id: user.id) # end if scope.is_a?(User) Eoi.where(user_id: scope.id) elsif scope.is_a?(Project) && (user.profile.projects.map(&:id).include?(project_id)) project_id = scope.first.project_id Eoi.where(project_id: scope.id) else Eoi.none end end end def index? record.is_a?(User) || user.profile.project.id == record.project_id end def new? true end def show? record.user_id == user.id || user.profile.project_id == record.project_id end def edit? user.id == eoi.user.id? end def create? true end def update? user.id == eoi.user.id? end def destroy? user.id == eoi.user.id? end end

Rutas

resources :eois#, only: [:index] concern :eoiable do resources :eois end resources :projects do concerns :eoiable end

Índice

<% @eois.sort_by(&:created_at).in_groups_of(2) do |group| %> <% group.compact.each do |eoi| %> <h4><%= link_to eoi.user.full_name %></h4> <%= link_to ''VIEW DETAILS'', project_eoi_path(eoi.project, eoi), :class=>"portfolio-item-view" %> <% end %> <% end %>

Ver

''test''

Esto no funciona, porque cuando navego a un proyecto y luego trato de representar el índice de eois que tienen un id. De proyecto coincidente, obtengo una página de índice vacía, cuando tengo 4 registros en mi base de datos que deben mostrarse.

LA SUGERENCIA DE LEITO

Tomando la sugerencia de Leito, también he intentado esto:

Controlador Eoi

class EoisController < ApplicationController before_action :get_project before_action :set_eoi, only: [:show, :edit, :update, :destroy] # before_action :load_parent # before_action :load_eoi, only: [:show, :edit, :update, :destroy] # GET /eois # GET /eois.json # def index # @eois = @project.eois # # @eois = Eois.find_by_project_id(params[:project_id]) # end def index # authorize @eois # authorize @parent # policy_scope(@project.eois) @eois = policy_scope(Eoi.where(project_id: params[:project_id])) # @eois = EoiPolicy::Scope.new(current_user, @parent).resolve end # GET /eois/1 # GET /eois/1.json def show end # GET /eois/new def new @project = Project.find(params[:project_id]) @eoi = @project.eois.build @contribute = params[:contribute] || false @participate = params[:participate] || false @partner = params[:partner] || false @grant = params[:grant] || false @invest = params[:invest] || false end # GET /eois/1/edit def edit end # POST /eois # POST /eois.json def create @eoi = Project.find(params[:project_id]).eois.build(eoi_params) @eoi.user_id = @current_user.id respond_to do |format| if @eoi.save format.html { redirect_to Project.find(params[:project_id]), notice: ''Eoi was successfully created.'' } format.json { render :show, status: :created, location: @project } else format.html { render :new } format.json { render json: @eoi.errors, status: :unprocessable_entity } end end end # PATCH/PUT /eois/1 # PATCH/PUT /eois/1.json def update respond_to do |format| if @eoi.update(eoi_params) format.html { redirect_to @project, notice: ''Eoi was successfully updated.'' } format.json { render :show, status: :ok, location: @eoi } else format.html { render :edit } format.json { render json: @eoi.errors, status: :unprocessable_entity } end end end # DELETE /eois/1 # DELETE /eois/1.json def destroy @eoi.destroy respond_to do |format| format.html { redirect_to @project, notice: ''Eoi was successfully destroyed.'' } format.json { head :no_content } end end private # def load_parent # # @parent = (params[:project_id] ? Project.find(params[:project_id] : current_user) # @parent = params[:project_id] ? Project.find(params[:project_id]) : current_user # end # def load_eoi # @eoi = Eoi.find(params[:id]) # # authorize @eoi # end # # Use callbacks to share common setup or constraints between actions. def set_eoi @eoi = Eoi.find(params[:id]) end def get_project @project = Project.find(params[:project_id]) end

Política Eoi

def initialize(user, scope) @user = user @scope = scope end def resolve if scope.joins(project: :profile).where profiles: { user_id: user } Eoi.where(project_id: scope.ids) elsif scope.joins(eoi: :user).where eois: { user_id: user } Eoi.where(user_id: scope.ids) else Eoi.none end # since we send the scoped eois from controller, we can pick # any eoi and get its project id # check if the current user is the owner of the project # if (user.profile.projects.map(&:id).include?(project_id)) # # user is the owner of the project, get all the eois # scope.all # end # #not the owner , then get only the eois created by the user # scope.where(user_id: user.id) # end # if scope.is_a?(User) # Eoi.where(user_id: scope.id) # elsif scope.is_a?(Project) && (user.profile.projects.map(&:id).include?(project_id)) # project_id = scope.first.project_id # Eoi.where(project_id: scope.id) # else # Eoi.none # end end end def index? true # record.is_a?(User) || user.profile.project.id == record.project_id end def new? true end def show? true # record.user_id == user.id || user.profile.project_id == record.project_id end def edit? user.id == eoi.user.id? end def create? true end def update? user.id == eoi.user.id? end def destroy? user.id == eoi.user.id? end end

Las rutas y vistas son las mismas que el intento anterior

El problema aquí es con el método get project en mi controlador. Necesito eso para el escenario en el que estoy tratando de mostrar todas las eois en un proyecto específico. No lo necesito cuando estoy tratando de mostrar toda la energía de un usuario.

Cuando guardo todo esto y lo intento, las eois en un proyecto se muestran correctamente. Sin embargo, la eois (no anidada dentro de un proyecto) que se supone que me muestran toda mi (como usuario) eois, muestra un error que dice:

Couldn''t find Project with ''id''=

El mensaje de error resalta el ''método get_project''.

LA SUGERENCIA ACTUALIZADA DE LEITO

Tomando la sugerencia actualizada de Leito, he establecido el intento actual.

Antes de hacerlo, quiero aclarar que todos los Eois tendrán una identificación de usuario y una identificación de proyecto. Utilizo esta tabla para que los usuarios expresen interés en los proyectos. Mi objetivo es que el usuario cuyo perfil es propietario del proyecto vea todas las eois enviadas en ese proyecto. Luego, también quiero que los usuarios vean todas sus propias publicaciones (en todos los proyectos).

Política Eoi

def resolve if scope.joins(project: :profile).where ''profiles.user_id = ? OR eois.user_id = ?'', user.id, user.id Eoi.all else Eoi.none end

Controlador Eoi

def index @eois = policy_scope(Eoi) @eois = @eois.where(project_id: params[:project_id]) if params[:project_id] end

Actualmente, esto funciona bien para encontrar las eois que están anidadas en un proyecto (project / 26 / eois). Sin embargo, cuando intento hacer eois / index (no anidado en el proyecto), que quiero devolver a todos los usuarios, me sale un error que dice:

Couldn''t find Project with ''id''=

Destaca esta línea del controlador eoi:

def get_project @project = Project.find(params[:project_id]) end

No estoy seguro de entender el método de resolución o la idea de eliminar el controlador ahora. No puedo ver qué pasa con la línea de alcance para ver qué cambiar.


En tu primer ejemplo, hay un par de problemas. En primer lugar, @eoi no existe, y no puede existir. La variable @eoi se establece en el controlador, y este es un objeto diferente. No funciona de la misma manera que sus vistas, donde esto es accesible, por lo que nunca se establecerá.

Igualmente, la variable eoi no se establecerá, ya que su método de initialize solo asigna las variables de user y de resource , por lo que son las únicas dos a las que tiene acceso (a menos que cambie el nombre)

El alcance de la política funciona de forma un poco diferente de cómo piensas que funciona. La política en sí misma generalmente toma al usuario conectado, y una clase, o un registro que usted está autorizando. El alcance sin embargo, normalmente no toma un registro como el segundo argumento. Es un ámbito, por lo tanto, una subclase de registro activo o una relación. Sin embargo, no está restringido a esto y podría solucionarlo suministrando un registro, pero tenga en cuenta que este no es un comportamiento normal para Pundit.

Para lograr lo que busca, solo debe hacer algunos ajustes:

class EoiPolicy < ApplicationPolicy class Scope attr_reader :user, :eoi def initialize(user, eoi) @user = user @eoi = eoi end def resolve if user.profile.project.id == eoi.project_id Eoi.where(project_id: user.profile.project.id) elsif user.id == eoi.user_id Eoi.where(user_id: user.id) else nil end end end def index? user.profile.project.id == record.project_id or user.id == record.user_id end def new? true end def show? user.profile.project.id == record.project_id? or user.id == record.user_id end def edit? user.id == record.user.id end def create? true end def update? user.id == record.user.id end def destroy? user.id == record.user.id end end

Los cambios principales aquí son que attr_reader :user, :scope ahora es attr_reader :user, :eoi que le dará acceso a eoi dentro de ese alcance.

El acceso a esto ya no está prefijado con @ ya que está en línea con el funcionamiento del experto.

A lo largo del resto de la política, @eoi nuevamente no puede funcionar, pero esto se ha cambiado para record (suponiendo que esto es lo que es en ApplicationPolicy). Tenga en cuenta el alcance, y el resto de la política son dos clases diferentes.

Con esta configuración, ahora debería poder simplemente llamar a policy_scope(@eoi) desde su controlador. Tenga en cuenta el uso de la variable @eoi aquí y NO la clase Eoi como antes. Esto es crucial, ya que sin esto, no tendrá acceso a cosas como user_id o project_id ya que esos métodos no existen en la clase Eoi, sino solo en un registro.

También he eliminado el ? símbolos desde el final de sus condiciones if. En general, se utilizan para indicar que el método al que se llama devuelve un valor booleano, mientras que los tenía al final de algo que simplemente devuelve un número entero. Me imagino que en realidad obtendrías un error al decir que el método no existe, pero si has cambiado el nombre de las cosas, entonces querrás volver a ponerlas, pero como digo, eso va en contra de los estilos de codificación ruby.

Y en una nota lateral, el uso de or o en las declaraciones en lugar de || o && puede en las ocasiones ocasionales comportarse de manera diferente a como espera. En la mayoría de los escenarios está bien, pero técnicamente no significa lo mismo.

Espero que todo esto ayude, avíseme si tiene más problemas con él.


Soy el comentarista anterior sobre ese tema.

Para su EoiScope, simplemente quiere a qué EoT tiene acceso el usuario (porque pertenecen a proyectos bajo este perfil), independiente del proyecto (este requisito es solo para el controlador, porque está anidado), por lo que su controlador debería verse como esta:

Editar : Basado en su último intento, actualicé el alcance para dar cuenta de Eois que pertenece directamente al usuario (no a través de un proyecto) y simplemente debería extenderlo a un proyecto o no basado en la presencia de params [: project_id] , mira la respuesta actualizada.

@eois = policy_scope(Eoi) @eois = @eios.where(project_id: params[:project_id]) if params[:project_id]

Y su alcance debería hacer combinaciones hasta que llegue al usuario o simplemente busque la propiedad user_id en Eoi.

class EoiPolicy < ApplicationPolicy class Scope < Scope def resolve scope.joins(project: : profile).where ''profiles.user_id = ? OR eois.user_id = ?'', user.id, user.id end end # Other methods that differ from ApplicationPolicy''s methods end

Tenga en cuenta que Scope no llama a eoi , pero el alcance predeterminado * solo conoce el scope y el user . * Por defecto, quiero decir cuando hereda de ApplicationPolicy::Scope


Para otros, no estoy seguro de si esta es una solución que hace uso de Pundit de la manera que se pretendía, sin embargo, genera los flujos que quiero, dentro de los límites de mi capacidad.

Gracias a todos los que ayudaron en esto. Estoy seguro de que todavía tengo mucho que aprender sobre cómo mejorar esto, pero por ahora, esta es una solución que funciona.

En resumen, ahora tengo dos políticas para 1 controlador.

Política Eoi

class EoiPolicy < ApplicationPolicy class Scope def initialize(user, scope) @user = user @scope = scope end def resolve # selects all the EOI''s for a given user @scope.where(user_id: @user.id) end end def index? true end

Política del Proyecto Eoi

class ProjectEoiPolicy < ApplicationPolicy class Scope < Scope def resolve(project_id) project = Project.find(project_id) if project.owner?(@user) # if the user is the owner of the project, then get # all the eois project.eois else # select all the eois for the project # created by this user Eoi.for_user(@user.id).for_project(project_id) end end end end

Eoi Controller index action

class EoisController < ApplicationController before_action :get_project, except: [:index, :show] before_action :set_eoi, only: [:show, :edit, :update, :destroy] def index if params[:project_id] @eois = ProjectEoiPolicy::Scope.new(current_user, Eoi).resolve(params[:project_id]) else @eois = policy_scope(Eoi) end end