Suppose I have an Order model and the following policy:
class OrderPolicy < ApplicationPolicy
def create?
true
end
def revalidate?
true
end
end
But I wanna have only CRUD methods in my policies, so I create a new policy:
class RevalidationOrderPolicy < ApplicationPolicy
def create?
true
end
However, I couldn't find a way to pass an order to my new policy (reading the code, I think it's not supported right now), I have tried things like:
policy(:revalidation_order).create?
But the method create? receives :revalidation_order as record (I need to receive an order instance). Just to confirm, is there a way to do it? If not, what do you think about these api below?
policy(order, klass: RevalidationOrder).create?
# or
policy(order, klass: [:revalidation_order]).create?
It would be a pleasure to implement.
+1
Has somebody found a workaround yet? I hoped that I would be able to manually specify the class analogous to the scope example from the documentation:
@posts = PostPolicy::Scope.new(current_user, Post).resolve
But I couldn't come up with something working.
It's a bit confusing that in your title you said "Pass class as argument" but in the description you passed :revalidation_order. When you say "Class" do you mean a Policy class?
I think the easiest is to override policy in that controller (assuming all actions in that controller uses the same policy).
def policy(record)
policies[record] ||= RevalidationOrderPolicy.new(pundit_user, record)
end
But if you need to use different policies in one controller let me know.
I am already doing this override of policy in the controller today.
But would be nice if we could have such api: policy(order, klass: RevalidationOrder).create? (or any other with the same purpose)
@glaucocustodio Cool, in that case, I used a very flexible patch:
```lang=ruby
# ApplicationController
class_attribute :pundit_policy_class
def self.set_pundit_policy_class(klass)
self.pundit_policy_class = klass
end
def authorize(record, query: nil, policy_class: nil)
query ||= params[:action].to_s + "?"
@_pundit_policy_authorized = true
if policy_class
policy = policy_class.new(pundit_user, record)
elsif self.pundit_policy_class
policy = self.pundit_policy_class.new(pundit_user, record)
else
policy = policy(record)
end
unless policy.public_send(query)
raise NotAuthorizedError, query: query, record: record, policy: policy
end
record
end
This allows you to specify policy on a per case basis:
authorize(@book, policy_class:FooPolicy)
This also allows me to set policy on a controller wide scope:
class FooController
set_pundit_policy_class FooFooPolicy
```
I think this could be related to this: https://github.com/varvet/pundit/pull/441
@pablocrivella: It certainly is. Thanks for pointing out.
It sure is related to #441
The easiest, and perhaps the best, way to do it right now is to create new RevalidationOrder object and authorize based on that. If you don't need any other functionality it's super easy by wrapping the Order object using SimpleDelegator.
I'll close this as we already track this in #441
Most helpful comment
@glaucocustodio Cool, in that case, I used a very flexible patch:
```lang=ruby
# ApplicationController
class_attribute :pundit_policy_class
def self.set_pundit_policy_class(klass)
self.pundit_policy_class = klass
end
def authorize(record, query: nil, policy_class: nil)
query ||= params[:action].to_s + "?"
end
authorize(@book, policy_class:FooPolicy)
class FooController
set_pundit_policy_class FooFooPolicy
```