ruby-on-rails - перевод - ruby on rails cancancan




Rails 4-политика Pundit для индексов (2)

Я пытаюсь узнать, как использовать Pundit с моим Rails 4.

У меня есть следующие модели:

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

У меня есть EoiPolicy с EoiPolicy :

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

В моем EoisController я попытался использовать область действия:

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

Затем, на мой view/eois/index , я попытался отобразить индекс с помощью:

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

Я не могу заставить это работать. Сообщение об ошибке выделяет эту строку моего метода scope в политике:

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

Для меня это выглядит правильно, хотя я все еще пытаюсь понять это. Может ли кто-нибудь увидеть, что должно произойти, чтобы сделать эту работу, так что, если пользователь является пользователем, которому принадлежит профиль, соответствующий проект, все евреи, относящиеся к этому проекту, видны.

В противном случае, если пользователь является пользователем, создавшим eoi, тогда все eois, которые они создали, видны?

В сообщении об ошибке говорится:

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

Мне интересно, если это потому, что индекс будет иметь много записей, ему нужно показать что-то другое в политике, чтобы распознать множественность?

Я также попытался заменить эту строку:

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

хотя это также неверно и дает

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

Я также попытался сделать область:

 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

но это также неправильно и дает эту ошибку:

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

Я также пробовал:

    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

но эта попытка дает это сообщение об ошибке:

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

ТЕКУЩАЯ МЫСЛЬ

Я думаю, мне нужно передать больше, чем пользователь и область видимости в метод области. Если я могу также передать проект, я могу сделать область применимой к проекту, к которому относится EoI.

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

class Scope
  attr_reader :user, :scope

  def initialize(user, scope, project)
    @user  = user
    @scope = scope
    @project = project
  end
end

затем в контроллере:

 def index
   # @eois = @project.eois

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

Это не работает, когда я пытаюсь получить ошибку, говоря, что политика

wrong number of arguments (given 2, expected 1)

Пожалуйста помоги!

СЛЕДУЮЩАЯ ПОПЫТКА

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

В моей политике Eoi я изменил метод разрешения на:

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

Затем в моем действии индекса контроллера eoi я попробовал это:

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

Это тоже не работает. Сообщение об ошибке для этой попытки говорит:

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

Im из идей для вещей, чтобы попробовать. Может ли кто-нибудь увидеть способ дать правилу правильные входные данные, чтобы настроить это?

НАБЛЮДЕНИЕ Я заметил, что многие репозитории на github, которые используют Pundit с областями, также включают в себя такой метод:

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

Этот метод находится в дополнение к классу Scope и не показан в документах Gem Pundit. Если это необходимо включить, что он делает? 1

REWRITE

Теперь я просмотрел более 200 репозиториев на github для понимания того, как я должен писать политику для достижения моих целей. У меня нет идей о том, как использовать Pundit по назначению.

Я полностью изменил настройку, чтобы попытаться обойти бит, который я не могу понять. У меня теперь есть:

Контроллер Eois

class EoisController < ApplicationController

  def index
    @eois = Eoi.by_user_id(current_user.id)
  end
end

Проекты :: Контроллер 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

Eoi Policy (чтобы решить, когда показывать все eois, сделанные пользователем)

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

Маршруты

resources :eois

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

Когда я хочу показать EoI, которые представлены в отношении проекта, я использую Политики проектов Eoi, и когда я хочу показать Eois, что пользователь создал, я использую Eoi Policy - no scopes.

Мне бы хотелось разобраться в этом, поэтому я могу использовать этот камень так, как он предназначен. Совет будет очень благодарен. Я уверен, что эта попытка не в том, что означает Pundit, но я не могу понять, как использовать этот камень, как показано в документах.

Я не могу использовать policy_scope, потому что мне нужно передать параметр project_id в действие индекса для действия индекса контроллера eoi.

ПРЕДЛАГАЕМЫЕ РЕШЕНИЯ

Моя попытка попытаться реализовать предложение PareeOhNos приведена ниже. Я не уверен, что правильно понимаю, потому что eois всегда будет иметь идентификатор проекта и идентификатор пользователя, но, возможно, я не понимаю, что делает метод load_parent.

В моем контроллере Eois у меня есть:

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

В моей политике Eoi у меня есть:

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

На моих маршрутах.rb у меня есть:

resources :projects do
    member do
  resources :eois, shallow: true

resources :eois, only: [:index]

В моем eois / index у меня есть:

    <% @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 %>  

В моем eois / show у меня есть:

"test"

Когда я пытаюсь все это, загружается страница eois / index. Когда я пытаюсь показать конкретную страницу eoi, я получаю сообщение об ошибке:

wrong number of arguments (given 2, expected 0)

сообщение об ошибке указывает на авторизацию строки @eoi контроллера:

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

Такая же ошибка возникает, если я устанавливаю authorize @eoi в действии show вместо метода load eoi.

ПОЛИТИКА ПРИМЕНЕНИЯ

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

СЛЕДУЮЩАЯ ПОПЫТКА

Принимая предложение PaReeOhNos (скопированное выше), я попытался немного его адаптировать, чтобы лучше соответствовать моим случаям использования.

Теперь у меня есть:

Контроллер 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

Политика 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

Маршруты

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

resources :projects do
    concerns :eoiable
  end

Индекс

   <% @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 %>   

Посмотреть

'test'

Это не работает, потому что, когда я перехожу к проекту, а затем пытаюсь отобразить индекс eois, у которого есть соответствующий идентификатор проекта, я получаю пустую индексную страницу, когда у меня есть 4 записи в моей базе данных, которые должны отображаться.

ПРЕДЛОЖЕНИЕ ЛЕЙТО

Принимая предложение Лейто, я также пробовал это:

Контроллер 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

Политика 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

Маршруты и представления такие же, как и попытка выше

Проблема здесь в том, что метод get project в моем контроллере. Мне нужно это для сценария, где Im пытается показать все eois по конкретному проекту. Мне это не нужно, когда я пытаюсь показать всех пользователей eois.

Когда я сохраню все это и попробую, эйз по проекту покажет правильно. Однако eois (не вложенный внутри проекта), который должен показать мне все мои (как пользователь) eois, показывает ошибку, которая гласит:

Couldn't find Project with 'id'=

В сообщении об ошибке выделяется метод get_project.

ОБНОВЛЕННОЕ ПРЕДЛОЖЕНИЕ LEITO

Принимая последнее предложение Лейто, я изложил текущую попытку.

Прежде чем это сделать, я хочу уточнить, что у всех Eois будет как идентификатор пользователя, так и идентификатор проекта. Я использую эту таблицу для того, чтобы пользователи проявляли интерес к проектам. Моя цель состоит в том, чтобы пользователь, чей профиль владеет проектом, видит все eois, представленные в этом проекте. Затем я также хочу, чтобы пользователи видели все свои собственные eois (во всех проектах).

Политика 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  

Контроллер Eoi

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

В настоящее время это прекрасно подходит для поиска eois, которые вложены в проект (проект / 26 / eois). Однако, когда я пытаюсь сделать eois / index (не вложенный в проект), который я хочу вернуть всем eois пользователя, я получаю сообщение об ошибке:

Couldn't find Project with 'id'=

Он выделяет эту строку контроллера eoi:

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

Я не уверен, что сейчас понимаю метод решения или контроллер, который выбрал идею. Я не вижу, что случилось с линией области видимости, чтобы увидеть, что попробовать.


В вашем первом примере есть несколько проблем. Во-первых, @eoi не существует и не может существовать. Переменная @eoi задается в контроллере, и это другой объект. Он не работает так же, как ваши взгляды, где это доступно, поэтому это никогда не будет установлено.

Аналогично, переменная eoi не будет установлена, так как ваш метод initialize присваивает только user и resource переменные, поэтому они являются единственными, к которым у вас есть доступ (если вы не переименовываете)

Область действия политики немного отличается от того, как вы думаете, что она работает. Сама политика обычно включает пользователя, входящего в систему, и класс или запись, которую вы разрешаете. Однако область действия обычно не принимает запись в качестве второго аргумента. Это область действия, поэтому либо активный подкласс класса, либо отношение. Однако вы не ограничены этим, и вы можете обойти это, поставив запись, но обратите внимание, что это не нормальное поведение для Pundit.

Чтобы добиться того, что вам нужно, вам нужно только внести несколько изменений:

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

Основные изменения здесь в том, что attr_reader :user, :scope теперь attr_reader :user, :eoi который даст вам доступ к eoi пределах этой области.

Доступ к этому уже не префикс с @ поскольку это соответствует тому, как работает pundit.

Во всей остальной части политики @eoi снова не может работать, но это было изменено на record (предполагая, что это то, что есть в ApplicationPolicy). Пожалуйста, имейте в виду область действия, а остальная часть политики - это два разных класса.

С помощью этой настройки вы должны теперь просто вызвать policy_scope(@eoi) из своего контроллера. Обратите внимание на использование переменной @eoi здесь и НЕ класс @eoi как и раньше. Это важно, так как без этого у вас не будет доступа к таким вещам, как user_id или project_id поскольку эти методы не существуют в классе Eoi, а только запись.

Я также удалил ? символы с конца ваших условий if. Они обычно используются для обозначения того, что вызываемый метод возвращает логическое значение, тогда как вы имели их в конце чего-то, что просто возвращает целое число. Я бы предположил, что на самом деле вы получите сообщение о том, что метод не существует, но если вы переименовали вещи, то можете захотеть их вернуть, но, как я уже сказал, это противоречит стилям рубинового кодирования.

И на стороне примечание, используя or или and в операторах вместо || или && могут в нечетном случае вести себя по-другому, как вы ожидаете. В большинстве сценариев это нормально, но технически это не означает то же самое.

Надеюсь, что все это поможет, сообщите мне, есть ли у вас какие-либо дальнейшие проблемы.


Я - предыдущий комментатор по этому вопросу.

Для вашего EoiScope вы просто хотите, чтобы у пользователя Eois был доступ (потому что он принадлежит проектам под этим профилем), независимо от проекта (это требование предназначено только для контроллера, поскольку оно вложенное), поэтому ваш контроллер должен выглядеть примерно так: это:

Изменить . Исходя из вашей последней попытки, я обновил область для учета Eois, принадлежащую непосредственно пользователю (а не через проект), и вы должны просто охватить его проектом или нет, основываясь на наличии параметров [: project_id] , см. обновленный ответ.

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

И ваша область действия должна объединять до тех пор, пока она не достигнет пользователя или просто не найдет свойство user_id на 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

Обратите внимание, что Scope не вызывает eoi , но область по умолчанию * только знает о scope и user . * По умолчанию я имею в виду, когда он наследует от ApplicationPolicy::Scope





pundit