i_am_i_can 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +236 -174
  4. data/lib/generators/i_am_i_can/setup_generator.rb +3 -7
  5. data/lib/generators/i_am_i_can/templates/migrations/i_am_i_can.erb +8 -8
  6. data/lib/generators/i_am_i_can/templates/models/permission.erb +4 -2
  7. data/lib/generators/i_am_i_can/templates/models/role.erb +3 -1
  8. data/lib/generators/i_am_i_can/templates/models/role_group.erb +3 -1
  9. data/lib/i_am_i_can/configs/config.rb +2 -3
  10. data/lib/i_am_i_can/configs/configs.rb +1 -7
  11. data/lib/i_am_i_can/helpers/dynamic.rb +102 -0
  12. data/lib/i_am_i_can/helpers/result_of.rb +71 -0
  13. data/lib/i_am_i_can/permission/assignment.rb +12 -60
  14. data/lib/i_am_i_can/permission/definition.rb +8 -28
  15. data/lib/i_am_i_can/permission.rb +18 -24
  16. data/lib/i_am_i_can/resource.rb +24 -19
  17. data/lib/i_am_i_can/role/assignment.rb +24 -45
  18. data/lib/i_am_i_can/role/definition.rb +17 -47
  19. data/lib/i_am_i_can/role.rb +14 -0
  20. data/lib/i_am_i_can/role_group/assignment.rb +6 -0
  21. data/lib/i_am_i_can/role_group/definition.rb +25 -0
  22. data/lib/i_am_i_can/subject/permission_querying.rb +28 -40
  23. data/lib/i_am_i_can/subject/role_querying.rb +8 -8
  24. data/lib/i_am_i_can/subject.rb +0 -10
  25. data/lib/i_am_i_can/support/association_class_methods.rb +43 -0
  26. data/lib/i_am_i_can/{configurable.rb → support/configurable.rb} +0 -7
  27. data/lib/i_am_i_can/support/reflection.rb +36 -0
  28. data/lib/i_am_i_can/version.rb +1 -1
  29. data/lib/i_am_i_can.rb +33 -16
  30. metadata +9 -9
  31. data/Gemfile.lock +0 -121
  32. data/lib/i_am_i_can/dynamic_generate.rb +0 -95
  33. data/lib/i_am_i_can/permission/helpers.rb +0 -75
  34. data/lib/i_am_i_can/permission/p_array.rb +0 -22
  35. data/lib/i_am_i_can/reflection.rb +0 -25
  36. data/lib/i_am_i_can/role/helpers.rb +0 -76
@@ -1,95 +0,0 @@
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
@@ -1,75 +0,0 @@
1
- module IAmICan
2
- module Permission
3
- module Helpers
4
- module Cls
5
- def _pms_definition_result(preds, obj, failed_items)
6
- prefix = 'Permission Definition Done'
7
- fail_msg = prefix + ", but #{failed_items} have been defined" if failed_items.present?
8
- raise Error, fail_msg if i_am_i_can.strict_mode && fail_msg
9
- puts fail_msg || prefix unless ENV['ITEST']
10
- prefix.present?
11
- end
12
-
13
- def _to_store_permission(pred, obj, **options)
14
- return false if i_am_i_can.permission_model.exists?(pred, obj)
15
- i_am_i_can.permission_model.create!(pred: pred, **deconstruct_obj(obj), **options)
16
- end
17
-
18
- def pms_naming(pred, obj)
19
- i_am_i_can.permission_model.naming(pred, obj)
20
- end
21
-
22
- def deconstruct_obj(obj)
23
- i_am_i_can.permission_model.deconstruct_obj(obj)
24
- end
25
-
26
- def defined_local_permissions
27
- @defined_local_permissions ||= { }
28
- end
29
-
30
- def defined_stored_pms_names
31
- i_am_i_can.permission_model.all.map(&:name)
32
- end
33
-
34
- def defined_stored_permissions
35
- i_am_i_can.permission_model.all.map { |pms| [ pms.name, pms.desc ] }.to_h
36
- end
37
-
38
- def defined_permissions
39
- defined_local_permissions.deep_merge(defined_stored_permissions)
40
- end
41
-
42
- def pms_of_defined_local_role(role_name)
43
- i_am_i_can.subject_model.defined_local_roles[role_name.to_sym]&.[](:permissions) || []
44
- end
45
- end
46
-
47
- module Ins
48
- def _pms_assignment_result(preds, obj, not_defined_items, covered_items = nil, strict_mode = false)
49
- prefix = 'Permission Assignment Done'
50
- msg1 = "#{not_defined_items} have not been defined or have been repeatedly assigned" if not_defined_items.present?
51
- msg2 = "#{covered_items} have been covered" if covered_items.present?
52
- fail_msg = prefix + ', but ' + [msg1, msg2].compact.join(', ') if msg1 || msg2
53
- raise Error, fail_msg if (strict_mode || i_am_i_can.strict_mode) && fail_msg
54
- puts fail_msg || prefix unless ENV['ITEST']
55
- prefix.present?
56
- end
57
-
58
- def pms_matched?(pms_name, plist)
59
- i_am_i_can.permission_model.matched?(pms_name, in: plist[:in])
60
- end
61
-
62
- def local_permissions
63
- @local_permissions ||= [ ]
64
- end
65
-
66
- alias local_permission_names local_permissions
67
-
68
- # TODO: show by hash
69
- def permissions
70
- local_permission_names + stored_permission_names
71
- end
72
- end
73
- end
74
- end
75
- end
@@ -1,22 +0,0 @@
1
- module IAmICan
2
- module Permission
3
- class PArray < ::Array
4
- attr_accessor :pms
5
-
6
- def matched?(pms_name)
7
- return false if self.blank?
8
- self.pms = pms_name.to_sym
9
- found? || covered?
10
- end
11
-
12
- def found?
13
- pms.in? self
14
- end
15
-
16
- def covered?
17
- pred, obj_type, obj_id = pms.to_s.split('_')
18
- pred.to_sym.in?(self) || :"#{pred}_#{obj_type}".in?(self)
19
- end
20
- end
21
- end
22
- end
@@ -1,25 +0,0 @@
1
- module IAmICan
2
- module Reflection
3
- extend ActiveSupport::Concern
4
-
5
- class_methods do
6
- # User._roles => 'stored_roles'
7
- %w[ subjects roles role_groups permissions ].each do |k|
8
- define_method "_#{k}" do
9
- v = instance_variable_get("@_#{k}")
10
- return v if v.present?
11
- instance_variable_set("@_#{k}", _reflect_of(k.singularize))
12
- end
13
- end
14
- end
15
-
16
- included do
17
- # user._roles => Association CollectionProxy, same as: `user.stored_roles`
18
- %w[ subjects roles role_groups permissions ].each do |k|
19
- define_method "_#{k}" do
20
- send(self.class.send("_#{k}")) if self.class.send("_#{k}")
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,76 +0,0 @@
1
- module IAmICan
2
- module Role
3
- module Helpers
4
- module Cls
5
- def _to_store_role name, **options
6
- return false if i_am_i_can.role_model.exists?(name: name) || i_am_i_can.role_group_model&.exists?(name: name)
7
- i_am_i_can.role_model.create!(name: name, **options)
8
- end
9
-
10
- def _role_definition_result(names, failed_items)
11
- prefix = 'Role Definition Done'
12
- fail_msg = prefix + ", but name #{failed_items} have been used by other role or group" if failed_items.present?
13
- raise Error, fail_msg if i_am_i_can.strict_mode && fail_msg
14
- puts fail_msg || prefix unless ENV['ITEST']
15
- prefix.present?
16
- end
17
-
18
- def defined_local_roles
19
- @local_roles ||= { }
20
- end
21
-
22
- def defined_stored_role_names
23
- i_am_i_can.role_model.pluck(:name).map(&:to_sym)
24
- end
25
-
26
- def defined_stored_roles
27
- i_am_i_can.role_model.all.map { |role| [ role.name.to_sym, role.desc ] }.to_h
28
- end
29
-
30
- def defined_roles
31
- defined_local_roles.deep_merge(defined_stored_roles)
32
- end
33
-
34
- def defined_role_group_names
35
- i_am_i_can.role_group_model.pluck(:name).map(&:to_sym)
36
- end
37
-
38
- def defined_role_groups
39
- i_am_i_can.role_group_model.all.map { |group| [ group.name.to_sym, group.member_names.map(&:to_sym).sort ] }.to_h
40
- end
41
- end
42
-
43
- module Ins
44
- def _role_assignment_result(names, failed_items)
45
- prefix = 'Role Assignment Done'
46
- fail_msg = prefix + ", but #{failed_items} have not been defined or have been repeatedly assigned" if failed_items.present?
47
- raise Error, fail_msg if i_am_i_can.strict_mode && fail_msg
48
- puts fail_msg || prefix unless ENV['ITEST']
49
- prefix.present?
50
- end
51
-
52
- def __role
53
- proc do |role|
54
- next role.to_sym if role.is_a?(String) || role.is_a?(Symbol)
55
- next role.name if role.is_a?(i_am_i_can.role_model)
56
- # raise error
57
- end
58
- end
59
-
60
- def local_role_names
61
- @local_role_names ||= [ ]
62
- end
63
-
64
- def local_roles
65
- defined_local_roles.slice(*local_role_names)
66
- end
67
-
68
- def roles
69
- local_role_names + stored_role_names
70
- end
71
-
72
- alias role_names roles
73
- end
74
- end
75
- end
76
- end