If we have an Order class, pundit gem will figure out to use the OrderPolicy for authorization. But what if we have multiple domains using the same item?
For example an online auction site, there will be a
Seller::OrdersController and a
Buyer::OrdersController. They will manage the two sides of the same order. The idea is that, buyer should be allowed to only update via
Buyer::OrdersController but not
Seller::OrdersController. And vice versa for seller. Obviously we would want to have two sets of policies.
However in Pundit namespaced policy require us to call
authorze [:seller, item], in order for it to use the
Seller::ItemPolicy. This can become repetitive.
For me, there is less room for error if we have a 1:1 relationship between controller and policy, and a controller can be assumed to use the same policy. So I patched
authorize call so policy can be set on the controller level.
Put the following under
ApplicationController as private methods.
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
In your controller, you can then do this to set policy:
class Seller::OrdersController < ApplicationController set_pundit_policy_class Seller::OrderPolicy
This means all actions under this controller will use
Seller::OrderPolicy by default when you call
If we want to override this, we can also pass in
authorize @order, policy_class: FooPolicy
Note that I made some changes to
authorize’s method signature:
query is a keyword argument now. It also returns the record object (as planned for its 1.2 release).