i_am_i_can 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +17 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +116 -0
- data/LICENSE.txt +21 -0
- data/README.md +442 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/i_am_i_can.gemspec +36 -0
- data/lib/generators/i_am_i_can/setup_generator.rb +56 -0
- data/lib/generators/i_am_i_can/templates/migrations/add_to_subject.erb +5 -0
- data/lib/generators/i_am_i_can/templates/migrations/permission.erb +15 -0
- data/lib/generators/i_am_i_can/templates/migrations/role.erb +13 -0
- data/lib/generators/i_am_i_can/templates/migrations/role_group.erb +14 -0
- data/lib/generators/i_am_i_can/templates/models/permission.erb +11 -0
- data/lib/generators/i_am_i_can/templates/models/role.erb +10 -0
- data/lib/generators/i_am_i_can/templates/models/role_group.erb +11 -0
- data/lib/i_am_i_can/config.rb +14 -0
- data/lib/i_am_i_can/has_an_array_of.rb +75 -0
- data/lib/i_am_i_can/permission/assignment.rb +90 -0
- data/lib/i_am_i_can/permission/definition.rb +61 -0
- data/lib/i_am_i_can/permission/helpers.rb +46 -0
- data/lib/i_am_i_can/permission/p_array.rb +22 -0
- data/lib/i_am_i_can/permission.rb +68 -0
- data/lib/i_am_i_can/role/assignment.rb +76 -0
- data/lib/i_am_i_can/role/definition.rb +100 -0
- data/lib/i_am_i_can/role/helpers.rb +28 -0
- data/lib/i_am_i_can/subject/permission_querying.rb +69 -0
- data/lib/i_am_i_can/subject/role_querying.rb +66 -0
- data/lib/i_am_i_can/version.rb +3 -0
- data/lib/i_am_i_can.rb +61 -0
- metadata +220 -0
data/README.md
ADDED
@@ -0,0 +1,442 @@
|
|
1
|
+
# IAmICan [PostgreSQL only currently]
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/zhandao/i_am_i_can.svg?branch=master)](https://travis-ci.org/zhandao/i_am_i_can)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/27b664da01b6cc7180e3/maintainability)](https://codeclimate.com/github/zhandao/i_am_i_can/maintainability)
|
5
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/27b664da01b6cc7180e3/test_coverage)](https://codeclimate.com/github/zhandao/i_am_i_can/test_coverage)
|
6
|
+
|
7
|
+
Concise and Natural DSL for `Subject - Role(Role Group) - Permission` Management.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# our Subject is People, and subject is he:
|
11
|
+
he = People.take
|
12
|
+
# let: Roles means PeopleRole, Groups means PeopleRoleGroup
|
13
|
+
|
14
|
+
# Role
|
15
|
+
People.have_role :admin # role definition
|
16
|
+
he.becomes_a :admin # role assignment
|
17
|
+
he.is? :admin # role querying => true
|
18
|
+
he.is? :someone_else # role querying => false
|
19
|
+
|
20
|
+
# Role Group
|
21
|
+
# role definition and grouping
|
22
|
+
People.have_and_group_roles :dev, :master, :committer, by_name: :team
|
23
|
+
he.becomes_a :master # role assignment
|
24
|
+
he.in_role_group? :team # role group querying => true
|
25
|
+
|
26
|
+
# Role - Permission
|
27
|
+
People.have_role :coder # role definition
|
28
|
+
Roles.have_permission :fly # permission definition
|
29
|
+
Roles.which(name: :coder).can :fly # permission assignment (by predicate)
|
30
|
+
he.becomes_a :coder # role assignment
|
31
|
+
he.can? :fly # permission querying
|
32
|
+
|
33
|
+
# Role Group - Permission
|
34
|
+
Groups.have_permission :manage, obj: User # permission definition
|
35
|
+
Groups.which(name: :team).can :manage, obj: User # permission assignment (by predicate and object)
|
36
|
+
he.is? :master # yes
|
37
|
+
he.can? :manage, User # permission querying
|
38
|
+
|
39
|
+
# more concise and faster way
|
40
|
+
he.becomes_a :magician, which_can: [:perform], obj: :magic
|
41
|
+
he.is? :magician # => true
|
42
|
+
Roles.which(name: :magician).can? :perform, :magic # => true
|
43
|
+
he.can? :perform, :magic # => true
|
44
|
+
|
45
|
+
# Cancel Assignment
|
46
|
+
he.falls_from :admin
|
47
|
+
Roles.which(name: :coder).cannot :fly
|
48
|
+
```
|
49
|
+
|
50
|
+
## Concepts and Overview
|
51
|
+
|
52
|
+
### Definition and uniqueness of nouns
|
53
|
+
|
54
|
+
1. Role
|
55
|
+
- definition: TODO
|
56
|
+
- uniqueness: by `name`
|
57
|
+
1. Role Group
|
58
|
+
- definition: TODO
|
59
|
+
- uniqueness: by `name`
|
60
|
+
1. Permission
|
61
|
+
- definition: TODO
|
62
|
+
- uniqueness: by `predicate + object` (name)
|
63
|
+
|
64
|
+
|
65
|
+
### In one word:
|
66
|
+
```
|
67
|
+
- role has permissions
|
68
|
+
- subject has the roles
|
69
|
+
> subject has the permissions through the roles.
|
70
|
+
```
|
71
|
+
|
72
|
+
### About role group?
|
73
|
+
```
|
74
|
+
- role group has permissions
|
75
|
+
- roles are in the group
|
76
|
+
- subject has one or more of the roles
|
77
|
+
> subject has the permissions through the role which is in the group
|
78
|
+
```
|
79
|
+
|
80
|
+
### Three steps of this gem
|
81
|
+
|
82
|
+
1. Querying
|
83
|
+
- Find if the given role is assigned to the subject
|
84
|
+
- Find if the given permission is assigned to the subject's roles / group
|
85
|
+
- instance methods, like: `user.can? :fly`
|
86
|
+
2. Assignment
|
87
|
+
- assign role to subject, or assign permission to role / group
|
88
|
+
- instance methods, like: `user.has_role :admin`
|
89
|
+
3. Definition
|
90
|
+
- the role or permission you want to assign **MUST** be defined before
|
91
|
+
- option :auto_define_before (before assignment) you may need in some cases
|
92
|
+
- class methods, like: `UserRoleGroup.have_permission :fly`
|
93
|
+
|
94
|
+
**Definition => Assignment => Querying**
|
95
|
+
|
96
|
+
### Two Concepts of this gem
|
97
|
+
|
98
|
+
1. Stored (save in database)
|
99
|
+
2. Local (variable value)
|
100
|
+
|
101
|
+
[Feature List: needs you](https://github.com/zhandao/i_am_i_can/issues/2)
|
102
|
+
|
103
|
+
## Installation And Setup
|
104
|
+
|
105
|
+
1. Add this line to your application's Gemfile and then `bundle`:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
gem 'i_am_i_can'
|
109
|
+
```
|
110
|
+
|
111
|
+
2. Generate migrations and models by your subject name:
|
112
|
+
|
113
|
+
```bash
|
114
|
+
rails g i_am_i_can:setup <subject_name>
|
115
|
+
```
|
116
|
+
|
117
|
+
For example, if your subject name is `user`, it will generate
|
118
|
+
model `UserRole`, `UserRoleGroup` and `UserPermission`
|
119
|
+
|
120
|
+
3. run `rails db:migrate`
|
121
|
+
|
122
|
+
4. enable it in your subject model, like:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
class User
|
126
|
+
act_as_i_am_i_can
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
[here](#options) is some options you can pass to the declaration.
|
131
|
+
|
132
|
+
That's all!
|
133
|
+
|
134
|
+
## Usage
|
135
|
+
|
136
|
+
### Options
|
137
|
+
|
138
|
+
TODO
|
139
|
+
|
140
|
+
### Methods and their Aliases
|
141
|
+
|
142
|
+
#### A. [Role Definition](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/role/definition.rb)
|
143
|
+
|
144
|
+
1. Caller: Subject Model, like `User`
|
145
|
+
2. methods:
|
146
|
+
1. save to database: `have_role`. aliases:
|
147
|
+
1. `have_roles`
|
148
|
+
2. `has_role` & `has_roles`
|
149
|
+
2. save to local variable: `declare_role`. alias `declare_roles`
|
150
|
+
3. helpers:
|
151
|
+
1. `defined_local_roles`
|
152
|
+
2. `defined_stored_roles` & `defined_stored_role_names`
|
153
|
+
3. `defined_roles`
|
154
|
+
|
155
|
+
Methods Explanation:
|
156
|
+
```ruby
|
157
|
+
# === Save to DB ===
|
158
|
+
# method signature
|
159
|
+
have_role *names, desc: nil, save: default_save#, which_can: [ ], obj: nil
|
160
|
+
# examples
|
161
|
+
User.have_roles :admin, :master # => 'Role Definition Done' or error message
|
162
|
+
User.defined_stored_roles.keys.count # => 2
|
163
|
+
|
164
|
+
# === Save in Local ===
|
165
|
+
# signature as `have_role`
|
166
|
+
# examples
|
167
|
+
User.declare_role :coder # => 'Role Definition Done' or error message
|
168
|
+
User.defined_local_roles.keys.count # => 1
|
169
|
+
|
170
|
+
User.defined_roles.keys.count # => 3
|
171
|
+
```
|
172
|
+
|
173
|
+
#### B. [Grouping Roles](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/role/definition.rb)
|
174
|
+
|
175
|
+
**Tips:**
|
176
|
+
1. Role Group must be saved in database currently
|
177
|
+
2. Roles that you're going to group should be defined
|
178
|
+
|
179
|
+
Overview:
|
180
|
+
1. Caller: Subject Model, like `User`
|
181
|
+
2. method: `group_roles`. aliases:
|
182
|
+
1. `group_role`
|
183
|
+
2. `groups_role` & `groups_roles`
|
184
|
+
3. shortcut combination method: `have_and_group_roles` (alias `has_and_groups_roles`)
|
185
|
+
it will do: roles definition => roles grouping
|
186
|
+
4. helpers:
|
187
|
+
1. `defined_role_groups` & `defined_role_group_names`
|
188
|
+
2. `members_of_role_group`
|
189
|
+
|
190
|
+
Methods Explanation:
|
191
|
+
```ruby
|
192
|
+
# method signature
|
193
|
+
group_roles *members, by_name:, #which_can: [ ], obj: nil
|
194
|
+
# examples
|
195
|
+
User.have_and_group_roles :vip1, :vip2, :vip3, by_name: :vip
|
196
|
+
User.defined_role_group_names # => [:vip]
|
197
|
+
User.members_of_role_group(:vip) # => %i[vip1 vip2 vip3]
|
198
|
+
```
|
199
|
+
|
200
|
+
#### C. [Role Assignment](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/role/definition.rb)
|
201
|
+
|
202
|
+
1. Caller: subject instance, like `User.find(1)`
|
203
|
+
2. assign methods:
|
204
|
+
1. save to database: `becomes_a`. aliases:
|
205
|
+
1. `is` & `is_a_role` & `is_roles`
|
206
|
+
2. `has_role` & `has_roles`
|
207
|
+
3. `role_is` & `role_are`
|
208
|
+
2. save to local variable: `temporarily_is`. alias `locally_is`
|
209
|
+
3. cancel assign method: `falls_from`. aliases:
|
210
|
+
1. `removes_role`
|
211
|
+
2. `leaves`
|
212
|
+
3. `is_not_a` & `has_not_role` & `has_not_roles`
|
213
|
+
4. `will_not_be`
|
214
|
+
4. helpers:
|
215
|
+
1. `local_roles` & `local_role_names`
|
216
|
+
2. `stored_roles` & `stored_role_names`
|
217
|
+
3. `roles`
|
218
|
+
|
219
|
+
Methods Explanation:
|
220
|
+
```ruby
|
221
|
+
he = User.take
|
222
|
+
# === Save to DB ===
|
223
|
+
# method signature
|
224
|
+
becomes_a *roles, auto_define_before: auto_define_before, save: default_save#, which_can: [ ], obj: nil
|
225
|
+
# examples
|
226
|
+
he.becomes_a :admin # => 'Role Definition Done' or error message
|
227
|
+
he.stored_roles # => [<#UserRole id: 1>]
|
228
|
+
|
229
|
+
# === Save in Local ===
|
230
|
+
# signature as `becomes_a`
|
231
|
+
# examples
|
232
|
+
he.temporarily_is :coder # => 'Role Assignment Done' or error message
|
233
|
+
he.local_roles # => [{ coder: { .. } }]
|
234
|
+
|
235
|
+
he.roles # => [:admin, :coder]
|
236
|
+
|
237
|
+
# === Cancel ===
|
238
|
+
# method signature
|
239
|
+
falls_from *roles, saved: default_save
|
240
|
+
# examples
|
241
|
+
he.falls_from :admin # => 'Role Assignment Done' or error message
|
242
|
+
he.removes_roles :coder, saved: false # => 'Role Assignment Done' or error message
|
243
|
+
he.roles # => []
|
244
|
+
```
|
245
|
+
|
246
|
+
#### D. [Role / Group Querying](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/subject/role_querying.rb)
|
247
|
+
|
248
|
+
1. Caller: subject instance, like `User.find(1)`
|
249
|
+
2. role querying methods:
|
250
|
+
1. `is?` / `is_role?` / `has_role?`
|
251
|
+
2. `isnt?`
|
252
|
+
3. `is!` / `is_role!` / `has_role!`
|
253
|
+
4. `is_one_of?` / `is_one_of_roles?`
|
254
|
+
4. `is_one_of!` / `is_one_of_roles!`
|
255
|
+
5. `is_every?` / `is_every_role_in?`
|
256
|
+
6. `is_every!` / `is_every_role_in!`
|
257
|
+
3. group querying methods:
|
258
|
+
1. `is_in_role_group?` / `in_role_group?`
|
259
|
+
2. `is_in_one_of?` / `in_one_of?`
|
260
|
+
|
261
|
+
all the `?` methods will return `true` or `false`
|
262
|
+
all the `!` bang methods will return `true` or raise `IAmICan::VerificationFailed`
|
263
|
+
|
264
|
+
Methods Examples:
|
265
|
+
```ruby
|
266
|
+
he = User.take
|
267
|
+
|
268
|
+
he.is? :admin
|
269
|
+
he.isnt? :admin
|
270
|
+
he.is! :admin
|
271
|
+
|
272
|
+
he.is_every? :admin, :master # return false if he is not a admin or master
|
273
|
+
he.is_one_of! :admin, :master # return true if he is a master or admin
|
274
|
+
|
275
|
+
he.is_in_role_group? :vip # return true if he has a role which is in the group :vip
|
276
|
+
```
|
277
|
+
|
278
|
+
#### E. [Permission Definition](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/permission/definition.rb)
|
279
|
+
|
280
|
+
1. Caller: Role / Role Group Model, like `UserRole` / `UserRoleGroup`
|
281
|
+
2. methods:
|
282
|
+
1. save to database: `have_permission`. aliases:
|
283
|
+
1. `have_permissions`
|
284
|
+
2. `has_permission` & `has_permissions`
|
285
|
+
2. save to local variable: `declare_permission`. alias `declare_permissions`
|
286
|
+
3. helpers:
|
287
|
+
1. `defined_local_permissions`
|
288
|
+
2. `defined_stored_permissions`
|
289
|
+
3. `defined_permissions`
|
290
|
+
4. class method: `which(name:)`
|
291
|
+
5. Permission
|
292
|
+
1. class method: `which(pred:, obj:)`
|
293
|
+
2. instance methods: `#pred`, `#obj`, `#name`
|
294
|
+
|
295
|
+
Methods Explanation:
|
296
|
+
```ruby
|
297
|
+
# === Save to DB ===
|
298
|
+
# method signature
|
299
|
+
have_permission *preds, obj: nil, desc: nil, save: default_save
|
300
|
+
# examples
|
301
|
+
UserRole.have_permission :fly # => 'Permission Definition Done' or error message
|
302
|
+
UserRole.defined_stored_permissions.keys.count # => 1
|
303
|
+
UserRoleGroup.have_permissions *%i[read write], obj: Book.find(1) # => 'Permission Definition Done' or error message
|
304
|
+
UserRoleGroup.defined_stored_permissions.keys.count # => 1
|
305
|
+
|
306
|
+
# === Save in Local ===
|
307
|
+
# signature as `have_permission`
|
308
|
+
# examples
|
309
|
+
UserRole.declare_permission :perform, obj: :magic # => 'Permission Definition Done' or error message
|
310
|
+
UserRole.defined_local_permissions.keys.count # => 1
|
311
|
+
|
312
|
+
UserRole.defined_permissions.keys.count # => 2
|
313
|
+
|
314
|
+
# === class methods ===
|
315
|
+
UserRole.which(name: :admin)
|
316
|
+
# as same as
|
317
|
+
UserRole.find_by_name!(:admin)
|
318
|
+
|
319
|
+
# === Permission ===
|
320
|
+
p = UserPermission.which(pred: :read, obj: Book.find(1))
|
321
|
+
p.pred == 'read'
|
322
|
+
p.obj == Book.find(1)
|
323
|
+
p.name == :read_Book_1
|
324
|
+
```
|
325
|
+
|
326
|
+
#### F. [Permission Assignment](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/permission/assignment.rb)
|
327
|
+
|
328
|
+
**What is Wrong Assignment - Covered?**
|
329
|
+
> Before: he can manage User
|
330
|
+
> When you do: he can manage User.find(1)
|
331
|
+
> will get an Error, tell you that User is cover User.find(1), no need to assign
|
332
|
+
|
333
|
+
Overview:
|
334
|
+
1. Caller: role / role group instance, like `UserRole.which(name: :admin)`
|
335
|
+
2. methods:
|
336
|
+
1. save to database: `can`. aliases: `has_permission`
|
337
|
+
2. save to local variable: `temporarily_can`. alias `locally_can`
|
338
|
+
3. cancel assign method: `cannot`. alias `is_not_allowed_to`
|
339
|
+
3. helpers:
|
340
|
+
1. `local_permissions`
|
341
|
+
2. `stored_permissions`
|
342
|
+
3. `permissions`
|
343
|
+
|
344
|
+
Methods Explanation:
|
345
|
+
```ruby
|
346
|
+
role = UserRole.which(name: :admin)
|
347
|
+
|
348
|
+
# === Save to DB ===
|
349
|
+
# method signature
|
350
|
+
can *preds, obj: nil, strict_mode: false, auto_define_before: auto_define_before
|
351
|
+
# examples
|
352
|
+
role.can :fly # => 'Permission Assignment Done' or error message
|
353
|
+
role.stored_permissions # => [<#UserPermission id: ..>]
|
354
|
+
|
355
|
+
# === Save in Local
|
356
|
+
# signature as `can`
|
357
|
+
# examples
|
358
|
+
role.temporarily_can :perform, obj: :magic # => 'Permission Assignment Done' or error message
|
359
|
+
role.local_permissions # => [:perform_magic]
|
360
|
+
|
361
|
+
role.permissions.keys.count # => 3
|
362
|
+
```
|
363
|
+
|
364
|
+
#### G. [Permission Querying](https://github.com/zhandao/i_am_i_can/blob/master/lib/i_am_i_can/subject/role_querying.rb)
|
365
|
+
|
366
|
+
1. Caller:
|
367
|
+
1. subject instance, like `User.find(1)`
|
368
|
+
2. role / role group instance, like `Role.which(name: :master)`
|
369
|
+
notice that this caller have only `can?` and `temporarily_can?` methods.
|
370
|
+
2. methods:
|
371
|
+
1. `can?`
|
372
|
+
2. `cannot?`
|
373
|
+
3. `can!`
|
374
|
+
4. `can_each?` & `can_each!`
|
375
|
+
4. `can_one_of!` & `can_one_of!`
|
376
|
+
5. `temporarily_can?` / `locally_can?`
|
377
|
+
6. `stored_can?`
|
378
|
+
7. `group_can?`
|
379
|
+
3. helpers:
|
380
|
+
1. `permissions_of_stored_roles`
|
381
|
+
2. `permissions_of_local_roles`
|
382
|
+
3. `permissions_of_role_groups`
|
383
|
+
|
384
|
+
all the `?` methods will return `true` or `false`
|
385
|
+
all the `!` bang methods will return `true` or raise `IAmICan::InsufficientPermission`
|
386
|
+
|
387
|
+
Methods Examples:
|
388
|
+
```ruby
|
389
|
+
he = User.take
|
390
|
+
|
391
|
+
he.can? :perform, :magic
|
392
|
+
he.cannot? :perform, :magic
|
393
|
+
he.can! :perform, :magic
|
394
|
+
|
395
|
+
he.can_each? :fly, :jump # return false if he can not fly or jump
|
396
|
+
he.can_one_of! :fly, :jump # return true if he can fly or jump
|
397
|
+
```
|
398
|
+
|
399
|
+
#### H. Shortcut Combinations - which_can
|
400
|
+
|
401
|
+
Faster way to assign, define roles and thier permissions.
|
402
|
+
You can use it when defining role even assigning role.
|
403
|
+
|
404
|
+
```ruby
|
405
|
+
# === use when defining role ===
|
406
|
+
# it does:
|
407
|
+
# 1. define the role to Subject Model
|
408
|
+
# 2. define & assign the permission to the role
|
409
|
+
User.have_role :coder, which_can: [:perform], obj: :magic
|
410
|
+
UserRole.which(name: :coder).can? :perform, :magic # => true
|
411
|
+
# save in local
|
412
|
+
User.local_role_which(name: :local_role, can: [:perform], obj: :magic)
|
413
|
+
UserRole.new(name: :local_role).temporarily_can? :perform, :magic # => true
|
414
|
+
|
415
|
+
# === use when assigning role ===
|
416
|
+
# it does:
|
417
|
+
# 1. define the role to Subject Model
|
418
|
+
# 2. assign the role to subject instance
|
419
|
+
# 2. define & assign the permission to the role
|
420
|
+
user = User.take
|
421
|
+
user.becomes_a :master, which_can: [:read], obj: :book
|
422
|
+
user.is? :master # => true
|
423
|
+
user.can? :read, :book # => true
|
424
|
+
```
|
425
|
+
|
426
|
+
## Development
|
427
|
+
|
428
|
+
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.
|
429
|
+
|
430
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
431
|
+
|
432
|
+
## Contributing
|
433
|
+
|
434
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/i_am_i_can. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
435
|
+
|
436
|
+
## License
|
437
|
+
|
438
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
439
|
+
|
440
|
+
## Code of Conduct
|
441
|
+
|
442
|
+
Everyone interacting in the IAmICan project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/i_am_i_can/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "i_am_i_can"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/i_am_i_can.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "i_am_i_can/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "i_am_i_can"
|
8
|
+
spec.version = IAmICan::VERSION
|
9
|
+
spec.authors = ["zhandao"]
|
10
|
+
spec.email = ["x@skippingcat.com"]
|
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.'
|
14
|
+
spec.homepage = 'https://github.com/zhandao/i_am_i_can'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency 'rspec-rails'
|
28
|
+
spec.add_development_dependency 'database_cleaner'
|
29
|
+
spec.add_development_dependency 'pg'
|
30
|
+
spec.add_development_dependency 'simplecov'
|
31
|
+
|
32
|
+
|
33
|
+
spec.add_dependency 'activerecord'
|
34
|
+
spec.add_dependency 'activesupport'
|
35
|
+
spec.add_dependency 'railties'
|
36
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/named_base'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
require 'rails/generators/active_record'
|
5
|
+
|
6
|
+
module IAmICan
|
7
|
+
module Generators
|
8
|
+
class SetupGenerator < Rails::Generators::NamedBase
|
9
|
+
include Rails::Generators::Migration
|
10
|
+
|
11
|
+
desc 'Generates migrations and models for the subject'
|
12
|
+
|
13
|
+
source_root File.expand_path('../templates', __FILE__)
|
14
|
+
|
15
|
+
def questions
|
16
|
+
@ii_opts = { }
|
17
|
+
unless yes?('Do you want to use role group?')
|
18
|
+
@ii_opts[:without_group] = true
|
19
|
+
end
|
20
|
+
unless yes?('Do yo want it to save role and permission to database by default?')
|
21
|
+
@ii_opts[:default_save] = false
|
22
|
+
end
|
23
|
+
if yes?('Do you want it to raise error when you are doing wrong definition or assignment?')
|
24
|
+
@ii_opts[:strict_mode] = true
|
25
|
+
end
|
26
|
+
if yes?('Do you want it to define the role/permission which is not defined when assigning to subject?')
|
27
|
+
@ii_opts[:auto_define_before] = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup_migrations
|
32
|
+
dest_prefix = 'db/migrate/i_am_i_can_'
|
33
|
+
migration_template 'migrations/add_to_subject.erb', "#{dest_prefix}add_role_ids_to_#{name.underscore}.rb"
|
34
|
+
migration_template 'migrations/role.erb', "#{dest_prefix}create_#{name.underscore}_roles.rb"
|
35
|
+
migration_template 'migrations/role_group.erb', "#{dest_prefix}create_#{name.underscore}_role_groups.rb" unless @ii_opts[:without_group]
|
36
|
+
migration_template 'migrations/permission.erb', "#{dest_prefix}create_#{name.underscore}_permissions.rb"
|
37
|
+
end
|
38
|
+
|
39
|
+
def setup_models
|
40
|
+
template 'models/role.erb', "app/models/#{name.underscore}_role.rb"
|
41
|
+
template 'models/role_group.erb', "app/models/#{name.underscore}_role_group.rb" unless @ii_opts[:without_group]
|
42
|
+
template 'models/permission.erb', "app/models/#{name.underscore}_permission.rb"
|
43
|
+
end
|
44
|
+
|
45
|
+
def tip
|
46
|
+
options = ' ' + @ii_opts.to_s[2..-2].gsub('=>', ': ').gsub(', :', ', ') if @ii_opts.keys.present?
|
47
|
+
puts 'Please add this line to your subject model:'.red
|
48
|
+
puts " act_as_i_am_i_can#{options}".red
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.next_migration_number(dirname)
|
52
|
+
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class IAmICanCreate<%= "#{name.underscore}_permissions".camelize %> < ActiveRecord::Migration::Current
|
2
|
+
def change
|
3
|
+
create_table :<%= "#{name.underscore}_permissions" %>, force: :cascade do |t|
|
4
|
+
t.string :pred, null: false
|
5
|
+
t.string :obj_type
|
6
|
+
t.integer :obj_id
|
7
|
+
t.string :desc
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :<%= "#{name.underscore}_permissions" %>, %i[pred obj_type obj_id],
|
13
|
+
unique: true, name: 'permission_unique_index', using: :btree
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class IAmICanCreate<%= "#{name.underscore}_roles".camelize %> < ActiveRecord::Migration::Current
|
2
|
+
def change
|
3
|
+
create_table :<%= "#{name.underscore}_roles" %>, force: :cascade do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.integer :permission_ids, array: true, default: [ ]
|
6
|
+
t.string :desc
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :<%= "#{name.underscore}_roles" %>, :name, unique: true, name: 'role_unique_index', using: :btree
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class IAmICanCreate<%= "#{name.underscore}_role_groups".camelize %> < ActiveRecord::Migration::Current
|
2
|
+
def change
|
3
|
+
create_table :<%= "#{name.underscore}_role_groups" %>, force: :cascade do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.integer :member_ids, array: true, default: [ ]
|
6
|
+
t.integer :permission_ids, array: true, default: [ ]
|
7
|
+
t.string :desc
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :<%= "#{name.underscore}_role_groups" %>, :name, unique: true, name: 'role_group_unique_index', using: :btree
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class <%= name.camelize %>RoleGroup < ActiveRecord::Base
|
2
|
+
end
|
3
|
+
|
4
|
+
__END__
|
5
|
+
|
6
|
+
string :name, null: false
|
7
|
+
integer :member_ids, array: true, default: [ ]
|
8
|
+
integer :permission_ids, array: true, default: [ ]
|
9
|
+
string :desc
|
10
|
+
|
11
|
+
index :name, unique: true, name: 'role_group_unique_index'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module IAmICan
|
2
|
+
class Config
|
3
|
+
attr_accessor :subject_model, :role_model, :role_group_model, :permission_model,
|
4
|
+
:auto_define_before, :strict_mode, :without_group, :default_save
|
5
|
+
|
6
|
+
def initialize(**options)
|
7
|
+
self.auto_define_before = false
|
8
|
+
self.strict_mode = false
|
9
|
+
self.without_group = false
|
10
|
+
self.default_save = true
|
11
|
+
options.each { |(key, val)| self.send("#{key}=", val) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|