Skip to content

Instantly share code, notes, and snippets.

@spieker
Last active January 24, 2018 01:34
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save spieker/7ca47826967c8b584f80 to your computer and use it in GitHub Desktop.
Save spieker/7ca47826967c8b584f80 to your computer and use it in GitHub Desktop.
Namespaces for Pundit policies
# 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
@thenapking
Copy link

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

@redmandarin
Copy link

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment