Usually individual instances have the same policy as all other instances in the class - this is in fact the normal way Pundit works. In this case, you really don't care about the policy that belongs to a specific instance; instead, you're looking for the policy of a class of objects.
The policy
method uses this find method to identify the policy class for your object.
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
You can pass any object to the policy method. Since classes are objects, you can pass the class in. In the find
method, the first condition checks whether the object you passed responds to policy_class
, so you can define a method in your Project
class called policy_class
and have that method return your ProjectPolicy
class.
class Project < ActiveRecord::Base
def self.policy_class
ProjectPolicy
end
end
If you don't define the policy_class method, then the first two conditions will fail and the find method will then try to construct a policy class name. It first looks at object.model_name. If you're in Rails and your model extends ActiveModel::Naming (which ActiveRecord::Base does), then it will already respond to model_name
, and if you're doing typical Rails-ey stuff, then that name is exactly what you want: it will return "Project"
. The find method will then affix "Policy" to the end of that to make "ProjectPolicy"
. If it is some other object, the name of the object is used, and that would work for most cases as well.
Alternatively, you can pass any prototype object that represents the type of object for which you want the policy. That means you can use Policy.new
to create one, or you can grab one out of your @policies
array. As you said, those options have drawbacks.
There are lots of options available to you. There isn't a particularly "correct" way. I prefer passing the class in a case like this as it makes the most semantic sense.