bali 2.0.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,10 +7,19 @@ class Bali::MapRulesDsl
7
7
  end
8
8
 
9
9
  # defining rules
10
- def rules_for(target_class, target_alias_hash = {}, &block)
10
+ def rules_for(target_class, options_hash = {}, &block)
11
11
  @@lock.synchronize do
12
+ raise Bali::DslError "rules_for must describe a target which is a class" unless target_class.is_a?(Class)
12
13
  self.current_rule_class = Bali::RuleClass.new(target_class)
13
- self.current_rule_class.alias_name = target_alias_hash[:as] || target_alias_hash["as"]
14
+
15
+ parent_class = options_hash[:inherits] || options_hash["inherits"]
16
+ if parent_class
17
+ # in case there is inherits specification
18
+ raise Bali::DslError, "inherits must take a class" unless parent_class.is_a?(Class)
19
+ rule_class_from_parent = Bali::Integrators::Rule.rule_class_for(parent_class)
20
+ raise Bali::DslError, "not yet defined a rule class for #{parent_class}" if rule_class_from_parent.nil?
21
+ self.current_rule_class = rule_class_from_parent.clone(target_class: target_class)
22
+ end
14
23
 
15
24
  Bali::RulesForDsl.new(self).instance_eval(&block)
16
25
 
@@ -50,6 +59,10 @@ class Bali::MapRulesDsl
50
59
  raise Bali::DslError, "can_all block must be within describe block"
51
60
  end
52
61
 
62
+ def clear_rules
63
+ raise Bali::DslError, "clear_rules must be called within describe block"
64
+ end
65
+
53
66
  def cant_all(*params)
54
67
  puts "Deprecation Warning: declaring rules with cant_all will be deprecated on major release 3.0, use cannot instead"
55
68
  cannot_all *params
@@ -14,32 +14,30 @@ class Bali::RulesForDsl
14
14
  end
15
15
 
16
16
  def describe(*params)
17
-
18
- subtargets = []
19
- rules = {}
20
-
21
- params.each do |passed_argument|
22
- if passed_argument.is_a?(Symbol) || passed_argument.is_a?(String)
23
- subtargets << passed_argument
24
- elsif passed_argument.is_a?(NilClass)
25
- subtargets << passed_argument
26
- elsif passed_argument.is_a?(Array)
27
- subtargets += passed_argument
28
- elsif passed_argument.is_a?(Hash)
29
- rules = passed_argument
30
- else
31
- raise Bali::DslError, "Allowed argument: symbol, string, nil and hash"
17
+ @@lock.synchronize do
18
+ subtargets = []
19
+ rules = {}
20
+
21
+ params.each do |passed_argument|
22
+ if passed_argument.is_a?(Symbol) || passed_argument.is_a?(String)
23
+ subtargets << passed_argument
24
+ elsif passed_argument.is_a?(NilClass)
25
+ subtargets << passed_argument
26
+ elsif passed_argument.is_a?(Array)
27
+ subtargets += passed_argument
28
+ elsif passed_argument.is_a?(Hash)
29
+ rules = passed_argument
30
+ else
31
+ raise Bali::DslError, "Allowed argument for describe: symbol, string, nil and hash"
32
+ end
32
33
  end
33
- end
34
34
 
35
- target_class = self.map_rules_dsl.current_rule_class.target_class
36
- target_alias = self.map_rules_dsl.current_rule_class.alias_name
35
+ target_class = self.map_rules_dsl.current_rule_class.target_class
37
36
 
38
- subtargets.each do |subtarget|
39
- @@lock.synchronize do
37
+ subtargets.each do |subtarget|
40
38
  rule_group = self.current_rule_class.rules_for(subtarget)
41
39
  if rule_group.nil?
42
- rule_group = Bali::RuleGroup.new(target_class, target_alias, subtarget)
40
+ rule_group = Bali::RuleGroup.new(target_class, subtarget)
43
41
  end
44
42
 
45
43
  self.current_rule_group = rule_group
@@ -64,12 +62,46 @@ class Bali::RulesForDsl
64
62
 
65
63
  # add current_rule_group
66
64
  self.map_rules_dsl.current_rule_class.add_rule_group(self.current_rule_group)
67
- end # mutex synchronize
68
- end # each subtarget
65
+ end # each subtarget
66
+ end # sync block
69
67
  end # describe
70
68
 
69
+ # others block
70
+ def others(*params)
71
+ @@lock.synchronize do
72
+ rules = {}
73
+
74
+ params.each do |passed_argument|
75
+ if passed_argument.is_a?(Hash)
76
+ rules = passed_argument
77
+ else
78
+ raise Bali::DslError, "Allowed argument for others: hash"
79
+ end
80
+ end
81
+
82
+ self.current_rule_group = self.map_rules_dsl.current_rule_class.others_rule_group
83
+
84
+ if block_given?
85
+ yield
86
+ else
87
+ rules.each do |auth_val, operations|
88
+ if operations.is_a?(Array)
89
+ operations.each do |op|
90
+ rule = Bali::Rule.new(auth_val, op)
91
+ self.current_rule_group.add_rule(rule)
92
+ end
93
+ else
94
+ operation = operations
95
+ rule = Bali::Rule.new(auth_val, operation)
96
+ self.current_rule_group.add_rule(rule)
97
+ end
98
+ end # each rules
99
+ end # block_given?
100
+ end # synchronize
101
+ end # others
102
+
71
103
  # to define can and cant is basically using this method
72
- def process_auth_rules(auth_val, operations)
104
+ def bali_process_auth_rules(auth_val, operations)
73
105
  conditional_hash = nil
74
106
 
75
107
  # scan operations for options
@@ -97,14 +129,20 @@ class Bali::RulesForDsl
97
129
  self.current_rule_group.add_rule(rule)
98
130
  end
99
131
  end
100
- end # process_auth_rules
132
+ end # bali_process_auth_rules
133
+
134
+ # clear all defined rules
135
+ def clear_rules
136
+ self.current_rule_group.clear_rules
137
+ true
138
+ end
101
139
 
102
140
  def can(*operations)
103
- process_auth_rules(:can, operations)
141
+ bali_process_auth_rules(:can, operations)
104
142
  end
105
143
 
106
144
  def cannot(*operations)
107
- process_auth_rules(:cannot, operations)
145
+ bali_process_auth_rules(:cannot, operations)
108
146
  end
109
147
 
110
148
  def cant(*operations)
@@ -17,6 +17,14 @@ class Bali::Rule
17
17
  self
18
18
  end
19
19
 
20
+ def clone
21
+ cloned_rule = Bali::Rule.new(auth_val, operation)
22
+ cloned_rule.decider = decider if decider
23
+ cloned_rule.decider_type = decider_type if decider_type
24
+
25
+ cloned_rule
26
+ end
27
+
20
28
  def auth_val=(aval)
21
29
  # TODO: in version 3 remove :cant
22
30
  if aval == :can || aval == :cannot
@@ -32,7 +40,7 @@ class Bali::Rule
32
40
  if dectype == :if || dectype == :unless
33
41
  @decider_type = dectype
34
42
  else
35
- raise Bali::DslError, "decider type can only be either if or unless"
43
+ raise Bali::DslError, "decider type can only be either if or unless, got: #{dectype}"
36
44
  end
37
45
  end
38
46
 
@@ -1,9 +1,16 @@
1
1
  # the parent of all Bali::RuleGroup.
2
2
  class Bali::RuleClass
3
3
  attr_reader :target_class
4
- attr_accessor :alias_name
5
4
 
5
+ # consist of canonised subtarget and its rule group, eg:
6
+ # {
7
+ # general_user: RuleGroup
8
+ # }
6
9
  attr_accessor :rule_groups
10
+
11
+ # rule group for "other" subtargets, always checked the last time
12
+ # after the "more proper" rule groups are checked
13
+ attr_accessor :others_rule_group
7
14
 
8
15
  def initialize(target_class)
9
16
  if target_class.is_a?(Class)
@@ -13,19 +20,43 @@ class Bali::RuleClass
13
20
  end
14
21
 
15
22
  self.rule_groups = {}
23
+ self.others_rule_group = Bali::RuleGroup.new(target_class, "__*__")
16
24
  end
17
25
 
18
26
  def add_rule_group(rule_group)
19
27
  if rule_group.is_a?(Bali::RuleGroup)
20
28
  target_user = rule_group.subtarget
21
- self.rule_groups[Bali::RuleGroup.canon_name(target_user)] = rule_group
29
+ if target_user == "__*__" || target_user == :"__*__"
30
+ raise Bali::DslError, "__*__ is a reserved subtarget used by Bali's internal"
31
+ end
32
+ self.rule_groups[rule_group.subtarget] = rule_group
22
33
  else
23
- raise Bali::DslError, "Rule group must be an instance of Bali::RuleGroup"
34
+ raise Bali::DslError, "Rule group must be an instance of Bali::RuleGroup, got: #{rule_group.class.name}"
24
35
  end
25
36
  end
26
37
 
27
38
  def rules_for(subtarget)
39
+ return others_rule_group if subtarget == "__*__"
28
40
  subtarget = Bali::RuleGroup.canon_name(subtarget)
29
41
  self.rule_groups[subtarget]
30
42
  end
43
+
44
+ # options can contains:
45
+ # :target_class => identifying the target class on which the clone will be applied
46
+ def clone(options = {})
47
+ target_class = options.fetch(:target_class)
48
+ cloned_rc = Bali::RuleClass.new(target_class)
49
+
50
+ rule_groups.each do |subtarget, rule_group|
51
+ rule_group_clone = rule_group.clone
52
+ rule_group_clone.target = target_class
53
+ cloned_rc.add_rule_group(rule_group_clone)
54
+ end
55
+
56
+ others_rule_group_clone = others_rule_group.clone
57
+ others_rule_group_clone.target = target_class
58
+ cloned_rc.others_rule_group = others_rule_group_clone
59
+
60
+ cloned_rc
61
+ end
31
62
  end
@@ -2,9 +2,6 @@ class Bali::RuleGroup
2
2
  # the target class
3
3
  attr_accessor :target
4
4
 
5
- # the alias name for the target
6
- attr_accessor :alias_tgt
7
-
8
5
  # the user to which this rule group is applied
9
6
  attr_accessor :subtarget
10
7
 
@@ -28,17 +25,30 @@ class Bali::RuleGroup
28
25
  end
29
26
  end
30
27
 
31
- def initialize(target, alias_tgt, subtarget)
28
+ def initialize(target, subtarget)
32
29
  self.target = target
33
- self.alias_tgt = alias_tgt
34
30
  self.subtarget = Bali::RuleGroup.canon_name(subtarget)
35
31
 
36
32
  self.cans = {}
37
33
  self.cants = {}
34
+
35
+ # by default, rule group is neither zeus nor plant
36
+ self.zeus = false
37
+ self.plant = false
38
+ end
39
+
40
+ def clone
41
+ cloned_rg = Bali::RuleGroup.new(target, subtarget)
42
+ cans.each_value { |can_rule| cloned_rg.add_rule(can_rule.clone) }
43
+ cants.each_value { |cant_rule| cloned_rg.add_rule(cant_rule.clone) }
44
+ cloned_rg.zeus = zeus
45
+ cloned_rg.plant = plant
46
+
47
+ cloned_rg
38
48
  end
39
49
 
40
50
  def add_rule(rule)
41
- raise Bali::DslError, "Rule must be of class Bali::Rule" unless rule.is_a?(Bali::Rule)
51
+ raise Bali::DslError, "Rule must be of class Bali::Rule, got: #{rule.class.name}" unless rule.is_a?(Bali::Rule)
42
52
 
43
53
  # operation cannot be defined twice
44
54
  operation = rule.operation.to_sym
@@ -54,6 +64,11 @@ class Bali::RuleGroup
54
64
  end
55
65
  end
56
66
 
67
+ def clear_rules
68
+ self.cans = {}
69
+ self.cants = {}
70
+ end
71
+
57
72
  def get_rule(auth_val, operation)
58
73
  rule = nil
59
74
  case auth_val
@@ -6,14 +6,9 @@ module Bali::Integrators::Rule
6
6
  end
7
7
 
8
8
  def rule_class_for(target)
9
- if target.is_a?(Symbol)
10
- class_name = Bali::ALIASED_RULE_CLASS_MAP[target]
11
- return class_name.nil? ? nil : rule_class_for(class_name)
12
- else
13
- raise Bali::DslError, "Target must be a class" unless target.is_a?(Class)
14
- rule_class = Bali::RULE_CLASS_MAP[target.to_s]
15
- return rule_class.nil? ? nil : rule_class
16
- end
9
+ raise Bali::DslError, "Target must be a class" unless target.is_a?(Class)
10
+ rule_class = Bali::RULE_CLASS_MAP[target.to_s]
11
+ return rule_class.nil? ? nil : rule_class
17
12
  end
18
13
 
19
14
  # attempt to search the rule group, but if not exist, will return nil
@@ -30,26 +25,9 @@ module Bali::Integrators::Rule
30
25
  def add_rule_class(rule_class)
31
26
  if rule_class.is_a?(Bali::RuleClass)
32
27
  target = rule_class.target_class
33
- alias_target = rule_class.alias_name
34
28
 
35
29
  raise Bali::DslError, "Target must be a class" unless target.is_a?(Class)
36
30
 
37
- # remove any previous association of rule
38
- begin
39
- last_associated_alias = Bali::REVERSE_ALIASED_RULE_CLASS_MAP[target]
40
- if last_associated_alias
41
- Bali::ALIASED_RULE_CLASS_MAP.delete(last_associated_alias)
42
- Bali::REVERSE_ALIASED_RULE_CLASS_MAP.delete(target)
43
- Bali::RULE_CLASS_MAP.delete(target)
44
- end
45
- end
46
-
47
- # if "as" is present
48
- if alias_target.is_a?(Symbol)
49
- Bali::ALIASED_RULE_CLASS_MAP[alias_target] = target
50
- Bali::REVERSE_ALIASED_RULE_CLASS_MAP[target] = alias_target
51
- end
52
-
53
31
  Bali::RULE_CLASS_MAP[target.to_s] = rule_class
54
32
  rule_class
55
33
  else
@@ -1,4 +1,4 @@
1
- # class that will be included in each instantiated target classes as defined
1
+ # module that will be included in each instantiated target classes as defined
2
2
  # in map_rules
3
3
  module Bali::Objector
4
4
  def self.included(base)
@@ -41,6 +41,38 @@ module Bali::Objector
41
41
  end
42
42
 
43
43
  module Bali::Objector::Statics
44
+ # FUZY-ed value is happen when it is not really clear, need further cross checking,
45
+ # whether it is really allowed or not. It happens for example in block with others, such as this:
46
+ #
47
+ # describe :finance do
48
+ # cannot :view
49
+ # end
50
+ # others do
51
+ # can :view
52
+ # can :index
53
+ # end
54
+ #
55
+ # In the example above, objecting cannot view on finance will result in STRONG_FALSE, but
56
+ # objecting can index on finance will result in FUZY_TRUE.
57
+ #
58
+ # Eventually, all FUZY value will be normal TRUE or FALSE if no definite counterpart
59
+ # is found/defined
60
+ BALI_FUZY_FALSE = -2
61
+ BALI_FUZY_TRUE = 2
62
+ BALI_FALSE = -1
63
+ BALI_TRUE = 1
64
+
65
+ # translate response for value above to traditional true/false
66
+ def bali_translate_response(bali_bool_value)
67
+ raise Bali::Error, "Expect bali value to be an integer" unless bali_bool_value.is_a?(Integer)
68
+ if bali_bool_value < 0
69
+ return false
70
+ elsif bali_bool_value > 0
71
+ return true
72
+ else
73
+ raise Bali::Error, "Bali bool value can either be negative or positive integer"
74
+ end
75
+ end
44
76
 
45
77
  # will return array
46
78
  def bali_translate_subtarget_roles(_subtarget_roles)
@@ -61,7 +93,7 @@ module Bali::Objector::Statics
61
93
  Bali::TRANSLATED_SUBTARGET_ROLES.each do |subtarget_class, roles_field_name|
62
94
  if _subtarget_class == subtarget_class
63
95
  deducted_roles = _subtarget.send(roles_field_name)
64
- if deducted_roles.is_a?(String) || deducted_roles.is_a?(Symbol)
96
+ if deducted_roles.is_a?(String) || deducted_roles.is_a?(Symbol) || deducted_roles.is_a?(NilClass)
65
97
  deducted_roles = [deducted_roles]
66
98
  break
67
99
  elsif deducted_roles.is_a?(Array)
@@ -87,42 +119,90 @@ module Bali::Objector::Statics
87
119
  # if performed on a class-level, don't call its class or it will return
88
120
  # Class. That's not what is expected.
89
121
  if self.is_a?(Class)
90
- rule_group = Bali::Integrators::Rule.rule_group_for(self, subtarget)
122
+ klass = self
91
123
  else
92
- rule_group = Bali::Integrators::Rule.rule_group_for(self.class, subtarget)
124
+ klass = self.class
93
125
  end
94
126
 
127
+ rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
128
+ other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__")
129
+
130
+ rule = nil
131
+
95
132
  # default of can? is false whenever RuleClass for that class is undefined
96
133
  # or RuleGroup for that subtarget is not defined
97
- return false if rule_group.nil?
98
-
99
- # get the specific rule
100
- rule = rule_group.get_rule(:can, operation)
134
+ if rule_group.nil?
135
+ # no more chance for checking
136
+ return BALI_FALSE if other_rule_group.nil?
137
+ else
138
+ # get the specific rule from its own describe block
139
+ rule = rule_group.get_rule(:can, operation)
140
+ end
101
141
 
102
- # plan subtarget is not allowed unless spesificly defined
103
- return false if rule_group.plant? && rule.nil?
142
+ # retrieve rule from others group
143
+ otherly_rule = other_rule_group.get_rule(:can, operation)
104
144
 
105
145
  # godly subtarget is allowed to do as he wishes
106
146
  # so long that the rule is not specificly defined
107
147
  # or overwritten by subsequent rule
108
- if rule_group.zeus?
148
+ if rule_group && rule_group.zeus?
109
149
  if rule.nil?
150
+ _options = options.dup
151
+ _options[:cross_check] = true
152
+ _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?
153
+
154
+ check_val = self.bali_cannot?(subtarget, operation, record, _options)
155
+
110
156
  # check further whether cant is defined to overwrite this can_all
111
- if self.cannot?(subtarget, operation, record, cross_check: true)
112
- return false
157
+ if check_val == BALI_TRUE
158
+ return BALI_FALSE
113
159
  else
114
- return true
160
+ return BALI_TRUE
115
161
  end
116
162
  end
117
163
  end
118
164
 
165
+ # do after crosscheck
166
+ # plan subtarget is not allowed unless spesificly defined
167
+ return BALI_FALSE if rule_group && rule_group.plant? && rule.nil? && otherly_rule.nil?
168
+
119
169
  if rule.nil?
120
170
  # default if can? for undefined rule is false, after related clause
121
171
  # cannot be found in cannot?
122
- return false if options[:cross_check]
123
- options[:cross_check] = true
124
- return !self.cannot?(subtarget, operation, record, options)
125
- else
172
+
173
+ unless options[:cross_check]
174
+ options[:cross_check] = true
175
+ cross_check_value = self.bali_cannot?(subtarget, operation, record, options)
176
+ end
177
+
178
+ # either if rule from others block is defined, and the result so far is fuzy
179
+ # or, otherly rule is defined, and it is still a cross check
180
+ # plus, the result is not a definite BALI_TRUE/BALI_FALSE
181
+ #
182
+ # rationalisation:
183
+ # 1. Definite answer such as BALI_TRUE and BALI_FALSE is to be prioritised over
184
+ # FUZY answer, because definite answer is not gathered from others block where
185
+ # FUZY answer is. Therefore, it is an intended result
186
+ # 2. If the answer is FUZY, otherly_rule only be considered if the result
187
+ # is either FUZY TRUE or FUZY FALSE, or
188
+ # 3. Or, when already in cross check mode, we cannot retrieve cross_check_value
189
+ # what we can is instead, if otherly rule is available, just to try the odd
190
+ if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
191
+ (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
192
+ (otherly_rule && options[:cross_check] && cross_check_value.nil?)
193
+ # give chance to check at the others block
194
+ rule = otherly_rule
195
+ else
196
+ # either the return is not fuzy, or otherly rule is undefined
197
+ if cross_check_value == BALI_TRUE
198
+ return BALI_FALSE
199
+ elsif cross_check_value == BALI_FALSE
200
+ return BALI_TRUE
201
+ end
202
+ end
203
+ end
204
+
205
+ if rule
126
206
  if rule.has_decider?
127
207
  # must test first
128
208
  decider = rule.decider
@@ -130,161 +210,185 @@ module Bali::Objector::Statics
130
210
  case decider.arity
131
211
  when 0
132
212
  if rule.decider_type == :if
133
- if decider.()
134
- return true
135
- else
136
- return false
137
- end
213
+ return decider.() ? BALI_TRUE : BALI_FALSE
138
214
  elsif rule.decider_type == :unless
139
215
  unless decider.()
140
- return true
216
+ return BALI_TRUE
141
217
  else
142
- return false
218
+ return BALI_FALSE
143
219
  end
144
220
  end
145
221
  when 1
146
222
  if rule.decider_type == :if
147
- if decider.(record)
148
- return true
149
- else
150
- return false
151
- end
223
+ return decider.(record) ? BALI_TRUE : BALI_FALSE
152
224
  elsif rule.decider_type == :unless
153
225
  unless decider.(record)
154
- return true
226
+ return BALI_TRUE
155
227
  else
156
- return false
228
+ return BALI_FALSE
157
229
  end
158
230
  end
159
231
  when 2
160
232
  if rule.decider_type == :if
161
- if decider.(record, original_subtarget)
162
- return true
163
- else
164
- return false
165
- end
233
+ return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
166
234
  elsif rule.decider_type == :unless
167
235
  unless decider.(record, original_subtarget)
168
- return true
236
+ return BALI_TRUE
169
237
  else
170
- return false
238
+ return BALI_FALSE
171
239
  end
172
240
  end
173
241
  end
174
242
  else
175
243
  # rule is properly defined
176
- return true
244
+ return BALI_TRUE
177
245
  end
178
246
  end
247
+
248
+ # return fuzy if otherly rule defines contrary to this (can)
249
+ if other_rule_group.get_rule(:cannot, operation)
250
+ return BALI_FUZY_FALSE
251
+ else
252
+ return BALI_FALSE
253
+ end
179
254
  end
180
255
 
181
256
  def bali_cannot?(subtarget, operation, record = self, options = {})
182
257
  if self.is_a?(Class)
183
- rule_group = Bali::Integrators::Rule.rule_group_for(self, subtarget)
258
+ klass = self
184
259
  else
185
- rule_group = Bali::Integrators::Rule.rule_group_for(self.class, subtarget)
260
+ klass = self.class
186
261
  end
187
262
 
188
- # default of cannot? is true whenever RuleClass for that class is undefined
189
- # or RuleGroup for that subtarget is not defined
190
- return true if rule_group.nil?
263
+ rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
264
+ other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__")
191
265
 
192
- rule = rule_group.get_rule(:cannot, operation)
266
+ rule = nil
193
267
 
194
- # godly subtarget is not to be prohibited in his endeavours
195
- # so long that no specific rule about this operation is defined
196
- return false if rule_group.zeus? && rule.nil?
268
+ # default of cannot? is true whenever RuleClass for that class is undefined
269
+ # or RuleGroup for that subtarget is not defined
270
+ if rule_group.nil?
271
+ return BALI_TRUE if other_rule_group.nil?
272
+ else
273
+ # get the specific rule from its own describe block
274
+ rule = rule_group.get_rule(:cannot, operation)
275
+ end
197
276
 
277
+ otherly_rule = other_rule_group.get_rule(:cannot, operation)
198
278
  # plant subtarget is not allowed to do things unless specificly defined
199
- if rule_group.plant?
279
+ if rule_group && rule_group.plant?
200
280
  if rule.nil?
281
+ _options = options.dup
282
+ _options[:cross_check] = true
283
+ _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?
284
+
201
285
  # check further whether defined in can?
202
- if self.can?(subtarget, operation, record, cross_check: true)
203
- return false # well, it is defined in can, so it must overwrite this cant_all rule
286
+ check_val = self.bali_can?(subtarget, operation, record, _options)
287
+
288
+ if check_val == BALI_TRUE
289
+ return BALI_FALSE # well, it is defined in can, so it must overwrite this cant_all rule
204
290
  else
205
291
  # plant, and then rule is not defined for further inspection. stright
206
292
  # is not allowed to do this thing
207
- return true
293
+ return BALI_TRUE
208
294
  end
209
295
  end
210
296
  end
211
297
 
212
- # if rule cannot be found, then true is returned for cannot? unless
213
- # can? is defined exactly for the same target, and subtarget, and record (if given)
214
298
  if rule.nil?
215
- return true if options[:cross_check]
216
- options[:cross_check] = true
217
- return !self.can?(subtarget, operation, record, options)
218
- else
299
+ unless options[:cross_check]
300
+ options[:cross_check] = true
301
+ cross_check_value = self.bali_can?(subtarget, operation, record, options)
302
+ end
303
+
304
+ if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
305
+ (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
306
+ (otherly_rule && options[:cross_check] && cross_check_value.nil?)
307
+ rule = otherly_rule
308
+ else
309
+ if cross_check_value == BALI_TRUE
310
+ # from can? because of cross check, then it should be false
311
+ return BALI_FALSE
312
+ elsif cross_check_value == BALI_FALSE
313
+ # from can? because of cross check returning false
314
+ # then it should be true, that is, cannot
315
+ return BALI_TRUE
316
+ end
317
+ end
318
+ end
319
+
320
+ # do after cross check
321
+ # godly subtarget is not to be prohibited in his endeavours
322
+ # so long that no specific rule about this operation is defined
323
+ return BALI_FALSE if rule_group && rule_group.zeus? && rule.nil? && otherly_rule.nil?
324
+
325
+ if rule
219
326
  if rule.has_decider?
220
327
  decider = rule.decider
221
328
  original_subtarget = options.fetch(:original_subtarget)
222
329
  case decider.arity
223
330
  when 0
224
331
  if rule.decider_type == :if
225
- if decider.()
226
- return true
227
- else
228
- return false
229
- end
332
+ return decider.() ? BALI_TRUE : BALI_FALSE
230
333
  elsif rule.decider_type == :unless
231
334
  unless decider.()
232
- return true
335
+ return BALI_TRUE
233
336
  else
234
- return false
337
+ return BALI_FALSE
235
338
  end
236
339
  end
237
340
  when 1
238
341
  if rule.decider_type == :if
239
- if decider.(record)
240
- return true
241
- else
242
- return false
243
- end
342
+ return decider.(record) ? BALI_TRUE : BALI_FALSE
244
343
  elsif rule.decider_type == :unless
245
344
  unless decider.(record)
246
- return true
345
+ return BALI_TRUE
247
346
  else
248
- return false
347
+ return BALI_FALSE
249
348
  end
250
349
  end
251
350
  when 2
252
351
  if rule.decider_type == :if
253
- if decider.(record, original_subtarget)
254
- return true
255
- else
256
- return false
257
- end
352
+ return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
258
353
  elsif rule.decider_type == :unless
259
354
  unless decider.(record, original_subtarget)
260
- return true
355
+ return BALI_TRUE
261
356
  else
262
- return false
357
+ return BALI_FALSE
263
358
  end
264
359
  end
265
360
  end
266
361
  else
267
- return true # rule is properly defined
362
+ return BALI_TRUE # rule is properly defined
268
363
  end # if rule has decider
269
364
  end # if rule is nil
270
- end
365
+
366
+ # return fuzy if otherly rule defines contrary to this cannot rule
367
+ if other_rule_group.get_rule(:can, operation)
368
+ return BALI_FUZY_TRUE
369
+ else
370
+ BALI_TRUE
371
+ end
372
+ end # bali cannot
271
373
 
272
374
  def can?(subtarget_roles, operation, record = self, options = {})
273
375
  subs = bali_translate_subtarget_roles(subtarget_roles)
274
376
  # well, it is largely not used unless decider's is 2 arity
275
377
  options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
276
378
 
277
- can_value = false
379
+ can_value = BALI_FALSE
278
380
  role = nil
279
381
 
280
382
  subs.each do |subtarget|
281
- next if can_value
383
+ next if can_value == BALI_TRUE
282
384
  role = subtarget
283
385
  can_value = bali_can?(role, operation, record, options)
284
386
  end
285
387
 
286
- yield options[:original_subtarget], role, can_value if can_value == false && block_given?
287
- can_value
388
+ if can_value == BALI_FALSE && block_given?
389
+ yield options[:original_subtarget], role, bali_translate_response(can_value)
390
+ end
391
+ bali_translate_response can_value
288
392
  rescue => e
289
393
  if e.is_a?(Bali::AuthorizationError)
290
394
  raise e
@@ -302,19 +406,20 @@ module Bali::Objector::Statics
302
406
  subs = bali_translate_subtarget_roles subtarget_roles
303
407
  options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
304
408
 
409
+ cant_value = BALI_TRUE
410
+
305
411
  subs.each do |subtarget|
412
+ next if cant_value == BALI_FALSE
306
413
  cant_value = bali_cannot?(subtarget, operation, record, options)
307
- if cant_value == false
414
+ if cant_value == BALI_FALSE
308
415
  role = subtarget
309
416
  if block_given?
310
- yield options[:original_subtarget], role, false
311
- else
312
- return false
417
+ yield options[:original_subtarget], role, bali_translate_response(cant_value)
313
418
  end
314
419
  end
315
420
  end
316
421
 
317
- true
422
+ bali_translate_response cant_value
318
423
  rescue => e
319
424
  if e.is_a?(Bali::AuthorizationError)
320
425
  raise e