cando 0.1.0
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.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +79 -0
- data/LICENSE.txt +25 -0
- data/README.md +144 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/cando.gemspec +75 -0
- data/contrib/initial_schema.rb +39 -0
- data/lib/cando.rb +88 -0
- data/lib/models/capability.rb +14 -0
- data/lib/models/role.rb +27 -0
- data/lib/models/user.rb +40 -0
- data/lib/tasks/cando.rake +138 -0
- data/spec/cando_spec.rb +126 -0
- data/spec/spec_helper.rb +48 -0
- metadata +180 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
gem "sequel"
|
7
|
+
gem "rake"
|
8
|
+
|
9
|
+
# Add dependencies to develop your gem here.
|
10
|
+
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
group :development do
|
12
|
+
gem "rspec", "~> 2.10.0"
|
13
|
+
gem "rdoc", "~> 3.12"
|
14
|
+
gem "bundler", "~> 1.0"
|
15
|
+
gem "jeweler", "~> 2.0.1"
|
16
|
+
gem "simplecov", ">= 0"
|
17
|
+
end
|
18
|
+
|
19
|
+
group :test do
|
20
|
+
gem "mysql"
|
21
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.3.6)
|
5
|
+
builder (3.2.2)
|
6
|
+
descendants_tracker (0.0.4)
|
7
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
8
|
+
diff-lcs (1.1.3)
|
9
|
+
docile (1.1.3)
|
10
|
+
faraday (0.9.0)
|
11
|
+
multipart-post (>= 1.2, < 3)
|
12
|
+
git (1.2.6)
|
13
|
+
github_api (0.11.3)
|
14
|
+
addressable (~> 2.3)
|
15
|
+
descendants_tracker (~> 0.0.1)
|
16
|
+
faraday (~> 0.8, < 0.10)
|
17
|
+
hashie (>= 1.2)
|
18
|
+
multi_json (>= 1.7.5, < 2.0)
|
19
|
+
nokogiri (~> 1.6.0)
|
20
|
+
oauth2
|
21
|
+
hashie (2.1.1)
|
22
|
+
highline (1.6.21)
|
23
|
+
jeweler (2.0.1)
|
24
|
+
builder
|
25
|
+
bundler (>= 1.0)
|
26
|
+
git (>= 1.2.5)
|
27
|
+
github_api
|
28
|
+
highline (>= 1.6.15)
|
29
|
+
nokogiri (>= 1.5.10)
|
30
|
+
rake
|
31
|
+
rdoc
|
32
|
+
json (1.8.1)
|
33
|
+
jwt (0.1.13)
|
34
|
+
multi_json (>= 1.5)
|
35
|
+
mini_portile (0.5.3)
|
36
|
+
multi_json (1.10.0)
|
37
|
+
multi_xml (0.5.5)
|
38
|
+
multipart-post (2.0.0)
|
39
|
+
mysql (2.9.1)
|
40
|
+
nokogiri (1.6.1)
|
41
|
+
mini_portile (~> 0.5.0)
|
42
|
+
oauth2 (0.9.3)
|
43
|
+
faraday (>= 0.8, < 0.10)
|
44
|
+
jwt (~> 0.1.8)
|
45
|
+
multi_json (~> 1.3)
|
46
|
+
multi_xml (~> 0.5)
|
47
|
+
rack (~> 1.2)
|
48
|
+
rack (1.5.2)
|
49
|
+
rake (10.3.1)
|
50
|
+
rdoc (3.12.2)
|
51
|
+
json (~> 1.4)
|
52
|
+
rspec (2.10.0)
|
53
|
+
rspec-core (~> 2.10.0)
|
54
|
+
rspec-expectations (~> 2.10.0)
|
55
|
+
rspec-mocks (~> 2.10.0)
|
56
|
+
rspec-core (2.10.1)
|
57
|
+
rspec-expectations (2.10.0)
|
58
|
+
diff-lcs (~> 1.1.3)
|
59
|
+
rspec-mocks (2.10.1)
|
60
|
+
sequel (4.10.0)
|
61
|
+
simplecov (0.8.2)
|
62
|
+
docile (~> 1.1.0)
|
63
|
+
multi_json
|
64
|
+
simplecov-html (~> 0.8.0)
|
65
|
+
simplecov-html (0.8.0)
|
66
|
+
thread_safe (0.3.3)
|
67
|
+
|
68
|
+
PLATFORMS
|
69
|
+
ruby
|
70
|
+
|
71
|
+
DEPENDENCIES
|
72
|
+
bundler (~> 1.0)
|
73
|
+
jeweler (~> 2.0.1)
|
74
|
+
mysql
|
75
|
+
rake
|
76
|
+
rdoc (~> 3.12)
|
77
|
+
rspec (~> 2.10.0)
|
78
|
+
sequel
|
79
|
+
simplecov
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2014 Daniel Bornkessel
|
2
|
+
|
3
|
+
The MIT License (MIT)
|
4
|
+
|
5
|
+
Copyright © 2013 SoundCloud Ltd., Internal Tools Team
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
9
|
+
in the Software without restriction, including without limitation the rights
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
12
|
+
furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
15
|
+
copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
SOFTWARE.
|
24
|
+
|
25
|
+
|
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# CanDo [](https://travis-ci.org/soundcloud/cando)
|
2
|
+
|
3
|
+
CanDo is a small gem to implement a simple user access system based on users, roles &
|
4
|
+
capabilites, where:
|
5
|
+
|
6
|
+
- each user can have 0, 1 or many roles
|
7
|
+
- each role can have 0, 1 or many capabilites
|
8
|
+
|
9
|
+
Users have capabilities by getting roles assigned (role == collection of
|
10
|
+
capabilities). Within the code, the `can` helper method can be used to test
|
11
|
+
whether a user has a certain capability or not (see below for a working code example).
|
12
|
+
|
13
|
+
## Dependencies
|
14
|
+
|
15
|
+
CanDo depends on the following software:
|
16
|
+
|
17
|
+
* [sequel](http://sequel.jeremyevans.net)
|
18
|
+
* [rake](https://github.com/jimweirich/rake)
|
19
|
+
|
20
|
+
|
21
|
+
## Installation and deployment
|
22
|
+
|
23
|
+
Download and install rake with the following.
|
24
|
+
|
25
|
+
gem install cando
|
26
|
+
|
27
|
+
## Configuration and usage
|
28
|
+
|
29
|
+
### Database setup & configuration
|
30
|
+
If you want to use an individual database for cando, create the db + credentials (adjust values):
|
31
|
+
|
32
|
+
CREATE DATABASE IF NOT EXISTS cando DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
|
33
|
+
GRANT ALL ON `cando`.* to 'cando_user'@'localhost' identified by 'cando_passwd';
|
34
|
+
|
35
|
+
Whenever you want to use a CanDo rake task, you need to set the database config via the env var `$CANDO_DB`:
|
36
|
+
|
37
|
+
export CANDO_DB=mysql://cando_user@cando_passwd@localhost/cando
|
38
|
+
|
39
|
+
for other dbs, [see the sequel
|
40
|
+
documentation](http://sequel.jeremyevans.net/rdoc/classes/Sequel.html#method-c-connect).
|
41
|
+
**Note that you will have to require the gem for your respective dbms, i.e. the
|
42
|
+
`mysql`-gem for mysql, the `sqlite3`-gem for sqlite, etc. **
|
43
|
+
|
44
|
+
### Init cando
|
45
|
+
Cando provides a rake task to get you started. This will setup the necessary
|
46
|
+
tables (they are all prefixed with `cando` and thus should not interfere with
|
47
|
+
your database.
|
48
|
+
|
49
|
+
rake cando:init
|
50
|
+
|
51
|
+
### Using rake tasks
|
52
|
+
Cando provides several useful rake tasks for easy cli-based operations. In order
|
53
|
+
to use those edit (or create) the `Rakefile` and include
|
54
|
+
|
55
|
+
require 'cando'
|
56
|
+
|
57
|
+
To get an overview, execute:
|
58
|
+
|
59
|
+
$ rake -T cando
|
60
|
+
rake cando:init # Initialize cando (creates schema and runs migration)
|
61
|
+
|
62
|
+
rake cando:list # List roles
|
63
|
+
rake cando:add # Add a new role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,...
|
64
|
+
rake cando:update # Update role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,...
|
65
|
+
rake cando:remove # Remove role (pass in role name with role=<name>)
|
66
|
+
|
67
|
+
rake cando:assign # Assign role to user (args: roles=<r1>,<r2>,<rn> user=<user_urn>)
|
68
|
+
rake cando:users # List users and their roles
|
69
|
+
|
70
|
+
### Using CanDo in your project's code
|
71
|
+
Using the CanDo in your code (working code with an empty database):
|
72
|
+
|
73
|
+
require 'cando'
|
74
|
+
include CanDo
|
75
|
+
|
76
|
+
CanDo.init do
|
77
|
+
# if passed, this will be executed if the user does not have the
|
78
|
+
# asked-for capability (only applies if 'can' is passed a block)
|
79
|
+
cannot_block do |user_urn, capability|
|
80
|
+
raise "#{user_urn} can not #{capability}"
|
81
|
+
end
|
82
|
+
|
83
|
+
connect "mysql://cando_user:cando_passwd@host:port/database"
|
84
|
+
end
|
85
|
+
|
86
|
+
# if the role or a capability does not exist, it'll be created
|
87
|
+
define_role("r1", ["capability1", "capability3"])
|
88
|
+
define_role("r2", ["capability2"])
|
89
|
+
|
90
|
+
# if the user does not exist, he'll be created -- the roles must be available
|
91
|
+
assign_roles("user1", ["r1", "r2"])
|
92
|
+
assign_roles("user2", ["r1"])
|
93
|
+
|
94
|
+
# use 'can' block syntax
|
95
|
+
can("user1", :capability1) do
|
96
|
+
puts "user has capability1"
|
97
|
+
end
|
98
|
+
|
99
|
+
# this will raise an exception as declared in the init block
|
100
|
+
can("user1", :super_admin) do
|
101
|
+
puts "hey hoh" # this will not be printed
|
102
|
+
end
|
103
|
+
|
104
|
+
# when no block is given, 'can' returns true or false/nil
|
105
|
+
unless can("user2", :capability2)
|
106
|
+
puts "user does not have capability1"
|
107
|
+
end
|
108
|
+
|
109
|
+
## Versioning
|
110
|
+
CanDo adheres to Semantic Versioning 2.0.0. If there is a violation of
|
111
|
+
this scheme, report it as a bug.Specifically, if a patch or minor version is
|
112
|
+
released and breaks backward compatibility, that version should be immediately
|
113
|
+
yanked and/or a new version should be immediately released that restores
|
114
|
+
compatibility. Any change that breaks the public API will only be introduced at
|
115
|
+
a major-version release. As a result of this policy, you can (and should)
|
116
|
+
specify any dependency on <project name> by using the Pessimistic Version
|
117
|
+
Constraint with two digits of precision.
|
118
|
+
|
119
|
+
## Licensing
|
120
|
+
|
121
|
+
See the [LICENSE](LICENSE.md) file for details.
|
122
|
+
|
123
|
+
The MIT License (MIT)
|
124
|
+
|
125
|
+
Copyright © 2014 Daniel Bornkessel
|
126
|
+
|
127
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
128
|
+
of this software and associated documentation files (the "Software"), to deal
|
129
|
+
in the Software without restriction, including without limitation the rights
|
130
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
131
|
+
copies of the Software, and to permit persons to whom the Software is
|
132
|
+
furnished to do so, subject to the following conditions:
|
133
|
+
|
134
|
+
The above copyright notice and this permission notice shall be included in
|
135
|
+
all copies or substantial portions of the Software.
|
136
|
+
|
137
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
138
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
139
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
140
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
141
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
142
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
143
|
+
THE SOFTWARE.
|
144
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler'
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
require 'rake'
|
14
|
+
|
15
|
+
require 'jeweler'
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
18
|
+
gem.name = "cando"
|
19
|
+
gem.homepage = "http://github.com/kesselborn/cando"
|
20
|
+
gem.license = "MIT"
|
21
|
+
gem.summary = %Q{Simple roles helper}
|
22
|
+
gem.description = %Q{cando description}
|
23
|
+
gem.email = "daniel@soundcloud.com"
|
24
|
+
gem.authors = ["Daniel Bornkessel"]
|
25
|
+
# dependencies defined in Gemfile
|
26
|
+
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
|
+
|
29
|
+
require 'rspec/core'
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
32
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Code coverage detail"
|
36
|
+
task :simplecov do
|
37
|
+
ENV['COVERAGE'] = "true"
|
38
|
+
Rake::Task['spec'].execute
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :spec
|
42
|
+
|
43
|
+
require 'rdoc/task'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "cando #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
rdoc.rdoc_files.include('contrib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/cando.gemspec
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "cando"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Daniel Bornkessel"]
|
12
|
+
s.date = "2014-05-20"
|
13
|
+
s.description = "cando description"
|
14
|
+
s.email = "daniel@soundcloud.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
".travis.yml",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.md",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"cando.gemspec",
|
30
|
+
"contrib/initial_schema.rb",
|
31
|
+
"lib/cando.rb",
|
32
|
+
"lib/models/capability.rb",
|
33
|
+
"lib/models/role.rb",
|
34
|
+
"lib/models/user.rb",
|
35
|
+
"lib/tasks/cando.rake",
|
36
|
+
"spec/cando_spec.rb",
|
37
|
+
"spec/spec_helper.rb"
|
38
|
+
]
|
39
|
+
s.homepage = "http://github.com/kesselborn/cando"
|
40
|
+
s.licenses = ["MIT"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = "1.8.23"
|
43
|
+
s.summary = "Simple roles helper"
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
49
|
+
s.add_runtime_dependency(%q<sequel>, [">= 0"])
|
50
|
+
s.add_runtime_dependency(%q<rake>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.10.0"])
|
52
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
53
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
54
|
+
s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
|
55
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<sequel>, [">= 0"])
|
58
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
59
|
+
s.add_dependency(%q<rspec>, ["~> 2.10.0"])
|
60
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
61
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
62
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
63
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<sequel>, [">= 0"])
|
67
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
68
|
+
s.add_dependency(%q<rspec>, ["~> 2.10.0"])
|
69
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
70
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
71
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
72
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
create_table :cando_users do
|
4
|
+
String :id, :unique => true, :null => false
|
5
|
+
primary_key :id
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :cando_roles do
|
9
|
+
String :id, :unique => true, :null => false
|
10
|
+
primary_key :id
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :cando_capabilities do
|
14
|
+
String :id, :unique => true, :null => false
|
15
|
+
primary_key :id
|
16
|
+
end
|
17
|
+
|
18
|
+
# associations
|
19
|
+
create_table :cando_roles_users do
|
20
|
+
String :user_id
|
21
|
+
String :role_id
|
22
|
+
primary_key [:user_id, :role_id], :name => :ur_pk
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table :cando_capabilities_roles do
|
26
|
+
String :role_id
|
27
|
+
String :capability_id
|
28
|
+
primary_key [:role_id, :capability_id], :name =>:rc_pk
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
down do
|
33
|
+
drop_table :cando_users
|
34
|
+
drop_table :cando_roles
|
35
|
+
drop_table :cando_capabilities
|
36
|
+
drop_table :cando_roles_users
|
37
|
+
drop_table :cando_capabilities_roles
|
38
|
+
end
|
39
|
+
end
|
data/lib/cando.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
if File.basename($0) == "rake" # we are in a rake call: export our rake stuff
|
4
|
+
require 'rake'
|
5
|
+
import File.join(File.dirname(File.dirname(__FILE__)), "lib", "tasks", "cando.rake" )
|
6
|
+
end
|
7
|
+
|
8
|
+
module CanDo
|
9
|
+
class ConfigCannotBlockError < RuntimeError; end
|
10
|
+
class ConfigMysqlDBError < RuntimeError; end
|
11
|
+
class ConfigMysqlConnectionError < RuntimeError; end
|
12
|
+
class DBNotConnected < RuntimeError; end
|
13
|
+
|
14
|
+
def self.db
|
15
|
+
@db or raise DBNotConnected.new("CanDo is not connected to a database")
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.init(&block)
|
19
|
+
CanDo.instance_eval &block
|
20
|
+
|
21
|
+
begin
|
22
|
+
Sequel::Model.db.test_connection
|
23
|
+
rescue ::Sequel::Error => e
|
24
|
+
raise DBNotConnected.new("No database connection established. Have you called 'connect' within the 'CanDo.init' block? Sequel error message is:\n#{e.message}")
|
25
|
+
end
|
26
|
+
|
27
|
+
Dir.glob(File.expand_path("#{__FILE__}/../models/*.rb")).each do |model|
|
28
|
+
require_relative model
|
29
|
+
end
|
30
|
+
|
31
|
+
Sequel::Model.db
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# will be executed if `can(user_urn, :capability) { }` will not be executed
|
36
|
+
# due to missing capabilities
|
37
|
+
def self.cannot_block(&block)
|
38
|
+
if !block
|
39
|
+
raise ConfigCannotBlockError.new("CanDo#cannot_block expects block")
|
40
|
+
end
|
41
|
+
if block.arity != 2
|
42
|
+
raise ConfigCannotBlockError.new("CanDo#cannot_block expects block expecting two arguments |user_urn, capability|")
|
43
|
+
end
|
44
|
+
|
45
|
+
@@cannot_block_proc = block
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.connect(connection)
|
49
|
+
if connection =~ /sqlite/
|
50
|
+
raise ConfigMysqlDBError.new("sqlite is not supported as it misses certain constraints")
|
51
|
+
end
|
52
|
+
|
53
|
+
begin
|
54
|
+
@db = Sequel.connect(connection)
|
55
|
+
@db.test_connection
|
56
|
+
@db
|
57
|
+
rescue => e
|
58
|
+
raise ConfigMysqlConnectionError.new(<<-EOF
|
59
|
+
Error connecting to database. Be sure to pass in a databse config like 'mysql://user:passwd@host/database':
|
60
|
+
#{e.message}
|
61
|
+
EOF
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def can(user_urn, capability)
|
67
|
+
user = CanDo::User.find(:id => user_urn)
|
68
|
+
has_permission = user && user.can(capability)
|
69
|
+
if block_given?
|
70
|
+
if has_permission
|
71
|
+
return yield
|
72
|
+
end
|
73
|
+
if @@cannot_block_proc
|
74
|
+
@@cannot_block_proc.call(user_urn, capability)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
has_permission
|
79
|
+
end
|
80
|
+
|
81
|
+
def define_role(role, capabilities)
|
82
|
+
CanDo::Role.define_role(role, capabilities)
|
83
|
+
end
|
84
|
+
|
85
|
+
def assign_roles(user, roles)
|
86
|
+
CanDo::User.find_or_create(:id => user).assign_roles(roles)
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module CanDo
|
2
|
+
class Capability < Sequel::Model(:cando_capabilities)
|
3
|
+
many_to_many :roles, :join_table => :cando_capabilities_roles
|
4
|
+
unrestrict_primary_key
|
5
|
+
|
6
|
+
def self.cleanup
|
7
|
+
Capability.all do |cap|
|
8
|
+
if cap.roles.count == 0
|
9
|
+
cap.destroy
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/models/role.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module CanDo
|
2
|
+
class Role < Sequel::Model(:cando_roles)
|
3
|
+
many_to_many :users, :join_table => :cando_roles_users
|
4
|
+
many_to_many :capabilities, :join_table => :cando_capabilities_roles
|
5
|
+
unrestrict_primary_key
|
6
|
+
|
7
|
+
def before_destroy
|
8
|
+
self.remove_all_capabilities
|
9
|
+
self.remove_all_users
|
10
|
+
Capability.cleanup
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.define_role(name, capabilities)
|
14
|
+
role = Role.find_or_create(:id => name)
|
15
|
+
role.remove_all_capabilities
|
16
|
+
capabilities.each do |capability|
|
17
|
+
role.add_capability( Capability.find_or_create(:id => capability.to_s) )
|
18
|
+
end
|
19
|
+
|
20
|
+
role
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"#{id}\t#{capabilities.map(&:id).join(",")}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/models/user.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module CanDo
|
2
|
+
class UndefinedRole < Exception; end
|
3
|
+
class User < Sequel::Model(:cando_users)
|
4
|
+
many_to_many :roles, :join_table => :cando_roles_users
|
5
|
+
unrestrict_primary_key
|
6
|
+
|
7
|
+
def capabilities
|
8
|
+
roles.inject([]){|a,role| a << role.capabilities}.flatten.uniq.map(&:id)
|
9
|
+
end
|
10
|
+
|
11
|
+
def can(capability)
|
12
|
+
capabilities.include?(capability.to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
def assign_roles(roles)
|
16
|
+
self.class.db.transaction do
|
17
|
+
self.remove_all_roles
|
18
|
+
roles.each do |r|
|
19
|
+
begin
|
20
|
+
role = r.is_a?(CanDo::Role) ? r : CanDo::Role.where(:id => r).first!
|
21
|
+
self.add_role(role)
|
22
|
+
rescue Sequel::UniqueConstraintViolation => e
|
23
|
+
puts "user already has role '#{r}'"
|
24
|
+
rescue Sequel::NoMatchingRow
|
25
|
+
raise UndefinedRole.new("Role '#{r}' does not exist")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def role_names
|
33
|
+
roles.map(&:id)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"#{id}\t#{role_names.join(",")}\t#{capabilities.join(",")}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
CANDO_MIGRATION_DIR = "db/cando-migrations"
|
4
|
+
CANDO_SCHEMA = File.join(File.dirname(File.dirname(File.dirname(__FILE__))), "contrib", "initial_schema.rb")
|
5
|
+
|
6
|
+
namespace :cando do
|
7
|
+
desc "Initialize cando (creates schema and runs migration)"
|
8
|
+
task :init do
|
9
|
+
if Dir.glob("#{CANDO_MIGRATION_DIR}/*#{File.basename(CANDO_SCHEMA)}*").empty?
|
10
|
+
puts green("Copying cando schema to local cando migrations folder '#{CANDO_MIGRATION_DIR}'")
|
11
|
+
cando_schema_migration = File.join(CANDO_MIGRATION_DIR, "#{Time.now.strftime("%Y%m%d%H%M%S")}_#{File.basename(CANDO_SCHEMA)}")
|
12
|
+
|
13
|
+
FileUtils.mkdir_p(CANDO_MIGRATION_DIR) unless File.exists?(CANDO_MIGRATION_DIR)
|
14
|
+
FileUtils.cp(CANDO_SCHEMA, cando_schema_migration)
|
15
|
+
else
|
16
|
+
$stderr.puts red("skipping copying cando schema migration file: already exists")
|
17
|
+
end
|
18
|
+
|
19
|
+
Sequel::Migrator.run(connect_to_db, CANDO_MIGRATION_DIR, { allow_missing_migration_files: true} )
|
20
|
+
|
21
|
+
puts <<EOF
|
22
|
+
#{green("Success!")}
|
23
|
+
|
24
|
+
In order to add, update or remove a role, call
|
25
|
+
|
26
|
+
rake cando:add role=<rolename> capabilities=<cap1>,<cap2>,<cap3>
|
27
|
+
rake cando:update role=<rolename> capabilities=<cap1>,<cap2>,<cap3>
|
28
|
+
rake cando:remove role=<rolename>
|
29
|
+
|
30
|
+
When adding or updating a roles it doesn't matter whether the passed in capabilities
|
31
|
+
exist or not -- if not existant, they will be created automatically
|
32
|
+
|
33
|
+
For more cando rake tasks execute
|
34
|
+
|
35
|
+
rake -T cando
|
36
|
+
|
37
|
+
EOF
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Add a new role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,... )"
|
42
|
+
task :add do
|
43
|
+
add_role(false)
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Update role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,... )"
|
47
|
+
task :update do
|
48
|
+
add_role(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
desc "Remove role (pass in role name with role=<name>)"
|
53
|
+
task :remove do
|
54
|
+
unless ENV['role']
|
55
|
+
$stderr.puts red("usage: rake cando:remove role=<rolename>")
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
|
59
|
+
connect_to_db
|
60
|
+
unless r = CanDo::Role.find(:id => ENV['role'])
|
61
|
+
$stderr.puts red("role '#{args.role}' does not exist")
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
|
65
|
+
r.destroy
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Assign role to user (args: roles=<r1>,<r2>,<rn> user=<user_urn>)"
|
69
|
+
task :assign do
|
70
|
+
roles = ENV['roles']
|
71
|
+
user_urn = ENV['user']
|
72
|
+
|
73
|
+
unless roles && user_urn
|
74
|
+
$stderr.puts red("usage: rake cando:assign user=<user_urn> roles=<role1>,<role2>,... ")
|
75
|
+
end
|
76
|
+
|
77
|
+
connect_to_db
|
78
|
+
include CanDo
|
79
|
+
assign_roles(user_urn, roles.split(","))
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "List roles"
|
83
|
+
task :list do
|
84
|
+
connect_to_db
|
85
|
+
puts "ROLE\tCAPABILITIES"
|
86
|
+
CanDo::Role.all.each do |role|
|
87
|
+
puts role
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "List users and their roles"
|
92
|
+
task :users do
|
93
|
+
connect_to_db
|
94
|
+
puts "USER_URN\tROLES\tCAPABILITIES"
|
95
|
+
CanDo::User.all.each do |user|
|
96
|
+
puts user
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
def green(text)
|
103
|
+
"\033[1;32;48m#{text}\033[m"
|
104
|
+
end
|
105
|
+
|
106
|
+
def red(text)
|
107
|
+
"\033[1;31;48m#{text}\033[m"
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_role(force = false)
|
111
|
+
role = ENV['role']
|
112
|
+
capabilities = ENV['capabilities'] && ENV['capabilities'].split(",")
|
113
|
+
|
114
|
+
unless role && capabilities
|
115
|
+
puts red("usage: rake cando:add role=<rolename> capabilities=<cap1>,<cap2>,<cap3>")
|
116
|
+
exit 1
|
117
|
+
end
|
118
|
+
|
119
|
+
connect_to_db
|
120
|
+
if !force && CanDo::Role.find(:id => role)
|
121
|
+
puts red("Role '#{role}' already exists!")
|
122
|
+
puts "If you want to update '#{role}', please use 'rake cando:update'"
|
123
|
+
exit 1
|
124
|
+
end
|
125
|
+
|
126
|
+
include CanDo
|
127
|
+
define_role(role, capabilities)
|
128
|
+
end
|
129
|
+
|
130
|
+
def connect_to_db
|
131
|
+
unless ENV['CANDO_DB']
|
132
|
+
raise "Please pass in database config, e.g. CANDO_DB=mysql://user:passwd@host/database bundle exec rake cando:<command>"
|
133
|
+
end
|
134
|
+
|
135
|
+
CanDo.init do
|
136
|
+
connect(ENV['CANDO_DB'])
|
137
|
+
end
|
138
|
+
end
|
data/spec/cando_spec.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
#describe "Cando" do
|
4
|
+
# it "fails" do
|
5
|
+
# fail "hey buddy, you should probably rename this file and start specing for real"
|
6
|
+
# end
|
7
|
+
#end
|
8
|
+
#
|
9
|
+
describe "CanDo module methods" do
|
10
|
+
context "CanDo.cannot_block expects block accepting two parameters" do
|
11
|
+
it { expect{ CanDo.cannot_block }.to raise_error(CanDo::ConfigCannotBlockError) }
|
12
|
+
it { expect{ CanDo.cannot_block{|x| x} }.to raise_error(CanDo::ConfigCannotBlockError) }
|
13
|
+
it { expect{ CanDo.cannot_block{|x,y,z| x} }.to raise_error(CanDo::ConfigCannotBlockError) }
|
14
|
+
|
15
|
+
it { expect{ CanDo.cannot_block{|x,y| x} }.to_not raise_error(CanDo::ConfigCannotBlockError) }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "CanDo.connect" do
|
19
|
+
it { expect{ CanDo.connect(nil) }.to raise_error(CanDo::ConfigMysqlConnectionError) }
|
20
|
+
it { expect{ CanDo.connect("sqlite::memory:") }.to raise_error(CanDo::ConfigMysqlDBError) }
|
21
|
+
it { CanDo.connect(ENV['CANDO_TEST_DB']).test_connection be_true }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "CanDo.init" do
|
25
|
+
context "CanDo.cannot_block called from config" do
|
26
|
+
it { expect{ CanDo.init{ cannot_block }}.to raise_error(CanDo::ConfigCannotBlockError) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "CanDo exported methods" do
|
31
|
+
include CanDo
|
32
|
+
|
33
|
+
context "CanDo#define_role" do
|
34
|
+
context "empty role (no capabilities)" do
|
35
|
+
it { expect{ define_role("role", []) }.to change{CanDo::Role.count}.from(0).to(1) }
|
36
|
+
it { expect{ define_role("role", []) }.to change{CanDo::Capability.count}.by(0) }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "standard situation" do
|
40
|
+
it { expect{ define_role("role", [:capability1, :capability2]) }.to change{CanDo::Role.count}.from(0).to(1) }
|
41
|
+
it { expect{ define_role("role", [:capability1, :capability2]) }.to change{CanDo::Capability.count}.from(0).to(2) }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "duplication" do
|
45
|
+
before(:each) { define_role("role", [:capability]) }
|
46
|
+
|
47
|
+
it { expect{ define_role("role", [:new_capability]) }.to change{CanDo::Role.count}.by(0) }
|
48
|
+
it { expect{ define_role("new_role", [:capability]) }.to change{CanDo::Capability.count}.by(0) }
|
49
|
+
end
|
50
|
+
|
51
|
+
context "cleanup" do
|
52
|
+
before(:each) do
|
53
|
+
@role = define_role("role", [:capabilty])
|
54
|
+
end
|
55
|
+
|
56
|
+
it { expect{ @role.destroy }.to change{CanDo::Role.count}.from(1).to(0) }
|
57
|
+
it { expect{ @role.destroy }.to change{CanDo::Capability.count}.from(1).to(0) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "CanDo#assign_roles" do
|
62
|
+
include CanDo
|
63
|
+
|
64
|
+
context "user without roles" do
|
65
|
+
it { expect{ assign_roles("user", []) }.to change{CanDo::User.count}.from(0).to(1) }
|
66
|
+
it { expect{ assign_roles("user", []) }.to change{CanDo::Role.count}.by(0) }
|
67
|
+
end
|
68
|
+
|
69
|
+
context "invalid roles" do
|
70
|
+
it { expect{ assign_roles("user", ["non-existant-role"]) }.to raise_error(CanDo::UndefinedRole) }
|
71
|
+
it { expect{ assign_roles("user", [nil]) }.to raise_error(CanDo::UndefinedRole) }
|
72
|
+
end
|
73
|
+
|
74
|
+
context "standard situation" do
|
75
|
+
before(:each) do
|
76
|
+
@r1 = define_role("r1", [:c1, :c2])
|
77
|
+
@r2 = define_role("r2", [:c2, :c3])
|
78
|
+
end
|
79
|
+
|
80
|
+
it { expect{ assign_roles("user", ["r1"]) }.to change{CanDo::User.count}.from(0).to(1) }
|
81
|
+
it { expect{ assign_roles("user", ["r1","r2"]) }.to change{CanDo::User.count}.from(0).to(1) }
|
82
|
+
|
83
|
+
it { expect{ assign_roles("user", [@r1]) }.to change{CanDo::User.count}.from(0).to(1) }
|
84
|
+
it { expect{ assign_roles("user", ["r1",@r2]) }.to change{CanDo::User.count}.from(0).to(1) }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "cleanup" do
|
88
|
+
before(:each) do
|
89
|
+
@role = define_role("role", [:capabilty])
|
90
|
+
@user = assign_roles("user", [@role])
|
91
|
+
end
|
92
|
+
|
93
|
+
it { expect{ @user.destroy }.to change{CanDo::User.count}.from(1).to(0) }
|
94
|
+
it { expect{ @user.destroy }.to change{CanDo::Role.count}.by(0) }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "CanDo#can" do
|
99
|
+
include CanDo
|
100
|
+
|
101
|
+
before(:each) do
|
102
|
+
@role = define_role("role", [:capability1, :capabilty2])
|
103
|
+
@user = assign_roles("user", [@role])
|
104
|
+
end
|
105
|
+
|
106
|
+
context "CanDo#can" do
|
107
|
+
it { can("user", :capability1).should be_true }
|
108
|
+
it { can("user", :undefined_capability).should be_false }
|
109
|
+
|
110
|
+
it { expect{ |can| can("user", :capability1, &can) }.to yield_control }
|
111
|
+
|
112
|
+
context "CanDo.cannot_block" do
|
113
|
+
class DummyException < RuntimeError; end
|
114
|
+
before(:all) do
|
115
|
+
CanDo.cannot_block do |user, capabilty|
|
116
|
+
raise DummyException
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it { expect{ can("user", :undefined_capability){} }.to raise_error(DummyException) }
|
121
|
+
it { expect{ can("user", :undefined_capability)}.to_not raise_error(DummyException) }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'cando'
|
3
|
+
|
4
|
+
module SimpleCov::Configuration
|
5
|
+
def clean_filters
|
6
|
+
@filters = []
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
SimpleCov.configure do
|
11
|
+
clean_filters
|
12
|
+
load_profile 'test_frameworks'
|
13
|
+
end
|
14
|
+
|
15
|
+
ENV["COVERAGE"] && SimpleCov.start do
|
16
|
+
add_filter "/.rvm/"
|
17
|
+
end
|
18
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
19
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
20
|
+
|
21
|
+
require 'rspec'
|
22
|
+
require 'cando'
|
23
|
+
|
24
|
+
# Requires supporting files with custom matchers and macros, etc,
|
25
|
+
# in ./support/ and its subdirectories.
|
26
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
27
|
+
|
28
|
+
ENV['CANDO_TEST_DB'] or raise "Please set CANDO_TEST_DB to a valid configuration similar to 'mysql://user:passwd@localhost/database'"
|
29
|
+
|
30
|
+
RSpec.configure do |config|
|
31
|
+
Sequel.extension :migration
|
32
|
+
migration = eval(File.read(File.join(File.dirname(File.dirname(__FILE__)), "contrib", "initial_schema.rb")))
|
33
|
+
db = nil
|
34
|
+
|
35
|
+
config.before(:suite) do
|
36
|
+
db = CanDo.init do
|
37
|
+
connect ENV['CANDO_TEST_DB']
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
config.before(:each) do
|
42
|
+
db.drop_table(*db.tables)
|
43
|
+
migration.apply(db, :up)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def connect
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cando
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Daniel Bornkessel
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-05-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sequel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.10.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.10.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rdoc
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.12'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.12'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: jeweler
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.0.1
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.0.1
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: simplecov
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: cando description
|
127
|
+
email: daniel@soundcloud.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files:
|
131
|
+
- LICENSE.txt
|
132
|
+
- README.md
|
133
|
+
files:
|
134
|
+
- .document
|
135
|
+
- .rspec
|
136
|
+
- .travis.yml
|
137
|
+
- Gemfile
|
138
|
+
- Gemfile.lock
|
139
|
+
- LICENSE.txt
|
140
|
+
- README.md
|
141
|
+
- Rakefile
|
142
|
+
- VERSION
|
143
|
+
- cando.gemspec
|
144
|
+
- contrib/initial_schema.rb
|
145
|
+
- lib/cando.rb
|
146
|
+
- lib/models/capability.rb
|
147
|
+
- lib/models/role.rb
|
148
|
+
- lib/models/user.rb
|
149
|
+
- lib/tasks/cando.rake
|
150
|
+
- spec/cando_spec.rb
|
151
|
+
- spec/spec_helper.rb
|
152
|
+
homepage: http://github.com/kesselborn/cando
|
153
|
+
licenses:
|
154
|
+
- MIT
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
161
|
+
requirements:
|
162
|
+
- - ! '>='
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
segments:
|
166
|
+
- 0
|
167
|
+
hash: -3220119119017063207
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 1.8.23
|
177
|
+
signing_key:
|
178
|
+
specification_version: 3
|
179
|
+
summary: Simple roles helper
|
180
|
+
test_files: []
|