Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Last active June 7, 2022 20:04
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 JoshCheek/62484a409b078f9ce89931fc7a610002 to your computer and use it in GitHub Desktop.
Save JoshCheek/62484a409b078f9ce89931fc7a610002 to your computer and use it in GitHub Desktop.
Extending CanCanCan, abilities
require 'cancan'
# Some models
Post = Struct.new(:author, :published, :body) { alias published? published }
User = Struct.new(:name, :admin) { alias admin? admin }
# Abilities
class CommonAbility
include CanCan::Ability
def initialize(user)
can(:read, Post) { |post| post.published? || post.author == user }
can(:edit, Post) { |post| post.author == user }
can :favourite, Post
end
end
class AdminAbility
include CanCan::Ability
def initialize(admin)
can :edit, Post
cannot :favourite, Post
end
end
# Test data
reader = User.new 'reader', false
author = User.new 'author', false
admin = User.new 'admin', true
published_post = Post.new author, true, 'My finished thoughts'
private_post = Post.new author, false, 'My unfinished thoughts'
reader_ability = CommonAbility.new(reader)
author_ability = CommonAbility.new(author)
admin_ability = CommonAbility.new(admin).merge(AdminAbility.new(admin)) # <-- must be instantiated like this (must merge admin into common, and common must be a new instance b/c `merge` mutates it)
# Test falling back to common enabled static ability: reading a published post (everyone can, via common ability)
reader_ability.can? :read, published_post # => true
author_ability.can? :read, published_post # => true
admin_ability.can? :read, published_post # => true
# Test falling back to common enabled dynamic ability: reading a private post (author can via common ability)
reader_ability.can? :read, private_post # => false
author_ability.can? :read, private_post # => true
admin_ability.can? :read, private_post # => false
# Test overriding to grant an ability: editing post (author can via common ability, admin can via admin ability)
reader_ability.can? :edit, published_post # => false
author_ability.can? :edit, published_post # => true
admin_ability.can? :edit, published_post # => true
# Test overriding to remove an ability: favouriting a post (admin cannot, b/c via admin ability)
reader_ability.can? :favourite, published_post # => true
author_ability.can? :favourite, published_post # => true
admin_ability.can? :favourite, published_post # => false
# Test abilities that were never defined: deleting a post (no one can)
reader_ability.can? :delete, published_post # => false
author_ability.can? :delete, published_post # => false
admin_ability.can? :delete, published_post # => false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment