AbilityList
Simple permissions system as plain old Ruby objects. No fancy integration with ORMs or frameworks.
All of this is just a single Ruby file with less than 50 lines of significant code. Read it now.
Defining abilities
Define the list of abilities a user has by subclassing AbilityList.
Each ability is comprised of a verb (required) and an object (optional). A verb is any symbol, while the object can be a symbol or a class.
class Abilities < AbilityList
def initialize(user)
can :view, Video
if user.admin?
can :delete, Video
can :upload, Video
end
can :login
can :view, :admin
end
end
Then hook it to user by defining an abilities method.
class User < OpenStruct
include AbilityList::Helpers
def abilities
@abilities ||= Abilities.new(self)
end
end
Checking for abilities
Now you may use can?:
user = User.new
user.can?(:view, Video)
user.can?(:view, Video.find(20))
user.can?(:login)
user.can?(:view, :admin)
The inverse cannot? is also available.
Raising errors
Or you can use authorize!, which is exactly like can? except it raises
an AbilityList::Error exception. Perfect for controllers.
user. :view, Video.find(20)
Custom criteria
You can pass a block to can for custom criteria:
can :view, Video do |video|
!video.restricted? or user.age > 18
end
You can even use Ruby's &:sym syntax:
cannot :edit, Article, &:published?
# Equivalent to cannot(:edit, Article) { |article| article.published? }
Object types
The method can always accepts at least 2 arguments: a verb and an object.
You can define your permissions by passing a class as the object:
can :view, Video
which makes it possible to check for instances or classes:
user.can?(:view, Video) #-> passing a class
user.can?(:view, Video.find(1008)) #-> passing an instance
But this doesn't have to be classes. Just pass anything else, like a symbol:
can :login, :mobile_site
# user.can?(:login, :mobile_site)
Overriding criteria
Criteria are evaluated on a top-down basis, and the ones at the bottom will override the ones on top.
The method cannot is provided to make exceptions to rules.
For example:
# Everyone can edit comments.
can :edit, Comment
# ...but unconfirmed users can't edit their comments.
if user.unconfirmed?
cannot :edit, Comment
end
# ...but if the comments are really new, they can be edited, even if the user
# hasn't confirmed.
can :edit, Comment { |c| c.created_at < 3.minutes.ago }
The :manage keyword
You can use :manage as the verb to allow any verb.
can :manage, Group
This allows the user to do anything to Group its instances.
user.can?(:delete, Group) #=> true
user.can?(:create, Group) #=> true
user.can?(:eviscerate, Group) #=> true
The :all keyword
You can use :all as the object for any permission. This allows a verb to work
on anything.
Don't know why you'll want this, but cancan has it, so:
can :delete, :all
So you can:
user.can?(:delete, Video) #=> true
user.can?(:delete, Article) #=> true
user.can?(:delete, Recipe) #=> true
More examples
See RECIPES.md for some practical examples.
Limitations
AbilityList aims to be extremely lean, and to be as framework- and ORM-agnostic as possible. As such, it doesn't:
No explicit integration with Rails controllers.
No explicit integration with ActiveRecord (or any other ORM).
No explicit provisions for roles.
See RECIPES.md on how to do these things.
Acknowledgements
Heavily inspired by cancan. AbilityList is generally a stripped-down version of cancan with a lot less features (see Limitations) above.
(c) 2013 MIT License.