cancannible 0.0.2 → 2.0.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.
- checksums.yaml +5 -13
- data/.github/workflows/ruby.yml +34 -0
- data/Gemfile +0 -4
- data/Guardfile +2 -12
- data/README.md +28 -5
- data/cancannible.gemspec +12 -9
- data/lib/cancannible/config.rb +1 -3
- data/lib/cancannible/grantee.rb +21 -22
- data/lib/cancannible/preload_adapter.rb +2 -5
- data/lib/cancannible/preloader.rb +9 -15
- data/lib/cancannible/version.rb +1 -1
- data/lib/cancannible.rb +5 -5
- data/lib/generators/cancannible/install_generator.rb +0 -1
- data/lib/generators/cancannible/templates/cancannible_initializer.rb +1 -2
- data/lib/generators/cancannible/templates/migration.rb +1 -1
- data/lib/generators/cancannible/templates/permission.rb +7 -3
- data/spec/spec_helper.rb +2 -2
- data/spec/support/migrations_helper.rb +1 -5
- data/spec/support/models.rb +3 -6
- data/spec/unit/cached_abilities_spec.rb +32 -18
- data/spec/unit/config_spec.rb +1 -1
- data/spec/unit/custom_refinements_spec.rb +0 -4
- data/spec/unit/grantee_spec.rb +13 -38
- data/spec/unit/inherited_permissions_spec.rb +2 -11
- metadata +75 -49
- data/.rspec +0 -2
- data/.travis.yml +0 -4
checksums.yaml
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
|
|
5
|
-
data.tar.gz: !binary |-
|
|
6
|
-
YjQ0NDZhNzQ5MzdhMDJiMDQ4NzRjMjI1MDE5ZGY1N2ViZjU2NThlMA==
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8ba63fd7b6b80a32a3bc93fbbe43845439c1eaffa9661e35510dbeff487af673
|
|
4
|
+
data.tar.gz: fdf8f225705b0e0fa9ea469674d62ce22b5340f48cb23fb94a2b8501112417cf
|
|
7
5
|
SHA512:
|
|
8
|
-
metadata.gz:
|
|
9
|
-
|
|
10
|
-
ZDNiNDM3ODVmZTExYzM5NDQ1ZmQyNDZmZjc0NDFiYWIxZTM2YjcwMTdiMGU0
|
|
11
|
-
YTliZGI0YjFhNDdjY2RmZWNhZjNhY2YyNDU5YTQ3MDQ3NDRhNjY=
|
|
12
|
-
data.tar.gz: !binary |-
|
|
13
|
-
MjFhMzBmNzQ5YTcyMzBmOTViMTYwMjEwNDI2YjllNjUxZjEzYjkxNmZjOTVi
|
|
14
|
-
YmVkOWZjMWUyODBiMDQ3MmY4YWExMDJiNmUzZTkzZTBlY2MwZWFjMzFmOWM0
|
|
15
|
-
MjNiMTBmMGFjNGI3OTllNTkxMjM3NTBmZDViOWYwNmVlZGMwZWE=
|
|
6
|
+
metadata.gz: f9068c6dce19852c747512ddafa0a29461586128d67408bc5c53f07e029e8273d0cb343acb4400dcfd180afd6596202837fac699838f62d6e252a56dd35f99df
|
|
7
|
+
data.tar.gz: e5ec5d5accb0e40139dbeee9b6bc211d3652688c7ffb17c1788c86cea455c626914b233d4eb52de2786993989df8018d38ddfb07bef5bedb2f04484d43f62054
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
+
|
|
8
|
+
name: Ruby
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [ '*' ]
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [ main ]
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
matrix:
|
|
22
|
+
ruby-version: ['2.7', '3.0']
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v2
|
|
26
|
+
- name: Set up Ruby
|
|
27
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
|
28
|
+
with:
|
|
29
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
30
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: bundle install
|
|
33
|
+
- name: Run tests
|
|
34
|
+
run: bundle exec rake
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
# A sample Guardfile
|
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
# rspec may be run, below are examples of the most common uses.
|
|
6
|
-
# * bundler: 'bundle exec rspec'
|
|
7
|
-
# * bundler binstubs: 'bin/rspec'
|
|
8
|
-
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
|
9
|
-
# installed the spring binstubs per the docs)
|
|
10
|
-
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
|
11
|
-
# * 'just' rspec: 'rspec'
|
|
12
|
-
guard :rspec, cmd: 'bundle exec rspec' do
|
|
4
|
+
guard :rspec, cmd: 'bundle exec rspec', all_on_start: false, all_after_pass: false do
|
|
13
5
|
watch(%r{^spec/.+_spec\.rb$})
|
|
14
|
-
watch(%r{^lib/(.+)\.rb$})
|
|
6
|
+
watch(%r{^lib/cancannible/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
|
15
7
|
watch('spec/spec_helper.rb') { "spec" }
|
|
16
|
-
|
|
17
8
|
end
|
|
18
|
-
|
data/README.md
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
# Cancannible
|
|
2
|
-
[](https://travis-ci.org/evendis/cancannible)
|
|
3
2
|
|
|
4
|
-
Cancannible is a gem that extends
|
|
3
|
+
Cancannible is a gem that extends [CanCanCan](https://github.com/CanCanCommunity/cancancan) with a range of capabilities:
|
|
4
|
+
|
|
5
5
|
* database-persisted permissions
|
|
6
6
|
* export CanCan methods to the model layer (so that permissions can be applied in model methods, and easily set in a test case)
|
|
7
7
|
* permissions inheritance (so that, for example, a User can inherit permissions from Roles and/or Groups)
|
|
8
8
|
* caching of abilities (so that they don't need to be recalculated on each web request)
|
|
9
9
|
* general-purpose access refinements (so that, for example, CanCan will automatically enforce multi-tenant or other security restrictions)
|
|
10
|
+
* battle-tested with Rails 3.2.x and 4.2.x
|
|
11
|
+
|
|
12
|
+
Two demo applications are available (with source) that show cancannible in action:
|
|
13
|
+
|
|
14
|
+
* [cancannibledemo.evendis.com](http://cancannibledemo.evendis.com) uses Rails 3.2.x
|
|
15
|
+
* [cancannibledemo4.evendis.com](http://cancannibledemo4.evendis.com) uses Rails 4.2.x
|
|
10
16
|
|
|
11
17
|
## Limitations
|
|
12
|
-
|
|
18
|
+
|
|
19
|
+
Cancannible's origin was in a web application that's been in production for over 4 years.
|
|
13
20
|
This gem is an initial refactoring as a separate component. It continues to be used in production, but
|
|
14
21
|
there are some limitations and constraints that will ideally be removed or changed over time:
|
|
15
22
|
|
|
16
23
|
* It only supports ActiveRecord for permissions storage (specifically, it has been tested with PostgreSQL and SQLite)
|
|
17
24
|
* It currently assumes permissions are stored in a Permission model with a specific structure
|
|
18
|
-
* It works with the [
|
|
19
|
-
* It assumes and is only tested with Rails 3.2. Not yet with Rails 4.
|
|
25
|
+
* It works with the [CanCanCan](https://github.com/CanCanCommunity/cancancan) gem.
|
|
20
26
|
* It assumes your CanCan rules are setup with the default `Ability` class
|
|
21
27
|
|
|
22
28
|
|
|
@@ -120,6 +126,23 @@ For example, this is a simple scheme using Redis:
|
|
|
120
126
|
end
|
|
121
127
|
|
|
122
128
|
|
|
129
|
+
## Testing the gem
|
|
130
|
+
|
|
131
|
+
The RSpec test suite runs as the default rake task:
|
|
132
|
+
|
|
133
|
+
rake
|
|
134
|
+
# same as:
|
|
135
|
+
rake spec
|
|
136
|
+
|
|
137
|
+
For convenience, guard is included in the development gem environment, so you can start automatic testing-on-change:
|
|
138
|
+
|
|
139
|
+
bundle exec guard
|
|
140
|
+
|
|
141
|
+
[Appraisal](https://github.com/thoughtbot/appraisal) is also included to run tests across Rails 3 and 4 environments:
|
|
142
|
+
|
|
143
|
+
appraisal rake spec
|
|
144
|
+
|
|
145
|
+
|
|
123
146
|
## Contributing
|
|
124
147
|
|
|
125
148
|
1. Fork it ( https://github.com/evendis/cancannible/fork )
|
data/cancannible.gemspec
CHANGED
|
@@ -18,14 +18,17 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
spec.add_runtime_dependency "activesupport", "~>
|
|
22
|
-
spec.add_runtime_dependency "activemodel", "~>
|
|
23
|
-
spec.add_runtime_dependency "
|
|
21
|
+
spec.add_runtime_dependency "activesupport", "~> 6.1"
|
|
22
|
+
spec.add_runtime_dependency "activemodel", "~> 6.1"
|
|
23
|
+
spec.add_runtime_dependency "cancancan"
|
|
24
24
|
|
|
25
|
-
spec.add_development_dependency "activerecord", "~>
|
|
26
|
-
spec.add_development_dependency "sqlite3", "
|
|
27
|
-
|
|
28
|
-
spec.add_development_dependency "
|
|
29
|
-
spec.add_development_dependency "
|
|
30
|
-
spec.add_development_dependency "
|
|
25
|
+
spec.add_development_dependency "activerecord", "~> 6.1"
|
|
26
|
+
spec.add_development_dependency "sqlite3", ">= 1.3.2"
|
|
27
|
+
|
|
28
|
+
spec.add_development_dependency "bundler"
|
|
29
|
+
spec.add_development_dependency "rake"
|
|
30
|
+
spec.add_development_dependency "rdoc"
|
|
31
|
+
spec.add_development_dependency "rspec"
|
|
32
|
+
spec.add_development_dependency "guard-rspec"
|
|
33
|
+
spec.add_development_dependency "rb-fsevent"
|
|
31
34
|
end
|
data/lib/cancannible/config.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
module Cancannible
|
|
2
|
-
|
|
3
2
|
mattr_accessor :refinements
|
|
4
3
|
mattr_accessor :get_cached_abilities
|
|
5
4
|
mattr_accessor :store_cached_abilities
|
|
@@ -24,5 +23,4 @@ module Cancannible
|
|
|
24
23
|
self.refinements[stage] ||= []
|
|
25
24
|
self.refinements[stage] << refinement
|
|
26
25
|
end
|
|
27
|
-
|
|
28
|
-
end
|
|
26
|
+
end
|
data/lib/cancannible/grantee.rb
CHANGED
|
@@ -23,34 +23,25 @@ module Cancannible::Grantee
|
|
|
23
23
|
resource_id = resource.try(:id)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
# This looks ugly, but it avoid version-specific issues with find_by*/find_or_initialize_by* methods
|
|
27
|
+
permission = where(asserted: asserted, ability: ability, resource_id: resource_id, resource_type: resource_type).first
|
|
28
|
+
permission ||= where(asserted: !asserted, ability: ability, resource_id: resource_id, resource_type: resource_type).first
|
|
29
|
+
permission ||= new(asserted: asserted, ability: ability, resource_id: resource_id, resource_type: resource_type)
|
|
30
|
+
permission.asserted = asserted
|
|
31
|
+
permission.save!
|
|
32
|
+
|
|
33
|
+
proxy_association.owner.instance_variable_set :@abilities, nil # invalidate the owner's ability collection
|
|
34
34
|
|
|
35
|
-
# if Rails.version =~ /3\.0/ # the rails 3.0 way
|
|
36
|
-
# proxy_owner.instance_variable_set :@permissions, nil # invalidate the owner's permissions collection
|
|
37
|
-
# proxy_owner.instance_variable_set :@abilities, nil # invalidate the owner's ability collection
|
|
38
|
-
# else
|
|
39
|
-
proxy_association.owner.instance_variable_set :@abilities, nil # invalidate the owner's ability collection
|
|
40
|
-
# end
|
|
41
35
|
permission
|
|
42
36
|
end
|
|
43
37
|
end
|
|
44
|
-
|
|
45
38
|
end
|
|
46
39
|
|
|
47
40
|
module ClassMethods
|
|
48
|
-
|
|
49
41
|
# Command: configures the set of associations (array of symbols) from which permissions should be inherited
|
|
50
42
|
def inherit_permissions_from(*relations)
|
|
51
43
|
self.inheritable_permissions = relations
|
|
52
44
|
end
|
|
53
|
-
|
|
54
45
|
end
|
|
55
46
|
|
|
56
47
|
# Returns the Ability set for the owner.
|
|
@@ -59,7 +50,13 @@ module Cancannible::Grantee
|
|
|
59
50
|
@abilities = if refresh
|
|
60
51
|
nil
|
|
61
52
|
elsif Cancannible.get_cached_abilities.respond_to?(:call)
|
|
62
|
-
Cancannible.get_cached_abilities.call(self)
|
|
53
|
+
result = Cancannible.get_cached_abilities.call(self)
|
|
54
|
+
if result
|
|
55
|
+
# performs a crude compatibility check
|
|
56
|
+
rules_size = result.send(:rules).size rescue nil
|
|
57
|
+
rules_index_size = (result.instance_variable_get(:@rules_index) || []).size
|
|
58
|
+
result if !rules_size.nil? && rules_index_size == rules_size
|
|
59
|
+
end
|
|
63
60
|
end
|
|
64
61
|
return @abilities if @abilities
|
|
65
62
|
|
|
@@ -70,7 +67,7 @@ module Cancannible::Grantee
|
|
|
70
67
|
ability_class.new(self)
|
|
71
68
|
end
|
|
72
69
|
|
|
73
|
-
Cancannible.store_cached_abilities.call(self
|
|
70
|
+
Cancannible.store_cached_abilities.call(self, @abilities) if Cancannible.store_cached_abilities.respond_to?(:call)
|
|
74
71
|
@abilities
|
|
75
72
|
end
|
|
76
73
|
|
|
@@ -104,16 +101,18 @@ module Cancannible::Grantee
|
|
|
104
101
|
def cannot(ability, resource)
|
|
105
102
|
permissions << [ability, resource, false]
|
|
106
103
|
end
|
|
107
|
-
|
|
108
104
|
end
|
|
109
105
|
|
|
110
|
-
|
|
111
106
|
module Cancannible
|
|
112
107
|
# This module is automatically included into all controllers.
|
|
113
108
|
# It overrides some CanCan ControllerAdditions
|
|
114
109
|
module ControllerAdditions
|
|
110
|
+
# Returns abilities cached in the current_user model.
|
|
111
|
+
# If that fails, returns a default Ability instance
|
|
115
112
|
def current_ability
|
|
116
|
-
current_user.try(:abilities)
|
|
113
|
+
current_user.try(:abilities) || if ability_class = ('::Ability'.constantize rescue nil)
|
|
114
|
+
ability_class.new(current_user)
|
|
115
|
+
end
|
|
117
116
|
end
|
|
118
117
|
end
|
|
119
118
|
end
|
|
@@ -2,14 +2,11 @@ module Cancannible::PreloadAdapter
|
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
4
|
included do
|
|
5
|
-
|
|
6
5
|
# Tap Ability.new to first preload permissions via Cancannible
|
|
7
6
|
alias_method :cancan_initialize, :initialize
|
|
8
7
|
def initialize(user)
|
|
9
|
-
Cancannible::Preloader.preload_abilities!(user,self)
|
|
8
|
+
Cancannible::Preloader.preload_abilities!(user, self)
|
|
10
9
|
cancan_initialize(user)
|
|
11
10
|
end
|
|
12
|
-
|
|
13
11
|
end
|
|
14
|
-
|
|
15
|
-
end
|
|
12
|
+
end
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
class Cancannible::Preloader
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
new(grantee,cancan_ability_object).preload!
|
|
2
|
+
def self.preload_abilities!(grantee, cancan_ability_object)
|
|
3
|
+
new(grantee, cancan_ability_object).preload!
|
|
5
4
|
end
|
|
6
5
|
|
|
7
6
|
attr_accessor :grantee
|
|
8
7
|
attr_accessor :cancan_ability_object
|
|
9
8
|
|
|
10
|
-
def initialize(grantee,cancan_ability_object)
|
|
9
|
+
def initialize(grantee, cancan_ability_object)
|
|
11
10
|
self.grantee = grantee
|
|
12
11
|
self.cancan_ability_object = cancan_ability_object
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def preload!
|
|
16
15
|
return unless grantee.respond_to?(:inherited_permissions)
|
|
16
|
+
|
|
17
17
|
# load inherited permissions to CanCan Abilities
|
|
18
18
|
preload_abilities_from_permissions(grantee.inherited_permissions)
|
|
19
19
|
# load user-based permissions from database to CanCan Abilities
|
|
@@ -39,17 +39,15 @@ class Cancannible::Preloader
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
if permission.resource_id.nil?
|
|
42
|
-
|
|
43
42
|
if action == :cannot
|
|
44
43
|
# apply generic unrestricted permission to the class
|
|
45
|
-
cancan_ability_object.send(
|
|
44
|
+
cancan_ability_object.send(action, ability, resource_type)
|
|
46
45
|
else
|
|
47
|
-
|
|
48
46
|
refinements = resolve_resource_refinements(ability,model_resource)
|
|
49
47
|
|
|
50
48
|
if refinements.empty?
|
|
51
49
|
# apply generic unrestricted permission to the class
|
|
52
|
-
cancan_ability_object.send(
|
|
50
|
+
cancan_ability_object.send(action, ability, resource_type)
|
|
53
51
|
else
|
|
54
52
|
secondary_refinements = resolve_resource_refinements(ability,model_resource,2).presence || [{}]
|
|
55
53
|
refinements.each do |refinement|
|
|
@@ -58,25 +56,22 @@ class Cancannible::Preloader
|
|
|
58
56
|
end
|
|
59
57
|
end
|
|
60
58
|
end
|
|
61
|
-
|
|
62
59
|
end
|
|
63
|
-
|
|
64
60
|
elsif resource_type.find_by_id(permission.resource_id)
|
|
65
61
|
cancan_ability_object.send( action, ability, resource_type, id: permission.resource_id)
|
|
66
62
|
end
|
|
67
|
-
|
|
68
63
|
end
|
|
69
64
|
end
|
|
70
65
|
|
|
71
66
|
def resolve_resource_type(given_resource_type)
|
|
72
67
|
model_resource = nil
|
|
73
68
|
resource_type = given_resource_type
|
|
74
|
-
resource_type = resource_type==resource_type.downcase ? resource_type.to_sym : resource_type.constantize rescue nil
|
|
69
|
+
resource_type = resource_type == resource_type.downcase ? resource_type.to_sym : resource_type.constantize rescue nil
|
|
75
70
|
model_resource = resource_type.respond_to?(:new) ? resource_type.new : resource_type rescue nil
|
|
76
71
|
[resource_type,model_resource]
|
|
77
72
|
end
|
|
78
73
|
|
|
79
|
-
def resolve_resource_refinements(ability,model_resource,stage=1)
|
|
74
|
+
def resolve_resource_refinements(ability, model_resource, stage=1)
|
|
80
75
|
Array(Cancannible.refinements[stage-1]).each_with_object([]) do |refinement,memo|
|
|
81
76
|
refinement_attributes = refinement.dup
|
|
82
77
|
|
|
@@ -111,5 +106,4 @@ class Cancannible::Preloader
|
|
|
111
106
|
memo.push(restriction) if restriction.present?
|
|
112
107
|
end
|
|
113
108
|
end
|
|
114
|
-
|
|
115
|
-
end
|
|
109
|
+
end
|
data/lib/cancannible/version.rb
CHANGED
data/lib/cancannible.rb
CHANGED
|
@@ -2,8 +2,8 @@ require 'active_support'
|
|
|
2
2
|
require 'active_support/core_ext'
|
|
3
3
|
require 'cancan'
|
|
4
4
|
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
5
|
+
require 'cancannible/version'
|
|
6
|
+
require 'cancannible/config'
|
|
7
|
+
require 'cancannible/preload_adapter'
|
|
8
|
+
require 'cancannible/preloader'
|
|
9
|
+
require 'cancannible/grantee'
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
Cancannible.setup do |config|
|
|
2
|
-
|
|
3
2
|
# ABILITY CACHING
|
|
4
3
|
# ===============
|
|
5
4
|
# Cancannible supports optional ability caching. This can provide a significant performance
|
|
@@ -24,6 +23,7 @@ Cancannible.setup do |config|
|
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
# ACCESS REFINMENTS
|
|
26
|
+
# =================
|
|
27
27
|
# Cancannible allows general-purpose access refinements to be declared here. This will be enforced
|
|
28
28
|
# in addition to any rules defined in you Ability.rb file.
|
|
29
29
|
|
|
@@ -99,5 +99,4 @@ Cancannible.setup do |config|
|
|
|
99
99
|
#
|
|
100
100
|
# By default, access refinements are "stage 1" i.e. applied directly to the permissions being loaded.
|
|
101
101
|
# By specifying stage 2, this refinement is applied on top of all stage 1 refinements (if possible / applicable)
|
|
102
|
-
|
|
103
102
|
end
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# The Permission class stores permissions managed by CanCan and Cancannible
|
|
2
2
|
class Permission < ActiveRecord::Base
|
|
3
3
|
belongs_to :permissible, polymorphic: true
|
|
4
|
-
belongs_to :resource, polymorphic: true
|
|
4
|
+
belongs_to :resource, polymorphic: true, optional: true
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
validates :ability, uniqueness: { scope: [:resource_id, :resource_type, :permissible_id, :permissible_type] }
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
# Note: for Rails 3 you may need to declare attr_accessible as follows, depending on your whitelist_attributes setting.
|
|
9
|
+
# A future version of cancannible should make this unnecessary.
|
|
10
|
+
#
|
|
11
|
+
# attr_accessible :asserted, :ability, :resource_id, :resource_type
|
|
12
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -6,11 +6,11 @@ require 'sqlite3'
|
|
|
6
6
|
|
|
7
7
|
# Requires supporting files with custom matchers and macros, etc,
|
|
8
8
|
# in ./support/ and its subdirectories.
|
|
9
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
|
9
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each {|f| require f}
|
|
10
10
|
|
|
11
11
|
RSpec.configure do |config|
|
|
12
12
|
config.before do
|
|
13
13
|
Cancannible.reset!
|
|
14
14
|
run_migrations
|
|
15
15
|
end
|
|
16
|
-
end
|
|
16
|
+
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
module MigrationsHelper
|
|
2
|
-
|
|
3
2
|
def run_migrations
|
|
4
3
|
ActiveRecord::Base.establish_connection({
|
|
5
4
|
adapter: 'sqlite3',
|
|
@@ -8,7 +7,6 @@ module MigrationsHelper
|
|
|
8
7
|
|
|
9
8
|
ActiveRecord::Migration.suppress_messages do
|
|
10
9
|
ActiveRecord::Schema.define(:version => 0) do
|
|
11
|
-
|
|
12
10
|
create_table "members", :force => true do |t|
|
|
13
11
|
t.string "name"
|
|
14
12
|
t.string "email"
|
|
@@ -49,13 +47,11 @@ module MigrationsHelper
|
|
|
49
47
|
t.string "name"
|
|
50
48
|
t.integer "category_id"
|
|
51
49
|
end
|
|
52
|
-
|
|
53
50
|
end
|
|
54
51
|
end
|
|
55
52
|
end
|
|
56
|
-
|
|
57
53
|
end
|
|
58
54
|
|
|
59
55
|
RSpec.configure do |conf|
|
|
60
56
|
conf.include MigrationsHelper
|
|
61
|
-
end
|
|
57
|
+
end
|
data/spec/support/models.rb
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
# These model definitions are just used for the test scenarios
|
|
2
2
|
|
|
3
|
-
# The Permission class stores permissions maanged by CanCan and Cancannible
|
|
4
3
|
class Permission < ActiveRecord::Base
|
|
5
4
|
belongs_to :permissible, polymorphic: true
|
|
6
|
-
belongs_to :resource, polymorphic: true
|
|
5
|
+
belongs_to :resource, polymorphic: true, optional: true
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
:scope => [:resource_id, :resource_type,
|
|
10
|
-
:permissible_id, :permissible_type]
|
|
7
|
+
validates :ability, uniqueness: { scope: [:resource_id, :resource_type, :permissible_id, :permissible_type] }
|
|
11
8
|
end
|
|
12
9
|
|
|
13
10
|
class Member < ActiveRecord::Base
|
|
@@ -29,7 +26,7 @@ class RolesUsers < ActiveRecord::Base
|
|
|
29
26
|
end
|
|
30
27
|
|
|
31
28
|
class Role < ActiveRecord::Base
|
|
32
|
-
has_many :roles_users, :
|
|
29
|
+
has_many :roles_users, class_name: 'RolesUsers'
|
|
33
30
|
has_many :users, through: :roles_users
|
|
34
31
|
|
|
35
32
|
include Cancannible::Grantee
|
|
@@ -3,29 +3,46 @@ require 'spec_helper'
|
|
|
3
3
|
describe Cancannible do
|
|
4
4
|
let(:grantee_class) { Member }
|
|
5
5
|
let(:grantee) { grantee_class.create! }
|
|
6
|
+
let(:cached_object) do
|
|
7
|
+
abilities = Ability.new(grantee)
|
|
8
|
+
abilities.can :read, :all
|
|
9
|
+
abilities
|
|
10
|
+
end
|
|
6
11
|
|
|
7
12
|
describe "#abilities" do
|
|
8
13
|
subject { grantee.abilities }
|
|
9
14
|
|
|
10
15
|
context "when get_cached_abilities provided" do
|
|
11
16
|
before do
|
|
12
|
-
Cancannible.get_cached_abilities = proc{|grantee|
|
|
17
|
+
Cancannible.get_cached_abilities = proc { |grantee| cached_object }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "returns the cached object" do
|
|
21
|
+
expect(cached_object.instance_variable_defined?(:@rules_index)).to eql(true)
|
|
22
|
+
expect(subject).to eql(cached_object)
|
|
13
23
|
end
|
|
14
|
-
|
|
15
|
-
|
|
24
|
+
context 'when incompatible cached_object' do
|
|
25
|
+
let(:cached_object) { 'bogative' }
|
|
26
|
+
it 'returns a new object' do
|
|
27
|
+
expect(subject).to be_an(Ability)
|
|
28
|
+
expect(subject).to_not eql(cached_object)
|
|
29
|
+
end
|
|
16
30
|
end
|
|
17
31
|
context "unless reload requested" do
|
|
18
32
|
subject { grantee.abilities(true) }
|
|
19
|
-
it
|
|
33
|
+
it 'returns a new object' do
|
|
34
|
+
expect(subject).to be_an(Ability)
|
|
35
|
+
expect(subject).to_not eql(cached_object)
|
|
36
|
+
end
|
|
20
37
|
end
|
|
21
38
|
end
|
|
22
39
|
|
|
23
40
|
context "when store_cached_abilities provided" do
|
|
24
41
|
before do
|
|
25
42
|
@stored = nil
|
|
26
|
-
Cancannible.store_cached_abilities = proc{ |grantee,ability| @stored = { grantee_id: grantee.id, ability: ability } }
|
|
43
|
+
Cancannible.store_cached_abilities = proc { |grantee, ability| @stored = { grantee_id: grantee.id, ability: ability } }
|
|
27
44
|
end
|
|
28
|
-
it "
|
|
45
|
+
it "stores the cached object" do
|
|
29
46
|
expect { subject }.to change { @stored }.from(nil)
|
|
30
47
|
expect(@stored[:grantee_id]).to eql(grantee.id)
|
|
31
48
|
expect(@stored[:ability]).to be_an(Ability)
|
|
@@ -36,32 +53,29 @@ describe Cancannible do
|
|
|
36
53
|
before do
|
|
37
54
|
@stored = nil
|
|
38
55
|
@store = 0
|
|
39
|
-
Cancannible.get_cached_abilities = proc{|grantee| @stored }
|
|
40
|
-
Cancannible.store_cached_abilities = proc{ |grantee,ability| @store += 1 ; @stored = { grantee_id: grantee.id, ability: ability } }
|
|
56
|
+
Cancannible.get_cached_abilities = proc { |grantee| @stored[:ability] if @stored }
|
|
57
|
+
Cancannible.store_cached_abilities = proc { |grantee, ability| @store += 1 ; @stored = { grantee_id: grantee.id, ability: ability } }
|
|
41
58
|
end
|
|
42
|
-
it "
|
|
59
|
+
it "stores the cached object on the first call" do
|
|
43
60
|
expect { subject }.to change { @stored }.from(nil)
|
|
44
61
|
expect(@store).to eql(1)
|
|
45
62
|
expect(@stored[:grantee_id]).to eql(grantee.id)
|
|
46
63
|
expect(@stored[:ability]).to be_an(Ability)
|
|
47
64
|
end
|
|
48
|
-
it "
|
|
65
|
+
it "returns the cached object on the second call" do
|
|
49
66
|
expect { subject }.to change { @stored }.from(nil)
|
|
50
67
|
expect(@store).to eql(1)
|
|
51
68
|
expect { grantee.abilities }.to_not change { @store }
|
|
52
|
-
expect(grantee.abilities
|
|
53
|
-
expect(
|
|
69
|
+
expect(grantee.abilities).to be_an(Ability)
|
|
70
|
+
expect(@stored[:grantee_id]).to eql(grantee.id)
|
|
54
71
|
end
|
|
55
72
|
it "should re-cache object on the second call if refresh requested" do
|
|
56
73
|
expect { subject }.to change { @stored }.from(nil)
|
|
57
74
|
expect(@store).to eql(1)
|
|
58
75
|
expect { grantee.abilities(true) }.to change { @store }.from(1).to(2)
|
|
59
|
-
expect(grantee.abilities
|
|
60
|
-
expect(
|
|
76
|
+
expect(grantee.abilities).to be_an(Ability)
|
|
77
|
+
expect(@stored[:grantee_id]).to eql(grantee.id)
|
|
61
78
|
end
|
|
62
79
|
end
|
|
63
|
-
|
|
64
|
-
|
|
65
80
|
end
|
|
66
|
-
|
|
67
|
-
end
|
|
81
|
+
end
|
data/spec/unit/config_spec.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
describe Cancannible do
|
|
5
4
|
let(:grantee_class) { User }
|
|
6
5
|
let(:ability) { :blow }
|
|
@@ -9,7 +8,6 @@ describe Cancannible do
|
|
|
9
8
|
subject(:grantee) { grantee_class.create!(username: username) }
|
|
10
9
|
|
|
11
10
|
describe "#can?" do
|
|
12
|
-
|
|
13
11
|
context "with custom attribute association restriction" do
|
|
14
12
|
let(:resource_class) { Widget }
|
|
15
13
|
before do
|
|
@@ -245,7 +243,5 @@ describe Cancannible do
|
|
|
245
243
|
it { should be_falsey }
|
|
246
244
|
end
|
|
247
245
|
end
|
|
248
|
-
|
|
249
246
|
end
|
|
250
|
-
|
|
251
247
|
end
|
data/spec/unit/grantee_spec.rb
CHANGED
|
@@ -4,7 +4,6 @@ describe Cancannible::Grantee do
|
|
|
4
4
|
let(:grantee_class) { Member }
|
|
5
5
|
|
|
6
6
|
context "without permissions inheritance" do
|
|
7
|
-
|
|
8
7
|
describe "##inheritable_permissions" do
|
|
9
8
|
subject { grantee_class.inheritable_permissions }
|
|
10
9
|
it { should be_empty }
|
|
@@ -43,9 +42,13 @@ describe Cancannible::Grantee do
|
|
|
43
42
|
context "when permission is not asserted" do
|
|
44
43
|
it { should be_truthy }
|
|
45
44
|
end
|
|
46
|
-
context "when
|
|
45
|
+
context "when :can already asserted" do
|
|
47
46
|
before { grantee.can(:read, resource) }
|
|
48
47
|
it { should be_falsey }
|
|
48
|
+
context "and then reset as :cannot" do
|
|
49
|
+
before { grantee.cannot(:read, resource) }
|
|
50
|
+
it { should be_truthy }
|
|
51
|
+
end
|
|
49
52
|
end
|
|
50
53
|
context "when permission is asserted" do
|
|
51
54
|
before { grantee.cannot(:read, resource) }
|
|
@@ -54,31 +57,6 @@ describe Cancannible::Grantee do
|
|
|
54
57
|
end
|
|
55
58
|
end
|
|
56
59
|
|
|
57
|
-
context "with a nil resource" do
|
|
58
|
-
let!(:resource) { nil }
|
|
59
|
-
describe "#can? -> nil" do
|
|
60
|
-
subject { grantee.can?(:read, nil) }
|
|
61
|
-
context "when permission is not set" do
|
|
62
|
-
it { should be_falsey }
|
|
63
|
-
end
|
|
64
|
-
context "when permission is set" do
|
|
65
|
-
before { grantee.can(:read, resource) }
|
|
66
|
-
it { should be_truthy }
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
describe "#can? -> ''" do
|
|
70
|
-
subject { grantee.can?(:read, '') }
|
|
71
|
-
context "when permission is not set" do
|
|
72
|
-
it { should be_falsey }
|
|
73
|
-
end
|
|
74
|
-
context "when permission is set" do
|
|
75
|
-
before { grantee.can(:read, resource) }
|
|
76
|
-
it { should be_falsey }
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
|
|
82
60
|
context "with a resource class" do
|
|
83
61
|
let!(:resource) { Widget }
|
|
84
62
|
|
|
@@ -117,15 +95,14 @@ describe Cancannible::Grantee do
|
|
|
117
95
|
context "with a non-existent model" do
|
|
118
96
|
describe "instance" do
|
|
119
97
|
let!(:obsolete_permission) {grantee.permissions.create!(asserted: true, ability: 'manage', resource_type: 'Bogative', resource_id: 33) }
|
|
120
|
-
it "
|
|
121
|
-
expect { grantee.abilities }.
|
|
98
|
+
it "raises CanCan::Error" do
|
|
99
|
+
expect { grantee.abilities }.to raise_error(CanCan::Error)
|
|
122
100
|
end
|
|
123
101
|
end
|
|
124
102
|
describe "class" do
|
|
125
103
|
let!(:obsolete_permission) {grantee.permissions.create!(asserted: true, ability: 'manage', resource_type: 'Bogative') }
|
|
126
|
-
it "
|
|
127
|
-
grantee.abilities
|
|
128
|
-
expect { grantee.abilities }.to_not raise_error
|
|
104
|
+
it "raises CanCan::Error" do
|
|
105
|
+
expect { grantee.abilities }.to raise_error(CanCan::Error)
|
|
129
106
|
end
|
|
130
107
|
end
|
|
131
108
|
end
|
|
@@ -134,19 +111,17 @@ describe Cancannible::Grantee do
|
|
|
134
111
|
class SuperBogative < ActiveRecord::Base
|
|
135
112
|
end
|
|
136
113
|
context "instance" do
|
|
137
|
-
let!(:
|
|
138
|
-
it "
|
|
114
|
+
let!(:invalid_permission) { grantee.permissions.create!(asserted: true, ability: 'manage', resource_type: 'SuperBogative', resource_id: 33) }
|
|
115
|
+
it "does not error on load" do
|
|
139
116
|
expect { grantee.abilities }.to_not raise_error
|
|
140
117
|
end
|
|
141
118
|
end
|
|
142
119
|
context "class" do
|
|
143
|
-
let!(:
|
|
144
|
-
it "
|
|
120
|
+
let!(:invalid_permission) { grantee.permissions.create!(asserted: true, ability: 'manage', resource_type: 'SuperBogative') }
|
|
121
|
+
it "does not error on load" do
|
|
145
122
|
expect { grantee.abilities }.to_not raise_error
|
|
146
123
|
end
|
|
147
124
|
end
|
|
148
125
|
end
|
|
149
|
-
|
|
150
126
|
end
|
|
151
|
-
|
|
152
127
|
end
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
describe Cancannible do
|
|
5
4
|
let(:grantee_class) { User }
|
|
6
5
|
|
|
7
6
|
context "with mulitple sources of permissions inheritance" do
|
|
8
|
-
|
|
9
7
|
describe "##inheritable_permissions" do
|
|
10
8
|
subject { grantee_class.inheritable_permissions }
|
|
11
9
|
it { should eql([:roles, :group]) }
|
|
@@ -15,12 +13,12 @@ describe Cancannible do
|
|
|
15
13
|
let!(:role_b) { Role.create! }
|
|
16
14
|
let!(:group_a) { Group.create! }
|
|
17
15
|
let!(:group_b) { Group.create! }
|
|
18
|
-
subject(:grantee)
|
|
16
|
+
subject(:grantee) do
|
|
19
17
|
u = grantee_class.new(group: group_a)
|
|
20
18
|
u.roles << role_a
|
|
21
19
|
u.save!
|
|
22
20
|
u
|
|
23
|
-
|
|
21
|
+
end
|
|
24
22
|
|
|
25
23
|
context "with a symbolic resource" do
|
|
26
24
|
let!(:resource) { :something }
|
|
@@ -43,10 +41,8 @@ describe Cancannible do
|
|
|
43
41
|
it { should be_truthy }
|
|
44
42
|
end
|
|
45
43
|
end
|
|
46
|
-
|
|
47
44
|
end
|
|
48
45
|
|
|
49
|
-
|
|
50
46
|
context "with a resource class" do
|
|
51
47
|
let!(:resource) { Widget }
|
|
52
48
|
|
|
@@ -68,7 +64,6 @@ describe Cancannible do
|
|
|
68
64
|
it { should be_truthy }
|
|
69
65
|
end
|
|
70
66
|
end
|
|
71
|
-
|
|
72
67
|
end
|
|
73
68
|
|
|
74
69
|
context "with a resource instance" do
|
|
@@ -136,10 +131,6 @@ describe Cancannible do
|
|
|
136
131
|
it { should be_truthy }
|
|
137
132
|
end
|
|
138
133
|
end
|
|
139
|
-
|
|
140
|
-
|
|
141
134
|
end
|
|
142
|
-
|
|
143
135
|
end
|
|
144
|
-
|
|
145
136
|
end
|
metadata
CHANGED
|
@@ -1,141 +1,169 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cancannible
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paul Gallagher
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-03-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - ~>
|
|
17
|
+
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '6.1'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - ~>
|
|
24
|
+
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '6.1'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: activemodel
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - ~>
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '6.1'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - ~>
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '6.1'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
42
|
+
name: cancancan
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- -
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '0'
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- -
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
54
|
+
version: '0'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: activerecord
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - ~>
|
|
59
|
+
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '6.1'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - ~>
|
|
66
|
+
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '6.1'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: sqlite3
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
|
-
- -
|
|
73
|
+
- - ">="
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version:
|
|
75
|
+
version: 1.3.2
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
|
-
- -
|
|
80
|
+
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version:
|
|
82
|
+
version: 1.3.2
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: bundler
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- -
|
|
87
|
+
- - ">="
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
89
|
+
version: '0'
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
|
-
- -
|
|
94
|
+
- - ">="
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
96
|
+
version: '0'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: rake
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
|
-
- -
|
|
101
|
+
- - ">="
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
103
|
+
version: '0'
|
|
104
104
|
type: :development
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
|
-
- -
|
|
108
|
+
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rdoc
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
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
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
126
|
name: rspec
|
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
|
114
128
|
requirements:
|
|
115
|
-
- -
|
|
129
|
+
- - ">="
|
|
116
130
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '
|
|
131
|
+
version: '0'
|
|
118
132
|
type: :development
|
|
119
133
|
prerelease: false
|
|
120
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
135
|
requirements:
|
|
122
|
-
- -
|
|
136
|
+
- - ">="
|
|
123
137
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '
|
|
138
|
+
version: '0'
|
|
125
139
|
- !ruby/object:Gem::Dependency
|
|
126
140
|
name: guard-rspec
|
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
|
128
142
|
requirements:
|
|
129
|
-
- -
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: rb-fsevent
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">="
|
|
130
158
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '
|
|
159
|
+
version: '0'
|
|
132
160
|
type: :development
|
|
133
161
|
prerelease: false
|
|
134
162
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
163
|
requirements:
|
|
136
|
-
- -
|
|
164
|
+
- - ">="
|
|
137
165
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '
|
|
166
|
+
version: '0'
|
|
139
167
|
description: Extends CanCan with dynamic, inheritable permissions stored in a database,
|
|
140
168
|
with caching and multi-tenant refinements
|
|
141
169
|
email:
|
|
@@ -144,9 +172,8 @@ executables: []
|
|
|
144
172
|
extensions: []
|
|
145
173
|
extra_rdoc_files: []
|
|
146
174
|
files:
|
|
147
|
-
- .
|
|
148
|
-
- .
|
|
149
|
-
- .travis.yml
|
|
175
|
+
- ".github/workflows/ruby.yml"
|
|
176
|
+
- ".gitignore"
|
|
150
177
|
- Gemfile
|
|
151
178
|
- Guardfile
|
|
152
179
|
- LICENSE.txt
|
|
@@ -176,24 +203,23 @@ homepage: https://github.com/evendis/cancannible
|
|
|
176
203
|
licenses:
|
|
177
204
|
- MIT
|
|
178
205
|
metadata: {}
|
|
179
|
-
post_install_message:
|
|
206
|
+
post_install_message:
|
|
180
207
|
rdoc_options: []
|
|
181
208
|
require_paths:
|
|
182
209
|
- lib
|
|
183
210
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
184
211
|
requirements:
|
|
185
|
-
- -
|
|
212
|
+
- - ">="
|
|
186
213
|
- !ruby/object:Gem::Version
|
|
187
214
|
version: '0'
|
|
188
215
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
216
|
requirements:
|
|
190
|
-
- -
|
|
217
|
+
- - ">="
|
|
191
218
|
- !ruby/object:Gem::Version
|
|
192
219
|
version: '0'
|
|
193
220
|
requirements: []
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
signing_key:
|
|
221
|
+
rubygems_version: 3.2.20
|
|
222
|
+
signing_key:
|
|
197
223
|
specification_version: 4
|
|
198
224
|
summary: Dynamic, configurable permissions for CanCan
|
|
199
225
|
test_files:
|
data/.rspec
DELETED
data/.travis.yml
DELETED