Last active
January 24, 2018 01:34
-
-
Save spieker/7ca47826967c8b584f80 to your computer and use it in GitHub Desktop.
Namespaces for Pundit policies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This concern enables namespaces in Pundit. In order to use it, put this | |
# module into the `app/controllers/concerns` folder of your project and then | |
# include the module into the controller you want to namespace the policies in | |
# after the include of Pundit. | |
# | |
# To secify the namespace to use, overwrite the `pundit_namespace` method on | |
# your controller then. | |
# | |
# Example | |
# ======= | |
# | |
# ```ruby | |
# class API::V3::BaseController < ApplicationController | |
# include Pundit | |
# include Concerns::PunditNamespaces | |
# | |
# def pundit_user | |
# current_user | |
# end | |
# | |
# def pundit_namespace | |
# V3 | |
# end | |
# end | |
# ``` | |
# | |
# In order to re-implement as few things as possible, the policies and the | |
# scopes are resolved by Pundit::PolicyFinder which returns the resolved class | |
# without a namespace. Therefor a dummy policy without a namespace has to | |
# exist, otherwise you will get a class not found exception. | |
# | |
module Concerns::PunditNamespaces | |
def self.included(receiver) | |
receiver.send :include, InstanceMethods | |
end | |
module InstanceMethods | |
# To set the namespace, overwrite this method in your controller | |
def pundit_namespace | |
Object | |
end | |
def policies | |
NamespacedPolicyFinder.new(pundit_user, pundit_namespace) | |
end | |
def policy_scopes | |
NamespacedPolicyScopeFinder.new(pundit_user, pundit_namespace) | |
end | |
end | |
class NamespacedPolicyFinder | |
def initialize(user, namespace) | |
@user = user | |
@namespace = namespace | |
end | |
def policies | |
@_policies ||= {} | |
end | |
def [](object) | |
policies[object] ||= begin | |
policy = Pundit::PolicyFinder.new(object).policy | |
policy = "#{@namespace}::#{policy.to_s}".constantize | |
policy.new(@user, object) | |
end | |
end | |
end | |
class NamespacedPolicyScopeFinder | |
def initialize(user, namespace) | |
@user = user | |
@namespace = namespace | |
end | |
def policy_scopes | |
@_policy_scopes ||= {} | |
end | |
def [](object) | |
policy_scopes[object] ||= begin | |
policy = Pundit::PolicyFinder.new(object).scope | |
policy = "#{@namespace}::#{policy.to_s}".constantize | |
policy.new(@user, object).resolve | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'd really like to get this to work - and for regular namespaced models/controllers not so much APIs. Could you explain how, if I have a namespace Ticket and a controller eg Ticket::DetailController I should name my policies for this to work? You refer to a dummy policy: what would that be?
For instance, I keep getting the error:
uninitialized constant Ticket::Ticket
From the line
policy = "#{@namespace}::#{policy.to_s}".constantize