rbacanable 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/Changes.rdoc +141 -0
- data/LICENSE +20 -0
- data/README.rdoc +164 -0
- data/Rakefile +44 -0
- data/examples/basic.rb +41 -0
- data/examples/roles.rb +100 -0
- data/lib/canable.rb +191 -0
- data/specs.watchr +47 -0
- data/test/helper.rb +33 -0
- data/test/test_ables.rb +83 -0
- data/test/test_canable.rb +26 -0
- data/test/test_cans.rb +51 -0
- data/test/test_enforcers.rb +32 -0
- data/test/test_roles.rb +353 -0
- metadata +124 -0
data/.document
ADDED
data/.gitignore
ADDED
data/Changes.rdoc
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
= RBACanable
|
2
|
+
=== Canable (by John Nunemaker) with a Role Based Access System
|
3
|
+
|
4
|
+
== How the system used to work
|
5
|
+
|
6
|
+
The User class or whichever object needed to be authorized against an action would include <tt>Canable::Cans</tt>. Objects being manipulated that needed protection would include <tt>Canable::Ables</tt>, and you would override the <tt>viewable_by?(user)</tt> methods to implement the various permissions governing that resource and the user trying to access it.
|
7
|
+
|
8
|
+
== How RBACanable works now
|
9
|
+
|
10
|
+
RBACanable is significantly different than the original Canable and unfortunately not compatible with existing code that uses Canable. The original Canable was oriented so that each resource (an object including <tt>Canable::Able</tt>) managed it's own permissions. RBACanable is oriented in the opposite way, where each object that wants to preform an action and needs authorization manages the permissions that govern it. This is necessary because each one of these action-performing objects (called Actors in RBACanable) get their governing rules from a series of Roles (any module including <tt>Canable::Role</tt>).
|
11
|
+
|
12
|
+
== Actors
|
13
|
+
|
14
|
+
Whatever class(es) you want all permissions to run through should include <tt>Canable::Actor</tt> instad of <tt>Canable::Cans</tt>.
|
15
|
+
|
16
|
+
class User > ActiveRecord::Base
|
17
|
+
include Canable::Actor
|
18
|
+
default_role :employee
|
19
|
+
end
|
20
|
+
|
21
|
+
Actors represent objects in the system that want to preform an action. An actor's abilities are defined by the role(s) it plays.
|
22
|
+
|
23
|
+
== Roles
|
24
|
+
|
25
|
+
The rules that govern your actors are defined in modules extending <tt>Canable::Role</tt> and/or each other.
|
26
|
+
|
27
|
+
module EmployeeRole
|
28
|
+
include Canable::Role
|
29
|
+
default_response false
|
30
|
+
end
|
31
|
+
|
32
|
+
module ManagerRole
|
33
|
+
include Canable::Role
|
34
|
+
default_response true
|
35
|
+
end
|
36
|
+
|
37
|
+
== Ables
|
38
|
+
|
39
|
+
class Article
|
40
|
+
include MongoMapper::Document
|
41
|
+
include Canable::Ables
|
42
|
+
end
|
43
|
+
|
44
|
+
Including Canable::Ables adds the able methods to the class including it. In this instance, any article instance now has viewable_by?(actor), creatable_by?(actor), updatable_by?(actor) and destroyable_by?(actor). Now
|
45
|
+
|
46
|
+
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.
|
47
|
+
|
48
|
+
class Article
|
49
|
+
include MongoMapper::Document
|
50
|
+
include Canable::Ables
|
51
|
+
userstamps! # adds creator and updater
|
52
|
+
|
53
|
+
def updatable_by?(user)
|
54
|
+
creator == user
|
55
|
+
end
|
56
|
+
|
57
|
+
def destroyable_by?(user)
|
58
|
+
updatable_by?(user)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Lets look at some sample code now:
|
63
|
+
|
64
|
+
john = User.create(:name => 'John')
|
65
|
+
steve = User.create(:name =. 'Steve')
|
66
|
+
|
67
|
+
ruby = Article.new(:title => 'Ruby')
|
68
|
+
john.can_create?(ruby) # true
|
69
|
+
steve.can_create?(ruby) # true
|
70
|
+
|
71
|
+
ruby.creator = john
|
72
|
+
ruby.save
|
73
|
+
|
74
|
+
john.can_view?(ruby) # true
|
75
|
+
steve.can_view?(ruby) # true
|
76
|
+
|
77
|
+
john.can_update?(ruby) # true
|
78
|
+
steve.can_update?(ruby) # false
|
79
|
+
|
80
|
+
john.can_destroy?(ruby) # true
|
81
|
+
steve.can_destroy?(ruby) # false
|
82
|
+
|
83
|
+
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.
|
84
|
+
|
85
|
+
== Enforcers
|
86
|
+
|
87
|
+
class ApplicationController
|
88
|
+
include Canable::Enforcers
|
89
|
+
end
|
90
|
+
|
91
|
+
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:
|
92
|
+
|
93
|
+
class ApplicationController
|
94
|
+
include Canable::Enforcers
|
95
|
+
|
96
|
+
delegate :can_view?, :to => :current_user
|
97
|
+
helper_method :can_view? # so you can use it in your views
|
98
|
+
hide_action :can_view?
|
99
|
+
|
100
|
+
private
|
101
|
+
def enforce_view_permission(resource)
|
102
|
+
raise Canable::Transgression unless can_view?(resource)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Which means you can use it like this:
|
107
|
+
|
108
|
+
class ArticlesController < ApplicationController
|
109
|
+
def show
|
110
|
+
@article = Article.find!(params[:id])
|
111
|
+
enforce_view_permission(@article)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
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.).
|
116
|
+
|
117
|
+
== Adding Your Own Actions
|
118
|
+
|
119
|
+
You can add your own actions like this:
|
120
|
+
|
121
|
+
Canable.add(:publish, :publishable)
|
122
|
+
|
123
|
+
The first parameter is the can method (ie: can_publish?) and the second is the able method (ie: publishable_by?).
|
124
|
+
|
125
|
+
== Review
|
126
|
+
|
127
|
+
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.
|
128
|
+
|
129
|
+
== Note on Patches/Pull Requests
|
130
|
+
|
131
|
+
* Fork the project.
|
132
|
+
* Make your feature addition or bug fix.
|
133
|
+
* Add tests for it. This is important so I don't break it in a
|
134
|
+
future version unintentionally.
|
135
|
+
* Commit, do not mess with rakefile, version, or history.
|
136
|
+
(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)
|
137
|
+
* Send me a pull request. Bonus points for topic branches.
|
138
|
+
|
139
|
+
== Copyright
|
140
|
+
|
141
|
+
Copyright (c) 2010 John Nunemaker. See LICENSE for details.
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 John Nunemaker
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
= RBACanable
|
2
|
+
=== Canable (by John Nunemaker) with a Role Based Access System latched on
|
3
|
+
|
4
|
+
Simple permissions system that features easily actors, resources for actors to manipulate, and roles actors play to define their permissions.
|
5
|
+
|
6
|
+
|
7
|
+
== Ables
|
8
|
+
|
9
|
+
Ables are the resource objets that an actor will want to manipulate.
|
10
|
+
|
11
|
+
class Article
|
12
|
+
include MongoMapper::Document
|
13
|
+
include Canable::Ables
|
14
|
+
end
|
15
|
+
|
16
|
+
Including Canable::Ables adds the able methods to the class including it. In this instance, any article instance now has viewable_by?(actor), creatable_by?(actor), updatable_by?(actor) and destroyable_by?(actor).
|
17
|
+
|
18
|
+
== Roles
|
19
|
+
|
20
|
+
The rules that govern your actors are defined in roles. Roles are modules that extend <tt>Canable::Role</tt> and/or other roles. Roles can be nested as deep and mixed and matched using Ruby's mixin system, as they are just normal Ruby modules that carry permission sets with them. The order they are included in each other will define the final permission set of the role: Roles included after (on a lower line) than others will override the settings of the others.
|
21
|
+
module Canable::Roles
|
22
|
+
module EmployeeRole
|
23
|
+
include Canable::Role
|
24
|
+
default_response false
|
25
|
+
|
26
|
+
def can_view_article?(article)
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def can_update_article?(article)
|
31
|
+
article.owner == @name
|
32
|
+
end
|
33
|
+
|
34
|
+
def can_destroy_article?(article)
|
35
|
+
self.can_update_article?(article)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ManagerRole
|
40
|
+
include Canable::Role
|
41
|
+
include EmployeeRole
|
42
|
+
|
43
|
+
def can_update_article?(article)
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def can_destroy_article(article)
|
48
|
+
article.owner == @name
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Here two roles are defined, one for employees and one for managers. Employees by default can't do anything, this is set by <tt>default_response false</tt> on the module. Then methods governing the actor's interaction with the Article model by defining the <tt>can_view_article?</tt> to always return true (Employees can view any article), and defining the <tt>can_update_article?</tt> method to only return true if the actor playing the employee role is the owner of the article.
|
54
|
+
|
55
|
+
The manager role includes the employee role, so it inherits the default_response behaviour of false and the defined rules for interactions with articles. Here, the <tt>can_update_article?</tt> method is override to always return true, so managers can edit any article. Note that <tt>EmployeeRole#can_destroy_article?(article)</tt> calls <tt>self.can_update_article?(article)</tt>, so when the ManagerRole inherits the EmployeeRole, <tt>ManagerRole#can_destroy_article?(article)</tt> returns the result of <tt>ManagerRole#can_update_article?(article)</tt>, which returns true. This means that when ManagerRole inherits EmployeeRole, the inherited methods behave differently, which might be unexpected. To mitigate this in the example above, <tt>ManagerRole#can_destroy_article?(article)</tt> is overridden to check if the Manager trying to destroy the role is the article owner.
|
56
|
+
|
57
|
+
== Actors
|
58
|
+
|
59
|
+
Whatever class(es) you want all permissions to run through should include <tt>Canable::Actor</tt>, instead of <tt>Canable::Cans</tt> from the original Canable gem.
|
60
|
+
|
61
|
+
class User > ActiveRecord::Base
|
62
|
+
include Canable::Actor
|
63
|
+
default_role :employee
|
64
|
+
end
|
65
|
+
|
66
|
+
Actors represent objects in the system that want to preform an action. An actor's abilities are defined by the role(s) it plays because the actor simply inherits any permission. Roles can be assigned to an actor ("played") by using the <tt>default_role(role)</tt> class method, or calling <tt>act(role)<tt> on an actor instance.
|
67
|
+
|
68
|
+
|
69
|
+
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.
|
70
|
+
|
71
|
+
class Article
|
72
|
+
include MongoMapper::Document
|
73
|
+
include Canable::Ables
|
74
|
+
userstamps! # adds creator and updater
|
75
|
+
|
76
|
+
def updatable_by?(user)
|
77
|
+
creator == user
|
78
|
+
end
|
79
|
+
|
80
|
+
def destroyable_by?(user)
|
81
|
+
updatable_by?(user)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Lets look at some sample code now:
|
86
|
+
|
87
|
+
john = User.create(:name => 'John')
|
88
|
+
steve = User.create(:name =. 'Steve')
|
89
|
+
|
90
|
+
ruby = Article.new(:title => 'Ruby')
|
91
|
+
john.can_create?(ruby) # true
|
92
|
+
steve.can_create?(ruby) # true
|
93
|
+
|
94
|
+
ruby.creator = john
|
95
|
+
ruby.save
|
96
|
+
|
97
|
+
john.can_view?(ruby) # true
|
98
|
+
steve.can_view?(ruby) # true
|
99
|
+
|
100
|
+
john.can_update?(ruby) # true
|
101
|
+
steve.can_update?(ruby) # false
|
102
|
+
|
103
|
+
john.can_destroy?(ruby) # true
|
104
|
+
steve.can_destroy?(ruby) # false
|
105
|
+
|
106
|
+
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.
|
107
|
+
|
108
|
+
== Enforcers
|
109
|
+
|
110
|
+
class ApplicationController
|
111
|
+
include Canable::Enforcers
|
112
|
+
end
|
113
|
+
|
114
|
+
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:
|
115
|
+
|
116
|
+
class ApplicationController
|
117
|
+
include Canable::Enforcers
|
118
|
+
|
119
|
+
delegate :can_view?, :to => :current_user
|
120
|
+
helper_method :can_view? # so you can use it in your views
|
121
|
+
hide_action :can_view?
|
122
|
+
|
123
|
+
private
|
124
|
+
def enforce_view_permission(resource)
|
125
|
+
raise Canable::Transgression unless can_view?(resource)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
Which means you can use it like this:
|
130
|
+
|
131
|
+
class ArticlesController < ApplicationController
|
132
|
+
def show
|
133
|
+
@article = Article.find!(params[:id])
|
134
|
+
enforce_view_permission(@article)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
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.).
|
139
|
+
|
140
|
+
== Adding Your Own Actions
|
141
|
+
|
142
|
+
You can add your own actions like this:
|
143
|
+
|
144
|
+
Canable.add(:publish, :publishable)
|
145
|
+
|
146
|
+
The first parameter is the can method (ie: can_publish?) and the second is the able method (ie: publishable_by?).
|
147
|
+
|
148
|
+
== Review
|
149
|
+
|
150
|
+
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.
|
151
|
+
|
152
|
+
== Note on Patches/Pull Requests
|
153
|
+
|
154
|
+
* Fork the project.
|
155
|
+
* Make your feature addition or bug fix.
|
156
|
+
* Add tests for it. This is important so I don't break it in a
|
157
|
+
future version unintentionally.
|
158
|
+
* Commit, do not mess with rakefile, version, or history.
|
159
|
+
(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)
|
160
|
+
* Send me a pull request. Bonus points for topic branches.
|
161
|
+
|
162
|
+
== Copyright
|
163
|
+
|
164
|
+
Copyright (c) 2010 John Nunemaker. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/lib/canable'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "rbacanable"
|
10
|
+
gem.summary = %Q{Simple role based permissions system}
|
11
|
+
gem.description = %Q{Simple role based permissions system}
|
12
|
+
gem.email = "harry.brundage@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/hornairs/rbacanable"
|
14
|
+
gem.authors = ["John Nunemaker", "Harry Brundage"]
|
15
|
+
gem.version = Canable::Version
|
16
|
+
gem.add_development_dependency "shoulda", "2.10.3"
|
17
|
+
gem.add_development_dependency "mocha", "0.9.8"
|
18
|
+
gem.add_development_dependency "yard", ">= 0"
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
Rake::TestTask.new(:test) do |test|
|
28
|
+
test.libs << 'lib' << 'test'
|
29
|
+
test.ruby_opts << '-rubygems'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :test => :check_dependencies
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
begin
|
38
|
+
require 'yard'
|
39
|
+
YARD::Rake::YardocTask.new
|
40
|
+
rescue LoadError
|
41
|
+
task :yardoc do
|
42
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
43
|
+
end
|
44
|
+
end
|
data/examples/basic.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_support'
|
5
|
+
require 'canable'
|
6
|
+
|
7
|
+
class User
|
8
|
+
include Canable::Cans
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
def initialize(attributes = {})
|
12
|
+
@name = attributes[:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def can_update_article(article)
|
16
|
+
article.creator == @name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Article
|
21
|
+
include Canable::Ables
|
22
|
+
attr_accessor :creator
|
23
|
+
|
24
|
+
def initialize(attributes = {})
|
25
|
+
@creator = attributes[:creator]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
post1 = Article.new(:creator => "John")
|
30
|
+
user1 = User.new(:name => "John")
|
31
|
+
user2 = User.new(:name => "Steve")
|
32
|
+
|
33
|
+
puts "Can User1 view Post1? #{user1.can_view?(post1)}"
|
34
|
+
puts "Can User2 view Post1? #{user2.can_view?(post1)}"
|
35
|
+
puts "Is Post1 viewable by User1? #{post1.viewable_by?(user1)}"
|
36
|
+
puts "Is Post1 viewable by User2? #{post1.viewable_by?(user2)}"
|
37
|
+
puts
|
38
|
+
puts "Can User1 update Post1? #{user1.can_update?(post1)}"
|
39
|
+
puts "Can User2 update Post1? #{user2.can_update?(post1)}"
|
40
|
+
puts "Is Post1 updatable by User1? #{post1.updatable_by?(user1)}"
|
41
|
+
puts "Is Post1 updatable by User2? #{post1.updatable_by?(user2)}"
|
data/examples/roles.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_support'
|
5
|
+
require 'canable'
|
6
|
+
require 'terminal-table/import'
|
7
|
+
|
8
|
+
|
9
|
+
module Canable::Roles
|
10
|
+
module BasicRole
|
11
|
+
include Canable::Role
|
12
|
+
default_response false
|
13
|
+
end
|
14
|
+
|
15
|
+
module EmployeeRole
|
16
|
+
include Canable::Role
|
17
|
+
default_response false
|
18
|
+
|
19
|
+
def can_destroy_article?(article)
|
20
|
+
article.creator == @name
|
21
|
+
end
|
22
|
+
|
23
|
+
def can_update_article?(article)
|
24
|
+
article.creator == @name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ManagerRole
|
29
|
+
include Canable::Role
|
30
|
+
include EmployeeRole
|
31
|
+
|
32
|
+
def can_destroy_article?(article)
|
33
|
+
true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module AdminRole
|
38
|
+
include Canable::Role
|
39
|
+
default_response true
|
40
|
+
|
41
|
+
def can_create_article?(article)
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class User
|
48
|
+
include Canable::Actor
|
49
|
+
attr_accessor :name
|
50
|
+
|
51
|
+
default_role Canable::Roles::BasicRole
|
52
|
+
# role_attribute :@role -- RBACanable by default looks in @role for a Module constant or string to use as a role
|
53
|
+
|
54
|
+
def initialize(attributes = {})
|
55
|
+
@name = attributes[:name]
|
56
|
+
@role = attributes[:role]
|
57
|
+
self.__initialize_canable_role # nessecary since initialize is overridden
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
"#{@role.to_s}: #{@name}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Article
|
66
|
+
include Canable::Ables
|
67
|
+
attr_accessor :creator
|
68
|
+
|
69
|
+
def initialize(attributes = {})
|
70
|
+
@creator = attributes[:creator]
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
"#{@creator}'s article"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
users = [
|
79
|
+
User.new(:name => "John", :role => :employee),
|
80
|
+
User.new(:name => "Steve", :role => :manager),
|
81
|
+
User.new(:name => "Harry", :role => :admin),
|
82
|
+
User.new(:name => "Jim")
|
83
|
+
]
|
84
|
+
|
85
|
+
posts = [
|
86
|
+
Article.new(:creator => "John"),
|
87
|
+
Article.new(:creator => "Steve"),
|
88
|
+
Article.new(:creator => "Harry")
|
89
|
+
]
|
90
|
+
|
91
|
+
|
92
|
+
user_table = table do |t|
|
93
|
+
t.headings = "User", "Resource", "Can View?", "Can Create?", "Can Update?", "Can Destroy?", "Role"
|
94
|
+
users.each do |user|
|
95
|
+
posts.each do |post|
|
96
|
+
t << [user.to_s, post.to_s, user.can_view?(post), user.can_create?(post), user.can_update?(post), user.can_destroy?(post), user.canable_included_role]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
puts user_table
|