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