i_am_i_can 3.0.0pre → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/README.md +46 -23
- data/i_am_i_can.gemspec +3 -2
- data/lib/generators/i_am_i_can/setup_generator.rb +53 -16
- data/lib/generators/i_am_i_can/templates/initializers/i_am_i_can.erb +8 -0
- data/lib/generators/i_am_i_can/templates/migrations/i_am_i_can.erb +78 -0
- data/lib/generators/i_am_i_can/templates/models/permission.erb +9 -2
- data/lib/generators/i_am_i_can/templates/models/role.erb +15 -4
- data/lib/generators/i_am_i_can/templates/models/role_group.erb +12 -5
- data/lib/i_am_i_can/configs/config.rb +32 -0
- data/lib/i_am_i_can/configs/configs.rb +29 -0
- data/lib/i_am_i_can/configurable.rb +28 -0
- data/lib/i_am_i_can/dynamic_generate.rb +95 -0
- data/lib/i_am_i_can/permission/assignment.rb +3 -18
- data/lib/i_am_i_can/permission/definition.rb +1 -21
- data/lib/i_am_i_can/permission/helpers.rb +35 -8
- data/lib/i_am_i_can/permission.rb +50 -50
- data/lib/i_am_i_can/reflection.rb +25 -0
- data/lib/i_am_i_can/resource.rb +29 -0
- data/lib/i_am_i_can/role/assignment.rb +2 -16
- data/lib/i_am_i_can/role/definition.rb +4 -46
- data/lib/i_am_i_can/role/helpers.rb +43 -5
- data/lib/i_am_i_can/role.rb +17 -0
- data/lib/i_am_i_can/subject/permission_querying.rb +4 -4
- data/lib/i_am_i_can/subject.rb +24 -0
- data/lib/i_am_i_can/version.rb +1 -1
- data/lib/i_am_i_can.rb +52 -35
- metadata +32 -12
- data/lib/generators/i_am_i_can/templates/migrations/add_to_subject.erb +0 -5
- data/lib/generators/i_am_i_can/templates/migrations/permission.erb +0 -15
- data/lib/generators/i_am_i_can/templates/migrations/role.erb +0 -13
- data/lib/generators/i_am_i_can/templates/migrations/role_group.erb +0 -14
- data/lib/i_am_i_can/config.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cda6740e5fdc39a1bf281df8ec61425d001171a
|
4
|
+
data.tar.gz: 263b17f70ce6ff0fdfdfff8338a85b798e43b500
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6d4f29d6565b394bce3d01ddab46f2532d307b093318b5a9cc01e8ce1746edbc3dbe0616403a9a1ab96b05403ac02b933fb90c0f089d99e0746d9efb1040897
|
7
|
+
data.tar.gz: a545727532511dbb469b0057b5160764442896f9c10d0705a43ce13b40b1bb4c55d3b40be3579449804b653a904cbf5e48ecbcbb6d38472f0ff8bde4d6f45497
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
i_am_i_can (3.0.
|
4
|
+
i_am_i_can (3.0.0)
|
5
5
|
activerecord
|
6
6
|
activesupport
|
7
7
|
railties
|
@@ -53,6 +53,7 @@ GEM
|
|
53
53
|
minitest (5.11.3)
|
54
54
|
nokogiri (1.8.4)
|
55
55
|
mini_portile2 (~> 2.3.0)
|
56
|
+
pg (1.0.0)
|
56
57
|
pry (0.11.3)
|
57
58
|
coderay (~> 1.1.0)
|
58
59
|
method_source (~> 0.9.0)
|
@@ -109,6 +110,7 @@ DEPENDENCIES
|
|
109
110
|
bundler (~> 1.16)
|
110
111
|
database_cleaner
|
111
112
|
i_am_i_can!
|
113
|
+
pg
|
112
114
|
pry
|
113
115
|
rake (~> 10.0)
|
114
116
|
rspec (~> 3.0)
|
data/README.md
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# IAmICan
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/i_am_i_can.svg)](https://badge.fury.io/rb/i_am_i_can)
|
3
4
|
[![Build Status](https://travis-ci.org/zhandao/i_am_i_can.svg?branch=master)](https://travis-ci.org/zhandao/i_am_i_can)
|
4
5
|
[![Maintainability](https://api.codeclimate.com/v1/badges/27b664da01b6cc7180e3/maintainability)](https://codeclimate.com/github/zhandao/i_am_i_can/maintainability)
|
5
6
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/27b664da01b6cc7180e3/test_coverage)](https://codeclimate.com/github/zhandao/i_am_i_can/test_coverage)
|
6
7
|
|
7
|
-
Concise and Natural DSL for `Subject - Role(Role Group) - Permission` Management.
|
8
|
+
Concise and Natural DSL for `Subject - Role(Role Group) - Permission - Resource` Management (RBAC like).
|
8
9
|
|
9
10
|
```ruby
|
10
11
|
# our Subject is People, and subject is he:
|
@@ -45,21 +46,32 @@ he.can? :perform, :magic # => true
|
|
45
46
|
# Cancel Assignment
|
46
47
|
he.falls_from :admin
|
47
48
|
Roles.which(name: :coder).cannot :fly
|
49
|
+
|
50
|
+
# Get allowed resources:
|
51
|
+
Resource.that_allow(user).to(:manage) # => Active::Relation
|
48
52
|
```
|
49
53
|
|
50
54
|
## Concepts and Overview
|
51
55
|
|
52
56
|
### Definition and uniqueness of nouns
|
53
57
|
|
58
|
+
0. Subject
|
59
|
+
- Someone who can be assigned roles, and who has permissions through the assigned roles.
|
60
|
+
- See wiki [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)
|
54
61
|
1. Role
|
55
|
-
-
|
56
|
-
-
|
57
|
-
|
58
|
-
|
59
|
-
-
|
60
|
-
|
61
|
-
|
62
|
-
-
|
62
|
+
- A job function that groups a series of permissions according to a certain dimension.
|
63
|
+
- Also see wiki [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)
|
64
|
+
- Uniquely identified by `name`
|
65
|
+
2. Role Group
|
66
|
+
- A group of roles that may have the same permissions.
|
67
|
+
- Uniquely identified by `name`
|
68
|
+
3. Permission
|
69
|
+
- An action, or an approval of a mode of access to a resource
|
70
|
+
- Also see wiki [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)
|
71
|
+
- Uniquely identified by `predicate( + object)` (name),
|
72
|
+
or we can say, `action( + resource)`
|
73
|
+
4. Resource
|
74
|
+
- Polymorphic association with permissions
|
63
75
|
|
64
76
|
|
65
77
|
### In one word:
|
@@ -90,13 +102,13 @@ Roles.which(name: :coder).cannot :fly
|
|
90
102
|
- the role or permission you want to assign **MUST** be defined before
|
91
103
|
- option :auto_define_before (before assignment) you may need in some cases
|
92
104
|
- class methods, like: `UserRoleGroup.have_permission :fly`
|
93
|
-
|
105
|
+
|
94
106
|
**Definition => Assignment => Querying**
|
95
|
-
|
107
|
+
|
96
108
|
### Two Concepts of this gem
|
97
109
|
|
98
|
-
1. Stored (save in database)
|
99
|
-
2. Local (variable value)
|
110
|
+
1. Stored (save in database) TODO
|
111
|
+
2. Local (variable value) TODO
|
100
112
|
|
101
113
|
[Feature List: needs you](https://github.com/zhandao/i_am_i_can/issues/2)
|
102
114
|
|
@@ -107,33 +119,40 @@ Roles.which(name: :coder).cannot :fly
|
|
107
119
|
```ruby
|
108
120
|
gem 'i_am_i_can'
|
109
121
|
```
|
110
|
-
|
122
|
+
|
111
123
|
2. Generate migrations and models by your subject name:
|
112
124
|
|
113
125
|
```bash
|
114
126
|
rails g i_am_i_can:setup <subject_name>
|
115
127
|
```
|
116
|
-
|
128
|
+
|
117
129
|
For example, if your subject name is `user`, it will generate
|
118
130
|
model `UserRole`, `UserRoleGroup` and `UserPermission`
|
119
|
-
|
120
|
-
3. run `rails db:migrate`
|
121
131
|
|
122
|
-
|
132
|
+
3. Add the code returned by the generator to your subject model, like:
|
123
133
|
|
124
134
|
```ruby
|
125
135
|
class User
|
126
|
-
|
136
|
+
has_and_belongs_to_many :stored_roles,
|
137
|
+
join_table: 'users_and_user_roles', foreign_key: 'user_role_id', class_name: 'UserRole', association_foreign_key: 'user_id'
|
138
|
+
|
139
|
+
act_as_subject
|
127
140
|
end
|
128
141
|
```
|
129
|
-
|
130
|
-
[here](#options) is some options you can pass to the declaration.
|
131
|
-
|
142
|
+
|
143
|
+
[here](#config-options) is some options you can pass to the declaration.
|
144
|
+
|
145
|
+
4. Run `rails db:migrate`
|
146
|
+
|
132
147
|
That's all!
|
133
148
|
|
134
149
|
## Usage
|
135
150
|
|
136
|
-
###
|
151
|
+
### Customization
|
152
|
+
|
153
|
+
1. association names TODO
|
154
|
+
|
155
|
+
### Config Options
|
137
156
|
|
138
157
|
TODO
|
139
158
|
|
@@ -423,6 +442,10 @@ user.is? :master # => true
|
|
423
442
|
user.can? :read, :book # => true
|
424
443
|
```
|
425
444
|
|
445
|
+
#### I. Resource Querying
|
446
|
+
|
447
|
+
TODO
|
448
|
+
|
426
449
|
## Development
|
427
450
|
|
428
451
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/i_am_i_can.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["zhandao"]
|
10
10
|
spec.email = ["x@skippingcat.com"]
|
11
11
|
|
12
|
-
spec.summary = 'Concise and Natural DSL for `Subject - Role(Role Group) - Permission` Management.'
|
13
|
-
spec.description = 'Concise and Natural DSL for `Subject - Role(Role Group) - Permission` Management.'
|
12
|
+
spec.summary = '(RBAC like) Concise and Natural DSL for `Subject - Role(Role Group) - Permission - Resource` Management.'
|
13
|
+
spec.description = '(RBAC like) Concise and Natural DSL for `Subject - Role(Role Group) - Permission - Resource` Management.'
|
14
14
|
spec.homepage = 'https://github.com/zhandao/i_am_i_can'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
27
|
spec.add_development_dependency 'rspec-rails'
|
28
28
|
spec.add_development_dependency 'database_cleaner'
|
29
|
+
spec.add_development_dependency 'pg'
|
29
30
|
spec.add_development_dependency 'simplecov'
|
30
31
|
spec.add_development_dependency 'pry'
|
31
32
|
|
@@ -12,45 +12,82 @@ module IAmICan
|
|
12
12
|
|
13
13
|
source_root File.expand_path('../templates', __FILE__)
|
14
14
|
|
15
|
+
# TODO: more readable tips
|
15
16
|
def questions
|
16
17
|
@ii_opts = { }
|
17
|
-
|
18
|
+
role_class = ask("Do you want to change the class name of the role model (defaults to [#{name_c}Role])? Press Enter or input your name:")
|
19
|
+
@ii_opts[:role_class] = role_class.blank? ? "#{name_c}Role" : role_class
|
20
|
+
pms_class = ask("Do you want to change the class name of the permission model (defaults to [#{name_c}Permission])? Press Enter or input your name:")
|
21
|
+
@ii_opts[:permission_class] = pms_class.blank? ? "#{name_c}Permission" : pms_class
|
22
|
+
if yes?('Do you want to use role group? y (default) / n')
|
23
|
+
group_class = ask("Do you want to change the class name of the role_group model (defaults to [#{name_c}RoleGroup])? Press Enter or input your name:")
|
24
|
+
@ii_opts[:role_group_class] = group_class.blank? ? "#{name_c}RoleGroup" : group_class
|
25
|
+
else
|
18
26
|
@ii_opts[:without_group] = true
|
19
27
|
end
|
20
|
-
|
28
|
+
|
29
|
+
unless yes?('Do yo want it to save role and permission to database by default? y (default) / n')
|
21
30
|
@ii_opts[:default_save] = false
|
22
31
|
end
|
23
|
-
if yes?('
|
32
|
+
# if @ii_opts[:default_save] != false && yes?('Don\'t you need **local** definition and assignment feature? y / n (default)')
|
33
|
+
# TODO
|
34
|
+
# end
|
35
|
+
if yes?('Do you want it to raise error when you are doing wrong definition or assignment? y / n (default)')
|
24
36
|
@ii_opts[:strict_mode] = true
|
25
37
|
end
|
26
|
-
if yes?('Do you want it to define the role/permission which is not defined when assigning to subject?')
|
38
|
+
if yes?('Do you want it to auto define the role/permission which is not defined when assigning to subject? y / n (default)')
|
27
39
|
@ii_opts[:auto_define_before] = true
|
28
40
|
end
|
29
41
|
end
|
30
42
|
|
31
43
|
def setup_migrations
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
44
|
+
migration_template 'migrations/i_am_i_can.erb', "db/migrate/#{name_u}_am_#{name_u}_can.rb"
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup_initializer
|
48
|
+
template 'initializers/i_am_i_can.erb', "config/initializers/#{name_u}_am_#{name_u}_can.rb"
|
37
49
|
end
|
38
50
|
|
39
51
|
def setup_models
|
40
|
-
template 'models/role.erb', "app/models/#{
|
41
|
-
template 'models/role_group.erb', "app/models/#{
|
42
|
-
template 'models/permission.erb', "app/models/#{
|
52
|
+
template 'models/role.erb', "app/models/#{role_u}.rb"
|
53
|
+
template 'models/role_group.erb', "app/models/#{group_u}.rb" unless @ii_opts[:without_group]
|
54
|
+
template 'models/permission.erb', "app/models/#{permission_u}.rb"
|
43
55
|
end
|
44
56
|
|
45
|
-
def
|
46
|
-
|
47
|
-
puts
|
48
|
-
|
57
|
+
def tips
|
58
|
+
puts " Add the code below to #{name_c}:".green
|
59
|
+
puts <<~TIPS
|
60
|
+
has_and_belongs_to_many :stored_roles,
|
61
|
+
join_table: '#{subj_role_tb}', foreign_key: '#{role_u}_id', class_name: '#{role_c}', association_foreign_key: '#{name_u}_id'
|
62
|
+
|
63
|
+
act_as_subject
|
64
|
+
TIPS
|
49
65
|
end
|
50
66
|
|
51
67
|
def self.next_migration_number(dirname)
|
52
68
|
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
53
69
|
end
|
70
|
+
|
71
|
+
def name_c; name.camelize end
|
72
|
+
def name_u; name.underscore end
|
73
|
+
def name_up; name_u.pluralize end
|
74
|
+
|
75
|
+
def role_c; @ii_opts[:role_class] end
|
76
|
+
def role_u; @ii_opts[:role_class].underscore end
|
77
|
+
def role_up; @ii_opts[:role_class].underscore.pluralize end
|
78
|
+
|
79
|
+
def group_c; @ii_opts[:role_group_class] end
|
80
|
+
def group_u; @ii_opts[:role_group_class]&.underscore end
|
81
|
+
def group_up; @ii_opts[:role_group_class]&.underscore&.pluralize end
|
82
|
+
|
83
|
+
def permission_c; @ii_opts[:permission_class] end
|
84
|
+
def permission_u; @ii_opts[:permission_class].underscore end
|
85
|
+
def permission_up; @ii_opts[:permission_class].underscore.pluralize end
|
86
|
+
|
87
|
+
def subj_role_tb; name_up + '_and_' + role_up end
|
88
|
+
def group_role_tb; group_up + '_and_' + role_up end
|
89
|
+
def role_pms_tb; role_up + '_and_' + permission_up end
|
90
|
+
def group_pms_tb; group_up + '_and_' + permission_up end
|
54
91
|
end
|
55
92
|
end
|
56
93
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
IAmICan::Configs.set_for(subject: '<%= name_c %>',
|
2
|
+
role: '<%= @ii_opts[:role_class] %>',
|
3
|
+
permission: '<%= @ii_opts[:permission_class] %>'<% unless @ii_opts[:without_group] %>,
|
4
|
+
role_group: '<%= @ii_opts[:role_group_class] %>'<% end %>) do |config|
|
5
|
+
<% @ii_opts.except(:role_class, :permission_class, :role_group_class).each do |k, v| %>
|
6
|
+
<%= "config.#{k} = " %><%= v %>
|
7
|
+
<% end %>
|
8
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class <%= name_c %>Am<%= name_c %>Can < ActiveRecord::Migration::Current
|
2
|
+
def change
|
3
|
+
create_table :<%= role_up %>, force: :cascade do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :desc
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :<%= role_up %>, :name,
|
11
|
+
unique: true, name: '<%= role_up %>_unique_index', using: :btree
|
12
|
+
<% unless @ii_opts[:without_group] %>
|
13
|
+
create_table :<%= group_up %>, force: :cascade do |t|
|
14
|
+
t.string :name, null: false
|
15
|
+
t.string :desc
|
16
|
+
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
|
20
|
+
add_index :<%= group_up %>, :name,
|
21
|
+
unique: true, name: '<%= group_up %>_unique_index', using: :btree
|
22
|
+
<% end %>
|
23
|
+
create_table :<%= permission_up %>, force: :cascade do |t|
|
24
|
+
t.string :pred, null: false
|
25
|
+
t.string :obj_type
|
26
|
+
t.integer :obj_id
|
27
|
+
t.string :desc
|
28
|
+
|
29
|
+
t.timestamps
|
30
|
+
end
|
31
|
+
|
32
|
+
add_index :<%= permission_up %>, %i[ pred obj_type obj_id ],
|
33
|
+
unique: true, name: '<%= permission_up %>_unique_index', using: :btree
|
34
|
+
|
35
|
+
create_table :<%= subj_role_tb %>, id: false, force: :cascade do |t|
|
36
|
+
t.belongs_to :<%= name_u %>, null: false#, index: false
|
37
|
+
t.belongs_to :<%= role_u %>, null: false#, index: false
|
38
|
+
end
|
39
|
+
|
40
|
+
# add_index :<%= subj_role_tb %>, :<%= name_u %>_id, name: ':<%= subj_role_tb %>_index1'
|
41
|
+
# add_index :<%= subj_role_tb %>, :<%= role_u %>_id, name: ':<%= subj_role_tb %>_index2'
|
42
|
+
add_index :<%= subj_role_tb %>, [ :<%= name_u %>_id, :<%= role_u %>_id ],
|
43
|
+
unique: true, name: '<%= subj_role_tb %>_uniq_index'
|
44
|
+
<% unless @ii_opts[:without_group] %>
|
45
|
+
create_table :<%= group_role_tb %>, id: false, force: :cascade do |t|
|
46
|
+
t.belongs_to :<%= group_u %>, null: false#, index: false
|
47
|
+
t.belongs_to :<%= role_u %>, null: false#, index: false
|
48
|
+
end
|
49
|
+
|
50
|
+
# add_index :<%= group_role_tb %>, :<%= group_u %>_id, name: '<%= group_role_tb %>_index1'
|
51
|
+
# add_index :<%= group_role_tb %>, :<%= role_u %>_id, name: '<%= group_role_tb %>_index2'
|
52
|
+
add_index :<%= group_role_tb %>, [ :<%= group_u %>_id, :<%= role_u %>_id ],
|
53
|
+
unique: true, name: '<%= group_role_tb %>_uniq_index'
|
54
|
+
|
55
|
+
<% end %>
|
56
|
+
create_table :<%= role_pms_tb %>, id: false, force: :cascade do |t|
|
57
|
+
t.belongs_to :<%= role_u %>, null: false#, index: false
|
58
|
+
t.belongs_to :<%= permission_u %>, null: false#, index: false
|
59
|
+
end
|
60
|
+
|
61
|
+
# add_index :<%= role_pms_tb %>, :<%= role_u %>_id, name: '<%= role_pms_tb %>_index1'
|
62
|
+
# add_index :<%= role_pms_tb %>, :<%= permission_u %>_id, name: '<%= role_pms_tb %>_index2'
|
63
|
+
add_index :<%= role_pms_tb %>, [ :<%= role_u %>_id, :<%= permission_u %>_id ],
|
64
|
+
unique: true, name: '<%= role_pms_tb %>_uniq_index'
|
65
|
+
<% unless @ii_opts[:without_group] %>
|
66
|
+
create_table :<%= group_pms_tb %>, id: false, force: :cascade do |t|
|
67
|
+
t.belongs_to :<%= group_u %>, null: false#, index: false
|
68
|
+
t.belongs_to :<%= permission_u %>, null: false#, index: false
|
69
|
+
end
|
70
|
+
|
71
|
+
# add_index :<%= group_pms_tb %>, :<%= group_u %>_id, name: '<%= group_pms_tb %>_index1'
|
72
|
+
# add_index :<%= group_pms_tb %>, :<%= permission_u %>_id, name: '<%= group_pms_tb %>_index2'
|
73
|
+
add_index :<%= group_pms_tb %>, [ :<%= group_u %>_id, :<%= permission_u %>_id ],
|
74
|
+
unique: true, name: '<%= group_pms_tb %>_uniq_index'
|
75
|
+
|
76
|
+
<% end %>
|
77
|
+
end
|
78
|
+
end
|
@@ -1,4 +1,11 @@
|
|
1
|
-
class <%=
|
1
|
+
class <%= permission_c %> < ActiveRecord::Base
|
2
|
+
has_and_belongs_to_many :related_roles,
|
3
|
+
join_table: '<%= role_pms_tb %>', foreign_key: :<%= role_u %>_id, class_name: '<%= role_c %>', association_foreign_key: :<%= permission_u %>_id
|
4
|
+
<% unless @ii_opts[:without_group] %>
|
5
|
+
has_and_belongs_to_many :related_role_groups,
|
6
|
+
join_table: '<%= group_pms_tb %>', foreign_key: :<%= group_u %>_id, class_name: '<%= group_c %>', association_foreign_key: :<%= permission_u %>_id
|
7
|
+
<% end %>
|
8
|
+
act_as_permission
|
2
9
|
end
|
3
10
|
|
4
11
|
__END__
|
@@ -8,4 +15,4 @@ __END__
|
|
8
15
|
integer :obj_id
|
9
16
|
string :desc
|
10
17
|
|
11
|
-
index %i[pred obj_type obj_id], unique: true
|
18
|
+
index %i[ pred obj_type obj_id ], unique: true
|
@@ -1,10 +1,21 @@
|
|
1
|
-
class <%=
|
1
|
+
class <%= role_c %> < ActiveRecord::Base
|
2
|
+
has_and_belongs_to_many :related_users,
|
3
|
+
join_table: '<%= subj_role_tb %>', foreign_key: :<%= name_u %>_id, class_name: '<%= name_c %>', association_foreign_key: :<%= role_u %>_id
|
4
|
+
<% unless @ii_opts[:without_group] %>
|
5
|
+
has_and_belongs_to_many :related_stored_groups,
|
6
|
+
join_table: '<%= group_role_tb %>', foreign_key: :<%= group_u %>_id, class_name: '<%= group_c %>', association_foreign_key: :<%= role_u %>_id
|
7
|
+
<% end %>
|
8
|
+
has_and_belongs_to_many :stored_permissions,
|
9
|
+
join_table: '<%= role_pms_tb %>', foreign_key: :<%= permission_u %>_id, class_name: '<%= permission_c %>', association_foreign_key: :<%= role_u %>_id
|
10
|
+
|
11
|
+
act_as_role
|
12
|
+
|
13
|
+
# default_scope { with_stored_permissions }
|
2
14
|
end
|
3
15
|
|
4
16
|
__END__
|
5
17
|
|
6
|
-
string :name,
|
7
|
-
integer :permission_ids, array: true, default: [ ]
|
18
|
+
string :name, null: false
|
8
19
|
string :desc
|
9
20
|
|
10
|
-
index :name, unique: true
|
21
|
+
index :name, unique: true
|
@@ -1,11 +1,18 @@
|
|
1
|
-
class
|
1
|
+
class RoleGroup < ActiveRecord::Base
|
2
|
+
has_and_belongs_to_many :stored_permissions,
|
3
|
+
join_table: '<%= group_pms_tb %>', foreign_key: :<%= permission_u %>_id, class_name: '<%= permission_c %>', association_foreign_key: :<%= group_u %>_id
|
4
|
+
|
5
|
+
has_and_belongs_to_many :members,
|
6
|
+
join_table: '<%= group_role_tb %>', foreign_key: :<%= role_u %>_id, class_name: '<%= role_c %>', association_foreign_key: :<%= group_u %>_id
|
7
|
+
|
8
|
+
act_as_role_group
|
9
|
+
|
10
|
+
# default_scope { with_members.with_stored_permissions) }
|
2
11
|
end
|
3
12
|
|
4
13
|
__END__
|
5
14
|
|
6
|
-
string :name,
|
7
|
-
integer :member_ids, array: true, default: [ ]
|
8
|
-
integer :permission_ids, array: true, default: [ ]
|
15
|
+
string :name, null: false
|
9
16
|
string :desc
|
10
17
|
|
11
|
-
index :name, unique: true
|
18
|
+
index :name, unique: true
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module IAmICan
|
2
|
+
module Configs
|
3
|
+
class Config
|
4
|
+
attr_accessor :subject_class, :role_class, :role_group_class, :permission_class,
|
5
|
+
:auto_define_before, :strict_mode, :without_group, :default_save, :act
|
6
|
+
|
7
|
+
def initialize(*classes)
|
8
|
+
self.subject_class, self.role_class, self.permission_class, self.role_group_class = classes
|
9
|
+
self.auto_define_before = false
|
10
|
+
self.strict_mode = false
|
11
|
+
self.without_group = false
|
12
|
+
self.default_save = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def subject_model
|
16
|
+
@subject_model ||= subject_class.constantize
|
17
|
+
end
|
18
|
+
|
19
|
+
def role_model
|
20
|
+
@role_model ||= role_class.constantize
|
21
|
+
end
|
22
|
+
|
23
|
+
def role_group_model
|
24
|
+
@role_group_model ||= role_group_class.constantize rescue nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def permission_model
|
28
|
+
@permission_model ||= permission_class.constantize
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'i_am_i_can/configs/config'
|
2
|
+
|
3
|
+
module IAmICan
|
4
|
+
module Configs
|
5
|
+
cattr_accessor :configs do
|
6
|
+
{ }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.set_for(subject:, role:, permission:, role_group: nil, &block)
|
10
|
+
config = Config.new(subject, role, permission, role_group)
|
11
|
+
config.instance_eval(&block)
|
12
|
+
configs.merge!(
|
13
|
+
subject => config.dup,
|
14
|
+
role => config.dup,
|
15
|
+
permission => config.dup,
|
16
|
+
)
|
17
|
+
configs.merge!(role_group => config.dup) if role_group
|
18
|
+
config
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.get(class_name)
|
22
|
+
configs[class_name]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.take
|
26
|
+
configs.values.first
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'i_am_i_can/configs/configs'
|
2
|
+
|
3
|
+
module IAmICan
|
4
|
+
module Configurable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
def i_am_i_can
|
9
|
+
Configs.get(self.name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def _reflect_of(key)
|
13
|
+
_name = i_am_i_can.send("#{key}_class")
|
14
|
+
reflections.each do |name, reflection|
|
15
|
+
return name if reflection.class_name == _name
|
16
|
+
end; nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
included do
|
21
|
+
def i_am_i_can
|
22
|
+
Configs.get(self.class.name)
|
23
|
+
end
|
24
|
+
|
25
|
+
delegate :_reflect_of, to: self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module IAmICan
|
2
|
+
module DynamicGenerate
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def scopes
|
6
|
+
# Generate scopes of each specified i_am_i_can association
|
7
|
+
#
|
8
|
+
# scope :with_stored_roles, -> { includes(:stored_roles) }
|
9
|
+
#
|
10
|
+
proc do |keys|
|
11
|
+
keys.each do |k|
|
12
|
+
scope :"with_#{_reflect_of(k)}", -> { includes(_reflect_of(k)) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def class_reflections
|
18
|
+
# Extend each associated querying to a class method that returns ActiveRecord::Relation
|
19
|
+
#
|
20
|
+
# Suppose: in UserRole model,
|
21
|
+
# has_and_belongs_to_many :related_users
|
22
|
+
#
|
23
|
+
# It will do like this:
|
24
|
+
# def self.related_users
|
25
|
+
# i_am_i_can.subject_model.with_stored_roles.where(user_roles: { id: self.ids })
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Usage:
|
29
|
+
# UserRole.all.related_users
|
30
|
+
#
|
31
|
+
proc do
|
32
|
+
%w[ subject role role_group permission ].each do |k|
|
33
|
+
next unless _reflect_of(k)
|
34
|
+
define_singleton_method _reflect_of(k) do
|
35
|
+
model = i_am_i_can.send("#{k}_model")
|
36
|
+
raise NoMethodError unless (reflect_name = model._reflect_of(i_am_i_can.act))
|
37
|
+
model.send("with_#{reflect_name}").where(
|
38
|
+
self.name.underscore.pluralize => { id: self.ids }
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def assignment_helpers
|
46
|
+
# Generate 4 methods for each Content of Assignment
|
47
|
+
#
|
48
|
+
# Example for a subject model called User, which `has_and_belongs_to_many :stored_roles`.
|
49
|
+
# You call this proc by given contents [:role], then:
|
50
|
+
#
|
51
|
+
# 1. stored_roles_add
|
52
|
+
# Add roles to a user instance
|
53
|
+
#
|
54
|
+
# 2. stored_roles_add
|
55
|
+
# Remove roles to a user instance
|
56
|
+
#
|
57
|
+
# 3. stored_role_names
|
58
|
+
# Get names of stored_roles of a user instance
|
59
|
+
#
|
60
|
+
# 4. self.stored_role_names
|
61
|
+
# Get names of stored_roles of User ActiveRecord::Relation
|
62
|
+
#
|
63
|
+
proc do |contents|
|
64
|
+
contents.each do |content|
|
65
|
+
# TODO: refactoring
|
66
|
+
define_method "#{_reflect_of(content)}_add" do |locate_vals = nil, check_size: nil, **condition|
|
67
|
+
condition = { name: locate_vals } if locate_vals
|
68
|
+
assoc = send("_#{content.to_s.pluralize}")
|
69
|
+
records = i_am_i_can.send("#{content}_model").where(condition).where.not(id: assoc.ids)
|
70
|
+
# will return false if it does nothing
|
71
|
+
return false if records.blank? || (check_size && records.count != check_size)
|
72
|
+
assoc << records
|
73
|
+
end
|
74
|
+
|
75
|
+
define_method "#{_reflect_of(content)}_rmv" do |locate_vals = nil, check_size: nil, **condition|
|
76
|
+
condition = { name: locate_vals } if locate_vals
|
77
|
+
assoc = send("_#{content.to_s.pluralize}")
|
78
|
+
records = i_am_i_can.send("#{content}_model").where(id: assoc.ids, **condition)
|
79
|
+
# will return false if it does nothing
|
80
|
+
return false if records.blank? || (check_size && records.count != check_size)
|
81
|
+
assoc.destroy(records)
|
82
|
+
end
|
83
|
+
|
84
|
+
define_method "#{_reflect_of(content).to_s.singularize}_names" do
|
85
|
+
send("_#{content.to_s.pluralize}").map(&:name).map(&:to_sym)
|
86
|
+
end
|
87
|
+
|
88
|
+
define_singleton_method "#{_reflect_of(content).to_s.singularize}_names" do
|
89
|
+
all.flat_map { |user| user.send("#{_reflect_of(content).to_s.singularize}_name") }.uniq
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|