i_am_i_can 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](https://travis-ci.org/zhandao/i_am_i_can)
|
4
|
+
[](https://codeclimate.com/github/zhandao/i_am_i_can/maintainability)
|
5
|
+
[](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
|