ability_list-cj 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a7a5761f53ea91eba6dc1592cb55a09cdb0e68fe
4
+ data.tar.gz: 46589d0fb886ec71b0a10a8bb236c958a20f8bde
5
+ SHA512:
6
+ metadata.gz: 1ade215924a122f5cda5f7161a98774e645b5ca22a00ee0d901ea05da1ca218657b2163415ccce2d667f9cf2981d6f4ca51039f3b3df755a3975e057eb9181b3
7
+ data.tar.gz: d898db2cdcb81289796623b07f198c4f2fdf78042db937b661ce3baa7a80935d11acd37a84580dc47911166c1b7d7ca8c013cf7135b40f97fb0cee960bf3c489
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ gemspec
@@ -0,0 +1,200 @@
1
+ AbilityList
2
+ ===========
3
+
4
+ Simple permissions system as plain old Ruby objects. No fancy integration with
5
+ ORMs or frameworks.
6
+
7
+ All of this is just a single Ruby file with less than 50 lines of significant
8
+ code. [Read it now][ability_list.rb].
9
+
10
+ ## Defining abilities
11
+
12
+ Define the list of abilities a user has by subclassing `AbilityList`.
13
+
14
+ Each ability is comprised of a **verb** (required) and an **object** (optional).
15
+ A *verb* is any symbol, while the *object* can be a symbol or a class.
16
+
17
+ ``` ruby
18
+ class Abilities < AbilityList
19
+ def initialize(user)
20
+ can :view, Video
21
+
22
+ if user.admin?
23
+ can :delete, Video
24
+ can :upload, Video
25
+ end
26
+
27
+ can :login
28
+
29
+ can :view, :admin
30
+ end
31
+ end
32
+ ```
33
+
34
+ Then hook it to user by defining an `abilities` method.
35
+
36
+ ``` ruby
37
+ class User < OpenStruct
38
+ include AbilityList::Helpers
39
+
40
+ def abilities
41
+ @abilities ||= Abilities.new(self)
42
+ end
43
+ end
44
+ ```
45
+
46
+ ## Checking for abilities
47
+
48
+ Now you may use `can?`:
49
+
50
+ ``` ruby
51
+ user = User.new
52
+ user.can?(:view, Video)
53
+ user.can?(:view, Video.find(20))
54
+
55
+ user.can?(:login)
56
+ user.can?(:view, :admin)
57
+ ```
58
+
59
+ The inverse `cannot?` is also available.
60
+
61
+ ## Raising errors
62
+
63
+ Or you can use `authorize!`, which is exactly like `can?` except it raises
64
+ an `AbilityList::Error` exception. Perfect for controllers.
65
+
66
+ ``` ruby
67
+ user.authorize! :view, Video.find(20)
68
+ ```
69
+
70
+ ## Custom criteria
71
+
72
+ You can pass a block to `can` for custom criteria:
73
+
74
+ ``` ruby
75
+ can :view, Video do |video|
76
+ !video.restricted? or user.age > 18
77
+ end
78
+ ```
79
+
80
+ You can even use Ruby's `&:sym` syntax:
81
+
82
+ ``` ruby
83
+ cannot :edit, Article, &:published?
84
+
85
+ # Equivalent to cannot(:edit, Article) { |article| article.published? }
86
+ ```
87
+
88
+ ## Object types
89
+
90
+ The method `can` always accepts at least 2 arguments: a *verb* and an *object*.
91
+
92
+ You can define your permissions by passing a class as the object:
93
+
94
+ ``` ruby
95
+ can :view, Video
96
+ ```
97
+
98
+ which makes it possible to check for instances or classes:
99
+
100
+ ``` ruby
101
+ user.can?(:view, Video) #-> passing a class
102
+ user.can?(:view, Video.find(1008)) #-> passing an instance
103
+ ```
104
+
105
+ But this doesn't have to be classes. Just pass anything else, like a symbol:
106
+
107
+ ``` ruby
108
+ can :login, :mobile_site
109
+
110
+ # user.can?(:login, :mobile_site)
111
+ ```
112
+
113
+ ## Overriding criteria
114
+
115
+ Criteria are evaluated on a top-down basis, and the ones at the bottom will
116
+ override the ones on top.
117
+
118
+ The method `cannot` is provided to make exceptions to rules.
119
+
120
+ For example:
121
+
122
+ ``` ruby
123
+ # Everyone can edit comments.
124
+ can :edit, Comment
125
+
126
+ # ...but unconfirmed users can't edit their comments.
127
+ if user.unconfirmed?
128
+ cannot :edit, Comment
129
+ end
130
+
131
+ # ...but if the comments are really new, they can be edited, even if the user
132
+ # hasn't confirmed.
133
+ can :edit, Comment { |c| c.created_at < 3.minutes.ago }
134
+ ```
135
+
136
+ ## The `:manage` keyword
137
+
138
+ You can use `:manage` as the verb to allow any verb.
139
+
140
+ ``` ruby
141
+ can :manage, Group
142
+ ```
143
+
144
+ This allows the user to do anything to `Group` its instances.
145
+
146
+ ``` ruby
147
+ user.can?(:delete, Group) #=> true
148
+ user.can?(:create, Group) #=> true
149
+ user.can?(:eviscerate, Group) #=> true
150
+ ```
151
+
152
+ ## The `:all` keyword
153
+
154
+ You can use `:all` as the object for any permission. This allows a verb to work
155
+ on anything.
156
+
157
+ Don't know why you'll want this, but cancan has it, so:
158
+
159
+ ``` ruby
160
+ can :delete, :all
161
+ ```
162
+
163
+ So you can:
164
+
165
+ ``` ruby
166
+ user.can?(:delete, Video) #=> true
167
+ user.can?(:delete, Article) #=> true
168
+ user.can?(:delete, Recipe) #=> true
169
+ ```
170
+
171
+ More examples
172
+ -------------
173
+
174
+ See [RECIPES.md] for some practical examples.
175
+
176
+ Limitations
177
+ -----------
178
+
179
+ AbilityList aims to be extremely lean, and to be as framework- and ORM-agnostic
180
+ as possible. As such, it doesn't:
181
+
182
+ * No explicit integration with Rails controllers.
183
+
184
+ * No explicit integration with ActiveRecord (or any other ORM).
185
+
186
+ * No explicit provisions for roles.
187
+
188
+ See [RECIPES.md] on how to do these things.
189
+
190
+ Acknowledgements
191
+ ----------------
192
+
193
+ Heavily inspired by [cancan]. AbilityList is generally a stripped-down version
194
+ of cancan with a lot less features (see Limitations) above.
195
+
196
+ (c) 2013 MIT License.
197
+
198
+ [cancan]: https://github.com/ryanb/cancan
199
+ [RECIPES.md]: https://github.com/rstacruz/ability_list/blob/master/RECIPES.md
200
+ [ability_list.rb]:https://github.com/rstacruz/ability_list/blob/master/lib/ability_list.rb
@@ -0,0 +1,101 @@
1
+ How to implement roles
2
+ ----------------------
3
+
4
+ AbilityList has no explicit support for roles because they're pretty easy to do
5
+ in the plain ol' Ruby way.
6
+
7
+ ``` ruby
8
+ class Abilities < AbilityList
9
+ def initialize(user)
10
+ # Assume this returns an array of strings like ['admin', 'editor'].
11
+ roles = user.roles
12
+
13
+ # Now simply call the respective methods of each role.
14
+ roles.each { |role| send role }
15
+ end
16
+
17
+ def admin
18
+ can :manage, User
19
+ can :manage, Group
20
+ end
21
+
22
+ def editor
23
+ can :edit, Article
24
+ end
25
+
26
+ def publisher
27
+ can :publish, Article
28
+ end
29
+
30
+ def writer
31
+ can :create, Article
32
+ end
33
+ end
34
+ ```
35
+
36
+ How to integrate with Rails controllers
37
+ ---------------------------------------
38
+
39
+ This Rails example raises an error when the logged in user is not allowed of a
40
+ certain permission.
41
+
42
+ ``` ruby
43
+ class ArticleController
44
+ before_filter :authorize_viewing
45
+
46
+ private
47
+
48
+ def authorize_viewing
49
+ current_user.authorize! :view, Article
50
+ end
51
+ end
52
+ ```
53
+
54
+ How to implement user levels
55
+ ----------------------------
56
+
57
+ If your `User` model has a numeric field `level`, where more permissions are
58
+ available to users with higher levels, you can implement it like so:
59
+
60
+ ``` ruby
61
+ class Abilities < AbilityList
62
+ def initialize(user)
63
+ if user.level > 50
64
+ can :launch, Rocket
65
+ can :command, Army
66
+ end
67
+
68
+ if user.level > 30
69
+ can :view_status, Rocket
70
+ end
71
+
72
+ if user.level > 1
73
+ can :login, :site
74
+ end
75
+ end
76
+ end
77
+ ```
78
+
79
+ Defining Rails helpers
80
+ ----------------------
81
+
82
+ The `Helpers` module used in Users can be used as Rails helpers too.
83
+
84
+ ``` ruby
85
+ module PermissionsHelper
86
+ # Provides `can?` and `cannot?`... as long as you have `#abilities` defined.
87
+ include AbilityList::Helpers
88
+
89
+ def abilities
90
+ current_user.try :abilities
91
+ end
92
+ end
93
+ ```
94
+
95
+ So that you may:
96
+
97
+ ``` erb
98
+ <% if can?(:edit, @post) %>
99
+ <a href="#edit">Edit</a>
100
+ <% end %>
101
+ ```
@@ -0,0 +1,6 @@
1
+ desc "Run tests."
2
+ task :test do
3
+ Dir['./test/*_test.rb'].each { |f| load f }
4
+ end
5
+
6
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ require './lib/ability_list/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "ability_list-cj"
5
+ s.version = AbilityList::VERSION
6
+ s.summary = %[Simple user permissions management.]
7
+ s.description = %[A very simple way to manage permissions. Works with any ORM.]
8
+ s.authors = ["Rico Sta. Cruz", 'CJ Lazell']
9
+ s.email = ["hi@ricostacruz.com"]
10
+ s.homepage = "http://github.com/cj/ability_list"
11
+ s.files = `git ls-files`.strip.split("\n")
12
+
13
+ s.add_development_dependency "minitest"
14
+ s.add_development_dependency "rake"
15
+ end
@@ -0,0 +1,90 @@
1
+ class AbilityList
2
+ Error = Class.new(StandardError)
3
+
4
+ # Returns a list of rules. These are populated by `can` and `cannot`.
5
+ # (Rules are tuples)
6
+ def rules
7
+ @rules ||= []
8
+ end
9
+
10
+ # ---
11
+
12
+ # Declares that the owner can perform `verb` on `class`.
13
+ def can(verb, klass=nil, columns=[], &block)
14
+ columns = [columns] unless columns.is_a? Array
15
+ rules << [true, verb, get_class(klass), columns, block]
16
+ end
17
+
18
+ # Inverse of `can`.
19
+ def cannot(verb, klass=nil, columns=[], &block)
20
+ columns = [columns] unless columns.is_a? Array
21
+ rules << [false, verb, get_class(klass), columns, block]
22
+ end
23
+
24
+ # ---
25
+
26
+ # Checks if the owner can perform `verb` on the given `object` (or class).
27
+ def can?(verb, object=nil, columns=[])
28
+ columns = [columns] unless columns.is_a? Array
29
+ rules = rules_for(verb, get_class(object))
30
+ rules.inject(false) do |bool, (sign, _, _, cols, proc)|
31
+ sign ?
32
+ ((bool || !proc || proc.call(object)) && ((columns & cols) == columns)) : # can
33
+ (bool && proc && !proc.call(object) && (columns.empty? || (columns & cols) != columns)) # cannot
34
+ end
35
+ end
36
+
37
+ # Inverse of `can?`.
38
+ def cannot?(verb, object=nil, columns)
39
+ ! can?(verb, object, columns)
40
+ end
41
+
42
+ # ---
43
+
44
+ # Ensures that the owner can perform `verb` on `object/class` -- raises an
45
+ # error otherwise.
46
+ def authorize!(verb, object=nil)
47
+ can?(verb, object) or raise Error.new("Access denied (#{verb})")
48
+ end
49
+
50
+ # Inverse of `authorize!`.
51
+ def unauthorize!(verb, object=nil)
52
+ cannot?(verb, object) or raise Error.new("Access denied (#{verb})")
53
+ end
54
+
55
+ # ---
56
+
57
+ # Returns a subset of `rules` that match the given `verb` and `class`.
58
+ def rules_for(verb, klass)
59
+ rules.select do |(sign, _verb, _klass, cols, block)|
60
+ (_verb == :manage || _verb == verb) &&
61
+ (_klass == :all || _klass == klass)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def get_class(object)
68
+ [NilClass, Symbol, Class].include?(object.class) ? object : object.class
69
+ end
70
+ end
71
+
72
+ # Provides `#can?` and `#cannot?` and other helpers.
73
+ # Assumes that you have an `#ability` method defined.
74
+ module AbilityList::Helpers
75
+ def can?(*a)
76
+ abilities && abilities.can?(*a)
77
+ end
78
+
79
+ def cannot?(*a)
80
+ !abilities || abilities.cannot?(*a)
81
+ end
82
+
83
+ def authorize!(*a)
84
+ raise AbilityList::Error.new("No 'ability' defined") unless abilities
85
+ abilities.authorize!(*a)
86
+ end
87
+ end
88
+
89
+ require 'ability_list/version'
90
+
@@ -0,0 +1,3 @@
1
+ class AbilityList
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path('../helper.rb', __FILE__)
2
+
3
+ module BasicTest
4
+ Video = Class.new(OpenStruct)
5
+
6
+ # ---
7
+
8
+ class User < OpenStruct
9
+ include AbilityList::Helpers
10
+
11
+ def abilities
12
+ @abilities ||= Abilities.new(self)
13
+ end
14
+ end
15
+
16
+ # ---
17
+
18
+ class Abilities < AbilityList
19
+ def initialize(user)
20
+ # Every can view videos.
21
+ can :view, Video
22
+
23
+ # ...except restricted ones.
24
+ cannot :view, Video, &:restricted
25
+
26
+ # ...but for those that are restricted, it's okay if the user is old enough.
27
+ can :view, Video do |vid|
28
+ vid.restricted && user.age > 18
29
+ end
30
+ end
31
+ end
32
+
33
+ # ---
34
+
35
+ describe "Basic tests" do
36
+ it "#can? 1" do
37
+ user = User.new :age => 22
38
+ video = Video.new :restricted => false
39
+
40
+ user.can?(:view, video).must_equal true
41
+ end
42
+
43
+ it "#can? 2" do
44
+ user = User.new :age => 10
45
+ video = Video.new :restricted => true
46
+
47
+ user.can?(:view, video).must_equal false
48
+ end
49
+
50
+ it "#can? 3" do
51
+ user = User.new :age => 42
52
+ video = Video.new :restricted => true
53
+
54
+ user.can?(:view, video).must_equal true
55
+ end
56
+
57
+ it "#cannot?" do
58
+ user = User.new :age => 10
59
+ video = Video.new :restricted => true
60
+
61
+ user.cannot?(:view, video).must_equal true
62
+ end
63
+
64
+ it "#authorize!" do
65
+ user = User.new :age => 10
66
+ video = Video.new :restricted => true
67
+
68
+ assert_raises AbilityList::Error do
69
+ user.authorize! :view, video
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path('../helper.rb', __FILE__)
2
+
3
+ module ColumnTest
4
+ Video = Class.new(OpenStruct)
5
+ Player = Class.new(OpenStruct)
6
+
7
+ # ---
8
+
9
+ class User < OpenStruct
10
+ include AbilityList::Helpers
11
+
12
+ def abilities
13
+ @abilities ||= Abilities.new(self)
14
+ end
15
+ end
16
+
17
+ # ---
18
+
19
+ class Abilities < AbilityList
20
+ def initialize(user)
21
+ # Every can view videos.
22
+ can :view, Video, [:title, :description]
23
+ can :view, Player, :name
24
+ end
25
+ end
26
+
27
+ # ---
28
+
29
+ describe 'Column tests' do
30
+ it '#can? 1' do
31
+ user = User.new
32
+ video = Video.new title: 'moo', description: 'cow', about: 'grass'
33
+ player = Player.new name: 'Bob', age: 100
34
+
35
+ user.can?(:view, video, :title).must_equal true
36
+ user.can?(:view, video, [:title, :description]).must_equal true
37
+ user.can?(:view, video, :about).must_equal false
38
+ user.can?(:view, player, :name).must_equal true
39
+ user.cannot?(:view, player, :age).must_equal true
40
+ user.cannot?(:view, player, :name).must_equal false
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'minitest/autorun'
4
+ require 'ability_list'
5
+ require 'ostruct'
6
+
7
+ class TestCase < MiniTest::Unit::TestCase
8
+
9
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path('../helper.rb', __FILE__)
2
+
3
+ module HelpersTest
4
+ Video = Class.new(OpenStruct)
5
+
6
+ # ---
7
+
8
+ class User < OpenStruct
9
+ include AbilityList::Helpers
10
+
11
+ def abilities
12
+ end
13
+ end
14
+
15
+ # ---
16
+
17
+ describe "Helper tests" do
18
+ let(:user) { User.new }
19
+
20
+ it '#can? fail' do
21
+ (!! user.can?(:cook, :spam)).must_equal false
22
+ end
23
+
24
+ it '#cannot? fail' do
25
+ (!! user.cannot?(:cook, :spam)).must_equal true
26
+ end
27
+
28
+ it '#authorize! fail' do
29
+ assert_raises AbilityList::Error do
30
+ user.authorize! :cook, :spam
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path('../helper.rb', __FILE__)
2
+
3
+ module NilTest
4
+ class User < OpenStruct
5
+ include AbilityList::Helpers
6
+
7
+ def abilities
8
+ @abilities ||= Abilities.new(self)
9
+ end
10
+ end
11
+
12
+ # ---
13
+
14
+ class Abilities < AbilityList
15
+ def initialize(user)
16
+ can :make, :fire
17
+ can :make
18
+ end
19
+ end
20
+
21
+ # ---
22
+
23
+ describe "Nil tests" do
24
+ let(:user) { User.new }
25
+
26
+ it "#can? 1" do
27
+ user.can?(:make, :fire).must_equal true
28
+ end
29
+ it "#can? 2" do
30
+ user.can?(:make).must_equal true
31
+ end
32
+ it "#can? 3" do
33
+ user.can?(:make, :lasagna).must_equal false
34
+ end
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ability_list-cj
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Rico Sta. Cruz
8
+ - CJ Lazell
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ description: A very simple way to manage permissions. Works with any ORM.
43
+ email:
44
+ - hi@ricostacruz.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - README.md
52
+ - RECIPES.md
53
+ - Rakefile
54
+ - ability_list.gemspec
55
+ - lib/ability_list.rb
56
+ - lib/ability_list/version.rb
57
+ - test/basic_test.rb
58
+ - test/column_test.rb
59
+ - test/helper.rb
60
+ - test/helpers_test.rb
61
+ - test/nil_test.rb
62
+ homepage: http://github.com/cj/ability_list
63
+ licenses: []
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.2.2
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Simple user permissions management.
85
+ test_files: []
86
+ has_rdoc: