3

そこで、ユーザー認証ソリューションの評論家を試みることにしました。policy以下の単純なケースのように、インスタンス変数が nil である可能性があるビューでヘルパーを使用する方法を知りたいです。

アプリ/ビュー/プロジェクト/index.html.slim

h1 Projects
(...)
- if policy(@project).create?
  = link_to 'New Project', new_project_path

app/controllers/projects_controller.rb

(...)
def index
  @projects = Project.all
end
(...)

アプリ/ポリシー/project_policy.rb

class ProjectPolicy < Struct.new(:user, :project)
  def create?
    user.has_role? :admin
  end

Projects#index ページに「New Project」リンクを表示したいのですが、このビューで使用できる @project インスタンス変数がなく、エラーが発生します。

Pundit::プロジェクトの NotDefinedError#index

のポリシー NilClassPolicy が見つかりません

nil のインスタンス変数を渡すため、明らかにエラーが表示@projectされます。したがって、NilClass は明らかに承認する必要がありません。

この問題を正しく実行するための 2 つの回避策を見つけましたが、いずれも適切ではないようです。

  1. ビューで既存の @projects 変数を使用します。つまり、次のようになります。policy(@projects[0])
  2. このインスタンス変数を定義する行を projects#index コントローラー アクションに追加します。@project = Project.new(または上記のようなビューで直接: policy(Project.new))

最初の解決策では @projects 配列が空になるという同じエラーが発生し、2 番目の解決策では冗長なインスタンス変数が作成されます。ポリシー ヘルパーが知る必要があるのは、承認ロジックを適用したいクラスだけです。

それを達成するための適切な方法に関する提案はありますか?

4

2 に答える 2

5

あなたの2番目に提案された解決策は私がすることです。

- if policy(@project).create?
  = link_to 'New Project', new_project_path

ここでの新しいレコードのポリシー チェックでは、Project.new (インスタンス変数に割り当てられているかどうかに関係なく)を使用する必要があります。

- if policy(Project.new).create?
  = link_to 'New Project', new_project_path

いずれにせよ、Pundit がチェック ルックアップを実行するポリシー クラスを派生させるために、 のインスタンスをヘルパーにProject渡す必要があります。渡すと、Pundit が.policyProjectPolicycreate?nilNilClassPolicy

于 2013-08-24T17:33:17.573 に答える
3

通常、個々のインスタンスは、クラス内の他のすべてのインスタンスと同じポリシーを持ちます。これは実際、Pundit の通常の動作方法です。この場合、特定のインスタンスに属するポリシーは気にしません。代わりに、オブジェクトのクラスのポリシーを探しています。

このpolicyメソッドは、この検索メソッドを使用して、オブジェクトのポリシー クラスを識別します。

def find
  if object.respond_to?(:policy_class)
    object.policy_class
  elsif object.class.respond_to?(:policy_class)
    object.class.policy_class
  else
    klass = if object.respond_to?(:model_name)
      object.model_name
    elsif object.class.respond_to?(:model_name)
      object.class.model_name
    elsif object.is_a?(Class)
      object
    else
      object.class
    end
    "#{klass}Policy"
  end
end

ポリシー メソッドには任意のオブジェクトを渡すことができます。クラスはオブジェクトであるため、クラスを渡すことができます。findメソッドでは、最初の条件で、渡したオブジェクトが に応答するかどうかをチェックします。そのため、呼び出されたクラスpolicy_classでメソッドを定義し、そのメソッドがクラスを返すようにすることができます。Projectpolicy_classProjectPolicy

class Project < ActiveRecord::Base
  def self.policy_class
    ProjectPolicy
  end
end

policy_class メソッドを定義しない場合、最初の 2 つの条件が失敗し、find メソッドがポリシー クラス名の作成を試みます。最初に object.model_name を調べます。Rails を使用していて、モデルが ActiveModel::Naming (ActiveRecord::Base が行う) を拡張している場合、モデルは既に に応答しmodel_nameます。典型的な Rails-ey の処理を​​行っている場合、その名前はまさにあなたが望むものです。 :戻り"Project"ます。find メソッドは、make の末尾に「Policy」を追加します"ProjectPolicy"。それが他のオブジェクトである場合は、オブジェクトの名前が使用され、ほとんどの場合でも機能します。

または、ポリシーが必要なオブジェクトのタイプを表すプロトタイプ オブジェクトを渡すこともできます。つまり、 を使用して作成したり、配列Policy.newから取得したりできます。@policiesあなたが言ったように、これらのオプションには欠点があります。

利用可能なオプションはたくさんあります。特に「正しい」方法はありません。私はこのような場合にクラスを渡すことを好みます。それが最もセマンティックな意味を持つからです。

于 2013-08-29T13:51:19.763 に答える