Skip to content

Instantly share code, notes, and snippets.

@chriscz
Last active June 11, 2021 11:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriscz/ac35172e0870248af16b23475a7ddb72 to your computer and use it in GitHub Desktop.
Save chriscz/ac35172e0870248af16b23475a7ddb72 to your computer and use it in GitHub Desktop.
Add ActiveRecord::Relation support to CanCan can? check
require 'cancancan_ability_ext'
class Ability
include CanCan::Ability
include CanCanCanAbilityExt
def initialize(user)
can :clone, Post, { id: user.post_ids }
end
end
# This module extends cancan abilities to work with ActiveRecord relations when used with +authorize!+ or
# +ability.can?+.
#
# The extension is bypassed if attribute or extra_args are defined.
module CanCanCanAbilityExt
def can?(action, subject, attribute = nil, *extra_args)
if attribute.nil? && extra_args.blank?
if subject.is_a?(ActiveRecord::Relation)
return can_collection?(action, subject.klass, subject)
end
end
super(action, subject, attribute, *extra_args)
end
private
def can_collection?(action, klass, unchecked_records)
accessible_records = klass.accessible_by(self, action).merge(unchecked_records)
accessible_records.count == unchecked_records.count
end
end
class PostsController < ApplicationController
def index
@posts = Post.accessible_by(current_ability)
end
def bulk_clone
# We want to raise an AccessDenied instead of using accessible_by here because we would rather let the user
# know they have an invalid post_id in their params than fulfilling the request on a subset of the post_ids.
@posts = Post.find(params[:post_ids])
authorize!(:clone, @posts)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment