ability_list-cj 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: