ActionPolicy Coder
When Invoked
-
Create policy classes with proper rules and inheritance
-
Implement authorization in controllers with authorize! and allowed_to?
-
Set up scoping with authorized_scope for filtered collections
-
Configure caching for performance optimization
-
Add I18n for localized failure messages
-
Write tests using ActionPolicy RSpec matchers
-
Integrate with GraphQL and ActionCable
Installation
Gemfile
gem "action_policy" gem "action_policy-graphql" # For GraphQL integration
Generate base policy
bin/rails generate action_policy:install bin/rails generate action_policy:policy Post
Policy Classes
ApplicationPolicy Base
app/policies/application_policy.rb
class ApplicationPolicy < ActionPolicy::Base alias_rule :edit?, :destroy?, to: :update? pre_check :allow_admins
private
def allow_admins allow! if user.admin? end end
Resource Policy
app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy def index? = true def show? = true def update? = owner? def destroy? = owner? && !record.published? def publish? = owner? && record.draft?
private
def owner? = user.id == record.user_id end
Controller Integration
class PostsController < ApplicationController def show @post = Post.find(params[:id]) authorize! @post end
def update @post = Post.find(params[:id]) authorize! @post @post.update(post_params) ? redirect_to(@post) : render(:edit) end
def publish @post = Post.find(params[:id]) authorize! @post, to: :publish? @post.publish! redirect_to @post end end
Conditional Rendering
<% if allowed_to?(:edit?, @post) %> <%= link_to "Edit", edit_post_path(@post) %> <% end %>
Policy Scoping
class PostPolicy < ApplicationPolicy relation_scope do |relation| user.admin? ? relation.all : relation.where(user_id: user.id).or(relation.published) end
relation_scope(:own) { |relation| relation.where(user_id: user.id) } relation_scope(:drafts) { |relation| relation.where(user_id: user.id, status: :draft) } end
Controller usage
@posts = authorized_scope(Post.all) @drafts = authorized_scope(Post.all, type: :relation, as: :drafts)
Caching
class PostPolicy < ApplicationPolicy def update? cache { owner_or_collaborator? } # Cache expensive checks end end
config/initializers/action_policy.rb
ActionPolicy.configure do |config| config.cache_store = Rails.cache end
I18n Failure Messages
config/locales/action_policy.en.yml
en: action_policy: policy: post_policy: update?: "You can only edit your own posts" destroy?: "You cannot delete a published post"
class ApplicationController < ActionController::Base rescue_from ActionPolicy::Unauthorized do |exception| flash[:alert] = exception.result.message redirect_back fallback_location: root_path end end
Deliverables
When implementing authorization, provide:
-
Policy Classes: With rules, scopes, and caching
-
Controller Integration: authorize! and allowed_to? usage
-
Scoping: For index actions and filtered collections
-
I18n: Localized error messages
-
Tests: RSpec policy and request specs
-
GraphQL: preauthorize for mutations if applicable