Introduction
Pundit provides a minimal authorization system for Ruby on Rails built around plain Ruby policy classes. Instead of defining abilities in a centralized DSL, Pundit encourages one policy class per model, each containing methods that return true or false for specific actions. This approach keeps authorization logic explicit, easy to test, and close to the domain objects it protects.
What Pundit Does
- Maps controller actions to policy methods that determine whether a user can perform a given operation
- Provides scoping via policy scopes that filter database queries to return only records the user is allowed to see
- Raises a clear error when authorization is missing, ensuring no controller action accidentally skips access checks
- Generates policy and spec files from the command line to maintain consistent structure
- Works with any authentication system including Devise, Clearance, or custom solutions
Architecture Overview
Pundit relies on naming conventions: for a Post model, it looks up PostPolicy. Each policy class receives the current user and the record being authorized. Action methods like create?, update?, and destroy? return booleans. Scopes are inner classes that receive the user and a base relation, returning a filtered ActiveRecord scope. The authorize helper in controllers instantiates the correct policy, calls the matching method, and raises Pundit::NotAuthorizedError if it returns false. A verify_authorized callback can be added to ensure every action calls authorize.
Self-Hosting & Configuration
- Install the gem and run the generator to create
app/policies/application_policy.rbas the base class - Include
Pundit::Authorizationin ApplicationController or specific controllers - Create one policy file per model under
app/policies/with methods matching controller actions - Use
after_action :verify_authorizedto catch actions that forget to call authorize - Define policy scopes as inner
Scopeclasses for index actions and relation-based filtering
Key Features
- Pure Ruby classes with no DSL magic, making policies easy to read, test, and debug
- One policy per model keeps authorization logic organized and predictable
- Policy scopes integrate with ActiveRecord to filter queries at the database level
- Framework-agnostic core that works with Rails, Sinatra, or any Ruby application
- Built-in helpers for view-layer authorization checks via the
policyhelper method
Comparison with Similar Tools
- CanCanCan — centralized Ability class with a DSL; Pundit distributes logic across individual policy classes for better maintainability
- Action Policy — inspired by Pundit with additional features like caching and scoping; Pundit is simpler and more widely adopted
- Rolify — role management library; complements Pundit by providing roles that policies can check against
- Casbin — policy engine with model-based access control; Pundit is Ruby-specific and uses plain code instead of policy languages
- Authorizr (Go) — similar concept for Go; Pundit serves the Ruby ecosystem with tight Rails integration
FAQ
Q: How do I handle different user roles?
A: Check role attributes directly in policy methods (e.g., user.admin? or user.role == :editor). Combine with Rolify for more complex role hierarchies.
Q: Can I use Pundit outside of Rails?
A: Yes. Pundit works with any Ruby application. You need to provide the current user and call authorize manually without the Rails controller integration.
Q: How do I test Pundit policies? A: Pundit policies are plain Ruby objects. Instantiate them in tests with a user and record, then assert the return values of action methods directly.
Q: What happens if I forget to authorize an action?
A: Adding after_action :verify_authorized raises Pundit::AuthorizationNotPerformedError for any action that did not call authorize, preventing accidental open access.