bali 2.1.2 → 2.2.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.
@@ -0,0 +1,40 @@
1
+ module Bali::Judger
2
+ class NegativeJudge < Judge
3
+ def initialize
4
+ super(false)
5
+ end
6
+
7
+ def auth_level
8
+ :cannot
9
+ end
10
+
11
+ def reverse_auth_level
12
+ :can
13
+ end
14
+
15
+ def zeus_return_value
16
+ BALI_FALSE
17
+ end
18
+
19
+ def plant_return_value
20
+ BALI_TRUE
21
+ end
22
+
23
+ def default_positive_return_value
24
+ BALI_TRUE
25
+ end
26
+
27
+ def default_negative_fuzy_return_value
28
+ BALI_FUZY_TRUE
29
+ end
30
+
31
+ # cannot? by default return true when
32
+ def natural_value
33
+ BALI_TRUE
34
+ end
35
+
36
+ def need_to_check_for_intervention?
37
+ rule_group && rule_group.plant?
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module Bali::Judger
2
+ class PositiveJudge < Judge
3
+ def initialize
4
+ super(false)
5
+ end
6
+
7
+ def auth_level
8
+ :can
9
+ end
10
+
11
+ def reverse_auth_level
12
+ :cannot
13
+ end
14
+
15
+ def zeus_return_value
16
+ BALI_TRUE
17
+ end
18
+
19
+ def plant_return_value
20
+ BALI_FALSE
21
+ end
22
+
23
+ def default_positive_return_value
24
+ BALI_TRUE
25
+ end
26
+
27
+ def default_negative_fuzy_return_value
28
+ BALI_FUZY_FALSE
29
+ end
30
+
31
+ # value that is natural to be returned by the jury
32
+ # can? by default return false
33
+ def natural_value
34
+ BALI_FALSE
35
+ end
36
+
37
+ def need_to_check_for_intervention?
38
+ rule_group && rule_group.zeus?
39
+ end
40
+ end # positive judger
41
+ end # module judger
@@ -0,0 +1,61 @@
1
+ class Bali::RoleExtractor
2
+ attr_reader :arg
3
+
4
+ # argument can be anything, as long as role extractor know how to extract
5
+ def initialize(arg)
6
+ @arg = arg
7
+ end
8
+
9
+ def get_roles(object = @arg)
10
+ case object
11
+ when String; get_role_string(object)
12
+ when Symbol; get_role_symbol(object)
13
+ when NilClass; get_role_nil(object)
14
+ when Array; get_role_array(object)
15
+ else
16
+ get_role_object(object)
17
+ end
18
+ end
19
+
20
+ private
21
+ def get_role_string(object)
22
+ [object]
23
+ end
24
+
25
+ def get_role_symbol(object)
26
+ [object]
27
+ end
28
+
29
+ def get_role_nil(object)
30
+ [object]
31
+ end
32
+
33
+ def get_role_array(object)
34
+ object
35
+ end
36
+
37
+ # this case, _subtarget_roles is an object but not a symbol or a string
38
+ # let's try to deduct subtarget's roles
39
+ def get_role_object(object)
40
+ object_class = object.class.to_s
41
+
42
+ # variable to hold deducted role of the passed object
43
+ deducted_roles = nil
44
+ role_extracted = false
45
+
46
+ Bali::TRANSLATED_SUBTARGET_ROLES.each do |current_subtarget_class, roles_field_name|
47
+ if object_class == current_subtarget_class
48
+ deducted_roles = object.send(roles_field_name)
49
+ deducted_roles = get_roles(deducted_roles)
50
+ role_extracted = true
51
+ break
52
+ end # if matching class
53
+ end # each TRANSLATED_SUBTARGET_ROLES
54
+
55
+ unless role_extracted
56
+ raise Bali::AuthorizationError, "Bali does not know how to process roles: #{object}"
57
+ end
58
+
59
+ deducted_roles
60
+ end
61
+ end
@@ -27,9 +27,10 @@ class Bali::RuleClass
27
27
  if rule_group.is_a?(Bali::RuleGroup)
28
28
  target_user = rule_group.subtarget
29
29
  if target_user == "__*__" || target_user == :"__*__"
30
- raise Bali::DslError, "__*__ is a reserved subtarget used by Bali's internal"
30
+ self.others_rule_group = rule_group
31
+ else
32
+ self.rule_groups[rule_group.subtarget] = rule_group
31
33
  end
32
- self.rule_groups[rule_group.subtarget] = rule_group
33
34
  else
34
35
  raise Bali::DslError, "Rule group must be an instance of Bali::RuleGroup, got: #{rule_group.class.name}"
35
36
  end
@@ -33,6 +33,9 @@ class Bali::RuleGroup
33
33
  self.cants = {}
34
34
 
35
35
  # by default, rule group is neither zeus nor plant
36
+ # it is neutral.
37
+ # meaning, it is neither allowed to do everything, nor it is
38
+ # prohibited to do anything. neutral.
36
39
  self.zeus = false
37
40
  self.plant = false
38
41
  end
data/lib/bali/objector.rb CHANGED
@@ -40,392 +40,82 @@ module Bali::Objector
40
40
  end
41
41
  end
42
42
 
43
+ # to allow class-level objection
43
44
  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
45
+ # get the proper roles for the subtarget, for any type of subtarget
46
+ def bali_translate_subtarget_roles(arg)
47
+ role_extractor = Bali::RoleExtractor.new(arg)
48
+ role_extractor.get_roles
75
49
  end
76
50
 
77
- # will return array
78
- def bali_translate_subtarget_roles(_subtarget_roles)
79
- if _subtarget_roles.is_a?(String) || _subtarget_roles.is_a?(Symbol) || _subtarget_roles.is_a?(NilClass)
80
- return [_subtarget_roles]
81
- elsif _subtarget_roles.is_a?(Array)
82
- return _subtarget_roles
83
- else
84
- # this case, _subtarget_roles is an object but not a symbol or a string
85
- # let's try to deduct subtarget's roles
86
-
87
- _subtarget = _subtarget_roles
88
- _subtarget_class = _subtarget.class.to_s
89
-
90
- # variable to hold deducted role of the passed object
91
- deducted_roles = nil
92
-
93
- Bali::TRANSLATED_SUBTARGET_ROLES.each do |subtarget_class, roles_field_name|
94
- if _subtarget_class == subtarget_class
95
- deducted_roles = _subtarget.send(roles_field_name)
96
- if deducted_roles.is_a?(String) || deducted_roles.is_a?(Symbol) || deducted_roles.is_a?(NilClass)
97
- deducted_roles = [deducted_roles]
98
- break
99
- elsif deducted_roles.is_a?(Array)
100
- break
101
- end
102
- end # if matching class
103
- end # each TRANSLATED_SUBTARGET_ROLES
104
-
105
- if deducted_roles.nil?
106
- raise Bali::AuthorizationError, "Bali does not know how to process roles: #{_subtarget_roles}"
107
- end
108
-
109
- return deducted_roles
110
- end # if
111
- end
112
-
113
- ### options passable to bali_can? and bali_cannot? are:
114
- ### cross_action: if set to true wouldn't call its counterpart so as to prevent
115
- ### overflowing stack
116
- ### original_subtarget: the original passed to can? and cannot? before
117
- ### processed by bali_translate_subtarget_roles
118
-
119
- def bali_can?(subtarget, operation, record = self, options = {})
120
- # if performed on a class-level, don't call its class or it will return
121
- # Class. That's not what is expected.
122
- if self.is_a?(Class)
123
- klass = self
124
- else
125
- klass = self.class
126
- end
127
-
128
- rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
129
- other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__")
130
-
131
- rule = nil
132
-
133
- # default of can? is false whenever RuleClass for that class is undefined
134
- # or RuleGroup for that subtarget is not defined
135
- if rule_group.nil?
136
- # no more chance for checking
137
- return BALI_FALSE if other_rule_group.nil?
138
- else
139
- # get the specific rule from its own describe block
140
- rule = rule_group.get_rule(:can, operation)
141
- end
142
-
143
- # retrieve rule from others group
144
- otherly_rule = other_rule_group.get_rule(:can, operation)
145
-
146
- # godly subtarget is allowed to do as he wishes
147
- # so long that the rule is not specificly defined
148
- # or overwritten by subsequent rule
149
- if rule_group && rule_group.zeus?
150
- if rule.nil?
151
- _options = options.dup
152
- _options[:cross_check] = true
153
- _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?
154
-
155
- check_val = self.bali_cannot?(subtarget, operation, record, _options)
156
-
157
- # check further whether cant is defined to overwrite this can_all
158
- if check_val == BALI_TRUE
159
- return BALI_FALSE
160
- else
161
- return BALI_TRUE
162
- end
163
- end
164
- end
165
-
166
- # do after crosscheck
167
- # plan subtarget is not allowed unless spesificly defined
168
- return BALI_FALSE if rule_group && rule_group.plant? && rule.nil? && otherly_rule.nil?
169
-
170
- if rule.nil?
171
- # default if can? for undefined rule is false, after related clause
172
- # cannot be found in cannot?
173
-
174
- unless options[:cross_check]
175
- options[:cross_check] = true
176
- cross_check_value = self.bali_cannot?(subtarget, operation, record, options)
177
- end
178
-
179
- # either if rule from others block is defined, and the result so far is fuzy
180
- # or, otherly rule is defined, and it is still a cross check
181
- # plus, the result is not a definite BALI_TRUE/BALI_FALSE
182
- #
183
- # rationalisation:
184
- # 1. Definite answer such as BALI_TRUE and BALI_FALSE is to be prioritised over
185
- # FUZY answer, because definite answer is not gathered from others block where
186
- # FUZY answer is. Therefore, it is an intended result
187
- # 2. If the answer is FUZY, otherly_rule only be considered if the result
188
- # is either FUZY TRUE or FUZY FALSE, or
189
- # 3. Or, when already in cross check mode, we cannot retrieve cross_check_value
190
- # what we can is instead, if otherly rule is available, just to try the odd
191
- if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
192
- (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
193
- (otherly_rule && options[:cross_check] && cross_check_value.nil?)
194
- # give chance to check at the others block
195
- rule = otherly_rule
196
- else
197
- # either the return is not fuzy, or otherly rule is undefined
198
- if cross_check_value == BALI_TRUE
199
- return BALI_FALSE
200
- elsif cross_check_value == BALI_FALSE
201
- return BALI_TRUE
202
- end
203
- end
204
- end
205
-
206
- if rule
207
- if rule.has_decider?
208
- # must test first
209
- decider = rule.decider
210
- original_subtarget = options.fetch(:original_subtarget)
211
- case decider.arity
212
- when 0
213
- if rule.decider_type == :if
214
- return decider.() ? BALI_TRUE : BALI_FALSE
215
- elsif rule.decider_type == :unless
216
- unless decider.()
217
- return BALI_TRUE
218
- else
219
- return BALI_FALSE
220
- end
221
- end
222
- when 1
223
- if rule.decider_type == :if
224
- return decider.(record) ? BALI_TRUE : BALI_FALSE
225
- elsif rule.decider_type == :unless
226
- unless decider.(record)
227
- return BALI_TRUE
228
- else
229
- return BALI_FALSE
230
- end
231
- end
232
- when 2
233
- if rule.decider_type == :if
234
- return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
235
- elsif rule.decider_type == :unless
236
- unless decider.(record, original_subtarget)
237
- return BALI_TRUE
238
- else
239
- return BALI_FALSE
240
- end
241
- end
242
- end
243
- else
244
- # rule is properly defined
245
- return BALI_TRUE
246
- end
247
- end
248
-
249
- # return fuzy if otherly rule defines contrary to this (can)
250
- if other_rule_group.get_rule(:cannot, operation)
251
- return BALI_FUZY_FALSE
252
- else
253
- return BALI_FALSE
254
- end
255
- end
256
-
257
- def bali_cannot?(subtarget, operation, record = self, options = {})
258
- if self.is_a?(Class)
259
- klass = self
260
- else
261
- klass = self.class
262
- end
263
-
264
- rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
265
- other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__")
266
-
267
- rule = nil
268
-
269
- # default of cannot? is true whenever RuleClass for that class is undefined
270
- # or RuleGroup for that subtarget is not defined
271
- if rule_group.nil?
272
- return BALI_TRUE if other_rule_group.nil?
273
- else
274
- # get the specific rule from its own describe block
275
- rule = rule_group.get_rule(:cannot, operation)
276
- end
277
-
278
- otherly_rule = other_rule_group.get_rule(:cannot, operation)
279
- # plant subtarget is not allowed to do things unless specificly defined
280
- if rule_group && rule_group.plant?
281
- if rule.nil?
282
- _options = options.dup
283
- _options[:cross_check] = true
284
- _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?
285
-
286
- # check further whether defined in can?
287
- check_val = self.bali_can?(subtarget, operation, record, _options)
288
-
289
- if check_val == BALI_TRUE
290
- return BALI_FALSE # well, it is defined in can, so it must overwrite this cant_all rule
291
- else
292
- # plant, and then rule is not defined for further inspection. stright
293
- # is not allowed to do this thing
294
- return BALI_TRUE
295
- end
296
- end
297
- end
298
-
299
- if rule.nil?
300
- unless options[:cross_check]
301
- options[:cross_check] = true
302
- cross_check_value = self.bali_can?(subtarget, operation, record, options)
303
- end
304
-
305
- if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
306
- (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
307
- (otherly_rule && options[:cross_check] && cross_check_value.nil?)
308
- rule = otherly_rule
309
- else
310
- if cross_check_value == BALI_TRUE
311
- # from can? because of cross check, then it should be false
312
- return BALI_FALSE
313
- elsif cross_check_value == BALI_FALSE
314
- # from can? because of cross check returning false
315
- # then it should be true, that is, cannot
316
- return BALI_TRUE
317
- end
318
- end
319
- end
320
-
321
- # do after cross check
322
- # godly subtarget is not to be prohibited in his endeavours
323
- # so long that no specific rule about this operation is defined
324
- return BALI_FALSE if rule_group && rule_group.zeus? && rule.nil? && otherly_rule.nil?
325
-
326
- if rule
327
- if rule.has_decider?
328
- decider = rule.decider
329
- original_subtarget = options.fetch(:original_subtarget)
330
- case decider.arity
331
- when 0
332
- if rule.decider_type == :if
333
- return decider.() ? BALI_TRUE : BALI_FALSE
334
- elsif rule.decider_type == :unless
335
- unless decider.()
336
- return BALI_TRUE
337
- else
338
- return BALI_FALSE
339
- end
340
- end
341
- when 1
342
- if rule.decider_type == :if
343
- return decider.(record) ? BALI_TRUE : BALI_FALSE
344
- elsif rule.decider_type == :unless
345
- unless decider.(record)
346
- return BALI_TRUE
347
- else
348
- return BALI_FALSE
349
- end
350
- end
351
- when 2
352
- if rule.decider_type == :if
353
- return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
354
- elsif rule.decider_type == :unless
355
- unless decider.(record, original_subtarget)
356
- return BALI_TRUE
357
- else
358
- return BALI_FALSE
359
- end
360
- end
361
- end
362
- else
363
- return BALI_TRUE # rule is properly defined
364
- end # if rule has decider
365
- end # if rule is nil
366
-
367
- # return fuzy if otherly rule defines contrary to this cannot rule
368
- if other_rule_group.get_rule(:can, operation)
369
- return BALI_FUZY_TRUE
370
- else
371
- BALI_TRUE
372
- end
373
- end # bali cannot
374
-
375
51
  def can?(subtarget_roles, operation, record = self, options = {})
376
52
  subs = bali_translate_subtarget_roles(subtarget_roles)
377
53
  # well, it is largely not used unless decider's is 2 arity
378
- options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
54
+ original_subtarget = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
379
55
 
380
- can_value = BALI_FALSE
56
+ judgement_value = false
381
57
  role = nil
58
+ judger = nil
382
59
 
383
60
  subs.each do |subtarget|
384
- next if can_value == BALI_TRUE
61
+ next if judgement_value == true
62
+
63
+ judge = Bali::Judger::Judge.build(:can, {
64
+ subtarget: subtarget,
65
+ original_subtarget: original_subtarget,
66
+ operation: operation,
67
+ record: record
68
+ })
69
+ judgement_value = judge.judgement
70
+
385
71
  role = subtarget
386
- can_value = bali_can?(role, operation, record, options)
387
72
  end
388
73
 
389
- if can_value == BALI_FALSE && block_given?
390
- yield options[:original_subtarget], role, bali_translate_response(can_value)
74
+ if block_given?
75
+ yield original_subtarget, role, judgement_value
391
76
  end
392
- bali_translate_response can_value
77
+
78
+ judgement_value
393
79
  rescue => e
394
- if e.is_a?(Bali::AuthorizationError)
80
+ if e.is_a?(Bali::AuthorizationError) || e.is_a?(Bali::Error)
395
81
  raise e
396
82
  else
397
- raise Bali::ObjectionError, e.message
83
+ raise Bali::ObjectionError, e.message, e.backtrace
398
84
  end
399
85
  end
400
86
 
401
- def cant?(subtarget_roles, operation, record = self, options = {})
402
- puts "Deprecation Warning: please use cannot? instead, cant? will be deprecated on major release 3.0"
403
- cannot?(subtarget_roles, operation, record, options)
404
- end
405
-
406
87
  def cannot?(subtarget_roles, operation, record = self, options = {})
407
88
  subs = bali_translate_subtarget_roles subtarget_roles
408
- options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
89
+ original_subtarget = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
409
90
 
410
- cant_value = BALI_TRUE
91
+ judgement_value = true
92
+ role = nil
93
+ judger = nil
411
94
 
412
95
  subs.each do |subtarget|
413
- next if cant_value == BALI_FALSE
414
- cant_value = bali_cannot?(subtarget, operation, record, options)
415
- if cant_value == BALI_FALSE
416
- role = subtarget
417
- if block_given?
418
- yield options[:original_subtarget], role, bali_translate_response(cant_value)
419
- end
420
- end
96
+ next if judgement_value == false
97
+
98
+ judge = Bali::Judger::Judge.build(:cannot, {
99
+ subtarget: subtarget,
100
+ original_subtarget: original_subtarget,
101
+ operation: operation,
102
+ record: record
103
+ })
104
+ judgement_value = judge.judgement
105
+
106
+ role = subtarget
107
+ end
108
+
109
+ if block_given?
110
+ yield original_subtarget, role, judgement_value
421
111
  end
422
112
 
423
- bali_translate_response cant_value
113
+ judgement_value
424
114
  rescue => e
425
115
  if e.is_a?(Bali::AuthorizationError)
426
116
  raise e
427
117
  else
428
- raise Bali::ObjectionError, e.message
118
+ raise Bali::ObjectionError, e.message, e.backtrace
429
119
  end
430
120
  end
431
121
 
@@ -444,8 +134,10 @@ module Bali::Objector::Statics
444
134
  end
445
135
 
446
136
  raise auth_error
447
- end
448
- end
137
+ else
138
+ return can_value
139
+ end # if cannot is false, means cannot
140
+ end # can?
449
141
  end
450
142
 
451
143
  def cant!(subtarget_roles, operation, record = self, options = {})
@@ -453,6 +145,11 @@ module Bali::Objector::Statics
453
145
  cannot!(subtarget_roles, operation, record, options)
454
146
  end
455
147
 
148
+ def cant?(subtarget_roles, operation)
149
+ puts "Deprecation Warning: please use cannot? instead, cant? will be deprecated on major release 3.0"
150
+ cannot?(subtarget_roles, operation)
151
+ end
152
+
456
153
  def cannot!(subtarget_roles, operation, record = self, options = {})
457
154
  cannot?(subtarget_roles, operation, record, options) do |original_subtarget, role, cant_value|
458
155
  if cant_value == false
@@ -468,7 +165,9 @@ module Bali::Objector::Statics
468
165
  end
469
166
 
470
167
  raise auth_error
471
- end
472
- end
168
+ else
169
+ return cant_value
170
+ end # if cannot is false, means can
171
+ end # cannot?
473
172
  end
474
173
  end