Skip to content

Commit

Permalink
Update readme formatting to markdown
Browse files Browse the repository at this point in the history
  • Loading branch information
jnunemaker committed Apr 22, 2012
1 parent 07d548a commit 1023936
Showing 1 changed file with 105 additions and 83 deletions.
188 changes: 105 additions & 83 deletions README.md
@@ -1,136 +1,158 @@
= Canable # Canable


Simple permissions that I have used on my last several projects so I figured it was time to abstract and wrap up into something more easily reusable. Simple permissions that I have used on my last several projects so I figured it was time to abstract and wrap up into something more easily reusable.


== Cans ## Cans


Whatever class you want all permissions to run through should include Canable::Cans. Whatever class you want all permissions to run through should include Canable::Cans.


class User ```ruby
include MongoMapper::Document class User
include Canable::Cans include MongoMapper::Document
end include Canable::Cans
end
```


This means that an instance of a user automatically gets can methods for the default REST actions: can_view?(resource), can_create?(resource), can_update?(resource), can_destroy?(resource). This means that an instance of a user automatically gets can methods for the default REST actions: can_view?(resource), can_create?(resource), can_update?(resource), can_destroy?(resource).


== Ables ## Ables


Each of the can methods simply calls the related "able" method (viewable, creatable, updatable, destroyable) for the action (view, create, update, delete). Canable comes with defaults for this methods that you can then override as makes sense for your permissions. Each of the can methods simply calls the related "able" method (viewable, creatable, updatable, destroyable) for the action (view, create, update, delete). Canable comes with defaults for this methods that you can then override as makes sense for your permissions.


class Article ```ruby
include MongoMapper::Document class Article
include Canable::Ables include MongoMapper::Document
end include Canable::Ables
end
```


Including Canable::Ables adds the able methods to the class including it. In this instance, article now has viewable_by?(user), creatable_by?(user), updatable_by?(user) and destroyable_by?(user). Including Canable::Ables adds the able methods to the class including it. In this instance, article now has viewable_by?(user), creatable_by?(user), updatable_by?(user) and destroyable_by?(user).


Lets say an article can be viewed and created by anyone, but only updated or destroyed by the user that created the article. To do that, you could leave viewable_by? and creatable_by? alone as they default to true and just override the other methods. Lets say an article can be viewed and created by anyone, but only updated or destroyed by the user that created the article. To do that, you could leave viewable_by? and creatable_by? alone as they default to true and just override the other methods.


class Article ```ruby
include MongoMapper::Document class Article
include Canable::Ables include MongoMapper::Document
userstamps! # adds creator and updater include Canable::Ables
userstamps! # adds creator and updater
def updatable_by?(user)
creator == user def updatable_by?(user)
end creator == user
end
def destroyable_by?(user)
updatable_by?(user) def destroyable_by?(user)
end updatable_by?(user)
end end
end
```


Lets look at some sample code now: Lets look at some sample code now:


john = User.create(:name => 'John') ```ruby
steve = User.create(:name => 'Steve') john = User.create(:name => 'John')

steve = User.create(:name => 'Steve')
ruby = Article.new(:title => 'Ruby')
john.can_create?(ruby) # true ruby = Article.new(:title => 'Ruby')
steve.can_create?(ruby) # true john.can_create?(ruby) # true

steve.can_create?(ruby) # true
ruby.creator = john
ruby.save ruby.creator = john

ruby.save
john.can_view?(ruby) # true
steve.can_view?(ruby) # true john.can_view?(ruby) # true

steve.can_view?(ruby) # true
john.can_update?(ruby) # true
steve.can_update?(ruby) # false john.can_update?(ruby) # true

steve.can_update?(ruby) # false
john.can_destroy?(ruby) # true
steve.can_destroy?(ruby) # false john.can_destroy?(ruby) # true

steve.can_destroy?(ruby) # false
```

Now we can implement our permissions for each resource and then always check whether a user can or cannot do something. This makes it all really easy to test. Next, how would you use this in the controller. Now we can implement our permissions for each resource and then always check whether a user can or cannot do something. This makes it all really easy to test. Next, how would you use this in the controller.


== Enforcers ## Enforcers


class ApplicationController ```ruby
include Canable::Enforcers class ApplicationController
end include Canable::Enforcers
end
```


Including Canable::Enforcers adds an enforce permission method for each of the actions defined (by default view/create/update/destroy). It is the same thing as doing this for each Canable action: Including Canable::Enforcers adds an enforce permission method for each of the actions defined (by default view/create/update/destroy). It is the same thing as doing this for each Canable action:


class ApplicationController ```ruby
include Canable::Enforcers class ApplicationController
include Canable::Enforcers


delegate :can_view?, :to => :current_user delegate :can_view?, :to => :current_user
helper_method :can_view? # so you can use it in your views helper_method :can_view? # so you can use it in your views
hide_action :can_view? hide_action :can_view?


private private
def enforce_view_permission(resource) def enforce_view_permission(resource)
raise Canable::Transgression unless can_view?(resource) raise Canable::Transgression unless can_view?(resource)
end
end end
end
```


Which means you can use it like this: Which means you can use it like this:


class ArticlesController < ApplicationController ```ruby
def show class ArticlesController < ApplicationController
@article = Article.find!(params[:id]) def show
enforce_view_permission(@article) @article = Article.find!(params[:id])
end enforce_view_permission(@article)
end end
end
```


If the user can_view? the article, all is well. If not, a Canable::Transgression is raised which you can decide how to handle (show 404, slap them on the wrist, etc.). If the user can_view? the article, all is well. If not, a Canable::Transgression is raised which you can decide how to handle (show 404, slap them on the wrist, etc.).


== Adding Your Own Actions ## Adding Your Own Actions


You can add your own actions like this: You can add your own actions like this:


Canable.add(:publish, :publishable) ```ruby
Canable.add(:publish, :publishable)
```


The first parameter is the can method (ie: can_publish?) and the second is the able method (ie: publishable_by?). The first parameter is the can method (ie: can_publish?) and the second is the able method (ie: publishable_by?).


Ables can also be added as class methods. For example, to restrict access to an index action: Ables can also be added as class methods. For example, to restrict access to an index action:


Canable.add(:index, :indexable) ```ruby

Canable.add(:index, :indexable)
```

Then enforce by passing the class instead of the instance: Then enforce by passing the class instead of the instance:


class ArticlesController < ApplicationController ```ruby
def index class ArticlesController < ApplicationController
@articles = Article.all def index
enforce_index_permission(Article) @articles = Article.all
end enforce_index_permission(Article)
end end
end
```


Then in the article model, add the able check as a class method: Then in the article model, add the able check as a class method:


class Article ```ruby
... class Article
def self.indexable_by?(user) ...
!user.nil? def self.indexable_by?(user)
end !user.nil?
end end
end
```


== Review ## Review


So, lets review: cans go on user model, ables go on everything, you override ables in each model where you want to enforce permissions, and enforcers go after each time you find or initialize an object in a controller. Bing, bang, boom. So, lets review: cans go on user model, ables go on everything, you override ables in each model where you want to enforce permissions, and enforcers go after each time you find or initialize an object in a controller. Bing, bang, boom.


== Note on Patches/Pull Requests ## Contributing

* Fork the project. * Fork the project.
* Make your feature addition or bug fix. * Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a * Add tests for it. This is important so I don't break it in a
Expand All @@ -139,6 +161,6 @@ So, lets review: cans go on user model, ables go on everything, you override abl
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches. * Send me a pull request. Bonus points for topic branches.


== Copyright ## Copyright


Copyright (c) 2010 John Nunemaker. See LICENSE for details. Copyright (c) 2010 John Nunemaker. See LICENSE for details.

0 comments on commit 1023936

Please sign in to comment.