Namespaced Pundit Policies Without the Repetition Racket

4 months ago 7

If you’re using Pundit in a Rails app with namespaced policies — for example, Admin::PostPolicy — you’ve probably seen the official recommendation that goes something like this:

class AdminController < ApplicationController def policy_scope(scope) super([:admin, scope]) end def authorize(record, query = nil) super([:admin, record], query) end end class Admin::PostController < AdminController def index policy_scope(Post) end def show post = authorize Post.find(params[:id]) end end

Yeah, it works. But repeating that in every base controller gets old fast — and feels a bit noisy.

Let’s clean it up.

Here’s a small concern you can drop into app/controllers/concerns/namespaced_policy.rb:

# Controller concern for namespaced Pundit policies # # Usage: # include NamespacedPolicy::Policy(:users) # include NamespacedPolicy::Policy(:admin) module NamespacedPolicy def self.Policy(scope) Module.new do define_method :policy_scope do |scope_class| super([scope, scope_class]) end define_method :authorize do |record, query = nil| super([scope, record], query) end private :policy_scope, :authorize end end end

And now your controllers stay nice and tidy:

class AdminController < ApplicationController include NamespacedPolicy::Policy(:admin) end class Admin::PostController < AdminController def index policy_scope(Post) end def show post = authorize Post.find(params[:id]) end end

Much cleaner. Less boilerplate. Your future self will thank you.

Read Entire Article