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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +13 -0
  5. data/CHANGELOG.md +17 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +116 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +442 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/i_am_i_can.gemspec +36 -0
  15. data/lib/generators/i_am_i_can/setup_generator.rb +56 -0
  16. data/lib/generators/i_am_i_can/templates/migrations/add_to_subject.erb +5 -0
  17. data/lib/generators/i_am_i_can/templates/migrations/permission.erb +15 -0
  18. data/lib/generators/i_am_i_can/templates/migrations/role.erb +13 -0
  19. data/lib/generators/i_am_i_can/templates/migrations/role_group.erb +14 -0
  20. data/lib/generators/i_am_i_can/templates/models/permission.erb +11 -0
  21. data/lib/generators/i_am_i_can/templates/models/role.erb +10 -0
  22. data/lib/generators/i_am_i_can/templates/models/role_group.erb +11 -0
  23. data/lib/i_am_i_can/config.rb +14 -0
  24. data/lib/i_am_i_can/has_an_array_of.rb +75 -0
  25. data/lib/i_am_i_can/permission/assignment.rb +90 -0
  26. data/lib/i_am_i_can/permission/definition.rb +61 -0
  27. data/lib/i_am_i_can/permission/helpers.rb +46 -0
  28. data/lib/i_am_i_can/permission/p_array.rb +22 -0
  29. data/lib/i_am_i_can/permission.rb +68 -0
  30. data/lib/i_am_i_can/role/assignment.rb +76 -0
  31. data/lib/i_am_i_can/role/definition.rb +100 -0
  32. data/lib/i_am_i_can/role/helpers.rb +28 -0
  33. data/lib/i_am_i_can/subject/permission_querying.rb +69 -0
  34. data/lib/i_am_i_can/subject/role_querying.rb +66 -0
  35. data/lib/i_am_i_can/version.rb +3 -0
  36. data/lib/i_am_i_can.rb +61 -0
  37. 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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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,5 @@
1
+ class IAmICanAddRoleIdsTo<%= name.camelize %> < ActiveRecord::Migration::Current
2
+ def change
3
+ add_column :<%= "#{name.underscore.pluralize}" %>, :role_ids, :integer, array: true, default: [ ]
4
+ end
5
+ 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 %>Permission < ActiveRecord::Base
2
+ end
3
+
4
+ __END__
5
+
6
+ string :pred, null: false
7
+ string :obj_type
8
+ integer :obj_id
9
+ string :desc
10
+
11
+ index %i[pred obj_type obj_id], unique: true, name: 'permission_unique_index'
@@ -0,0 +1,10 @@
1
+ class <%= name.camelize %>Role < ActiveRecord::Base
2
+ end
3
+
4
+ __END__
5
+
6
+ string :name, null: false
7
+ integer :permission_ids, array: true, default: [ ]
8
+ string :desc
9
+
10
+ index :name, unique: true, name: 'role_unique_index'
@@ -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