bali 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZGNkMWUxZjYxNjc3ODBiNzk5ODI4ZWFhMTM3NTgyOGU0MzAzNTZkNA==
4
+ ZTcxZjc2ZmMyNDgzYWVmMDNjOWNhMzNmNjZiZmNjYTcwMjkzZGE0YQ==
5
5
  data.tar.gz: !binary |-
6
- OWU3MjVhNjYwM2U0YTM4YWFjZDBkNDRlOGE0MTM1MzVlMDdkZDNiNg==
6
+ ZTYwYzIwMWE2Nzg1ZjM5YmIwM2RjNTcwY2E2MDQ4OTQ0ZTUzNWZmOA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OGY3ZDhiNTNlNDEzZGMxZmIyNDIyM2E5NmRkZjY1MGI2N2ZiOTE2ZDU0NTIz
10
- ZmZhMjMwZTE5NTg4NTM1MjUxOGM0OTNkZTc4NDNiZDdiODBlYzY3MWZkZTIx
11
- YTJkZGNlODA4ZTAyMzk5YThkZmNlNWNmMGYzMDJhZjA4MzE5OTg=
9
+ MDdkNTNhODc2NTRkNmY1NTBmNDk3MTQ3Mzc5YWUwMjBkNmQzODkzYTliOTA3
10
+ MGJkMjBkMWE0ZTE1ODZkYTY0MmFhMmJmMzMxNTMwM2I0ZWU1ZDE1Y2ZmOWUz
11
+ N2I0NDg5MGYxZjU1NzMyOWU3MzcwOTJhMmUxOTBkNTQ0YWEzNGQ=
12
12
  data.tar.gz: !binary |-
13
- MTY1NDQ1MzRiOTRlZjhiNmRiYTZhNjM4MmFjODVlYjg5YzkzZDE4Yzk3Nzlk
14
- MWVmODIwM2ZjMDFhYTE0YmM5NDMwODI2Y2RmYmJkNTA4Y2JlYjliZGZiYjY5
15
- ZjhhZmFkMmVmMTdjODkyNmUyMTBkOTMwOTAzYWQ4OWYzYmZmNjk=
13
+ YmI1YjcxMmNmOGFjOWFmZjk1NDA0MGIzZWFlMmEwNTk3ODgxNzVkYzRmZjQz
14
+ ZTNkNTU4ZWYwYTBjNWNiZDdjZTNjODA2ZTBjNzc0NDRhZTU3MmQ0NWJiZmI0
15
+ MGViMWE0ZjllNzZjM2U0N2ViYjNjZDYzY2ZlOWRkOTJkNWVhNjM=
data/CHANGELOG.md CHANGED
@@ -72,3 +72,9 @@
72
72
 
73
73
  1. `nil` will be printed <nil> when objecting with can! or cannot! for better readability.
74
74
  2. Bug fixes on declaring multiple rules with decider. Previously, others were ignored--now, every single rule will get defined whether decider is present or not.
75
+
76
+ == Version 2.2.0
77
+
78
+ 1. Deprecating `describe` block in favour of `role` block, `describe` is to be deprecated in version 3.0.
79
+ 2. Using strategy pattern, heavy refactoring
80
+ 3. Human-readable authorisation error message when invoking !-version of can/cannot, for eg: Role general_user is not allowed to perform operation `update` on My::Transaction
data/README.md CHANGED
@@ -28,6 +28,7 @@ And then execute:
28
28
 
29
29
  1. `cant` and `cant_all` which are used to declare rules will be deprecated on version 3.0, in favor of `cannot` and `cannot_all`. The reason behind this is that `can` and `cant` only differ by 1 letter, it is thought to be better to make it less ambiguous.
30
30
  2. `cant?` and subsequently new-introduced `cant!` will be deprecated on version 3.0, in favor of `cannot?` and `cannot!` for the same reason as above.
31
+ 3. Since version 2.1.3, `describe` block is replaced with `role` block. `describe` block will be deprecated on version 3.0.
31
32
 
32
33
  ## Usage
33
34
 
@@ -88,21 +89,21 @@ The specification above seems very terrifying, but with Bali, those can be defin
88
89
  ```ruby
89
90
  Bali.map_rules do
90
91
  rules_for My::Transaction do
91
- describe(:supreme_user) { can_all }
92
- describe :admin_user do
92
+ role(:supreme_user) { can_all }
93
+ role :admin_user do
93
94
  can_all
94
95
  # a more specific rule would be executed even if can_all is present
95
96
  can :cancel,
96
97
  if: proc { |record| record.payment_channel == "CREDIT_CARD" &&
97
98
  !record.is_settled? }
98
99
  end
99
- describe "general user", can: [:download]
100
- describe "finance" do
100
+ role "general user", can: [:download]
101
+ role "finance" do
101
102
  can :delete, if: proc { |record| record.is_settled? }
102
103
  can :cancel, unless: proc { |record| record.is_settled? }
103
104
  end # finance_user description
104
- describe :guest, nil { can :report_fraud }
105
- describe :client do
105
+ role :guest, nil { can :report_fraud }
106
+ role :client do
106
107
  can :create
107
108
  end
108
109
  others do
@@ -42,8 +42,12 @@ class Bali::MapRulesDsl
42
42
  raise Bali::DslError, "describe block must be within rules_for block"
43
43
  end
44
44
 
45
+ def role(*params)
46
+ raise Bali::DslError, "role block must be within rules_for block"
47
+ end
48
+
45
49
  def can(*params)
46
- raise Bali::DslError, "can block must be within describe block"
50
+ raise Bali::DslError, "can block must be within role block"
47
51
  end
48
52
 
49
53
  def cant(*params)
@@ -52,15 +56,15 @@ class Bali::MapRulesDsl
52
56
  end
53
57
 
54
58
  def cannot(*params)
55
- raise Bali::DslError, "cant block must be within describe block"
59
+ raise Bali::DslError, "cant block must be within role block"
56
60
  end
57
61
 
58
62
  def can_all(*params)
59
- raise Bali::DslError, "can_all block must be within describe block"
63
+ raise Bali::DslError, "can_all block must be within role block"
60
64
  end
61
65
 
62
66
  def clear_rules
63
- raise Bali::DslError, "clear_rules must be called within describe block"
67
+ raise Bali::DslError, "clear_rules must be called within role block"
64
68
  end
65
69
 
66
70
  def cant_all(*params)
@@ -69,6 +73,6 @@ class Bali::MapRulesDsl
69
73
  end
70
74
 
71
75
  def cannot_all(*params)
72
- raise Bali::DslError, "cant_all block must be within describe block"
76
+ raise Bali::DslError, "cant_all block must be within role block"
73
77
  end
74
78
  end
@@ -4,6 +4,11 @@ class Bali::RulesForDsl
4
4
  attr_accessor :map_rules_dsl
5
5
  attr_accessor :current_rule_group
6
6
 
7
+ # all to be processed subtargets
8
+ attr_accessor :current_subtargets
9
+ # rules defined with hash can: [] and cannot: []
10
+ attr_accessor :shortcut_rules
11
+
7
12
  def initialize(map_rules_dsl)
8
13
  @@lock = Mutex.new
9
14
  self.map_rules_dsl = map_rules_dsl
@@ -12,123 +17,43 @@ class Bali::RulesForDsl
12
17
  def current_rule_class
13
18
  self.map_rules_dsl.current_rule_class
14
19
  end
20
+ protected :current_rule_class
15
21
 
16
- def describe(*params)
22
+ def role(*params)
17
23
  @@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
33
- end
34
-
35
- target_class = self.map_rules_dsl.current_rule_class.target_class
36
-
37
- subtargets.each do |subtarget|
38
- rule_group = self.current_rule_class.rules_for(subtarget)
39
- if rule_group.nil?
40
- rule_group = Bali::RuleGroup.new(target_class, subtarget)
41
- end
42
-
43
- self.current_rule_group = rule_group
44
-
24
+ bali_scrap_actors(*params)
25
+ bali_scrap_shortcut_rules(*params)
26
+ current_subtargets.each do |subtarget|
45
27
  if block_given?
46
- yield
47
- else
48
- # auth_val is either can or cant
49
- rules.each do |auth_val, operations|
50
- if operations.is_a?(Array)
51
- operations.each do |op|
52
- rule = Bali::Rule.new(auth_val, op)
53
- self.current_rule_group.add_rule(rule)
54
- end
55
- else
56
- operation = operations # well, basically is 1 only
57
- rule = Bali::Rule.new(auth_val, operation)
58
- self.current_rule_group.add_rule(rule)
59
- end
60
- end # each rules
61
- end # block_given?
62
-
63
- # add current_rule_group
64
- self.map_rules_dsl.current_rule_class.add_rule_group(self.current_rule_group)
65
- end # each subtarget
66
- end # sync block
67
- end # describe
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
28
+ bali_process_subtarget(subtarget) do
29
+ yield
30
+ end
77
31
  else
78
- raise Bali::DslError, "Allowed argument for others: hash"
32
+ bali_process_subtarget(subtarget)
79
33
  end
80
34
  end
35
+ end
36
+ end # role
81
37
 
82
- self.current_rule_group = self.map_rules_dsl.current_rule_class.others_rule_group
83
-
84
- if block_given?
38
+ def describe(*params)
39
+ puts "Bali Deprecation Warning: describing rules using describe will be deprecated on major release 3.0, use role instead"
40
+ if block_given?
41
+ role(*params) do
85
42
  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
-
103
- # to define can and cant is basically using this method
104
- def bali_process_auth_rules(auth_val, args)
105
- conditional_hash = nil
106
- operations = []
107
-
108
- # scan args for options
109
- args.each do |elm|
110
- if elm.is_a?(Hash)
111
- conditional_hash = elm
112
- else
113
- operations << elm
114
43
  end
44
+ else
45
+ role(*params)
115
46
  end
47
+ end
116
48
 
117
- # add operation one by one
118
- operations.each do |op|
119
- rule = Bali::Rule.new(auth_val, op)
120
- if conditional_hash
121
- if conditional_hash[:if] || conditional_hash["if"]
122
- rule.decider = conditional_hash[:if] || conditional_hash["if"]
123
- rule.decider_type = :if
124
- elsif conditional_hash[:unless] || conditional_hash[:unless]
125
- rule.decider = conditional_hash[:unless] || conditional_hash["unless"]
126
- rule.decider_type = :unless
127
- end
49
+ # others block
50
+ def others(*params)
51
+ if block_given?
52
+ role("__*__") do
53
+ yield
128
54
  end
129
- self.current_rule_group.add_rule(rule)
130
55
  end
131
- end # bali_process_auth_rules
56
+ end # others
132
57
 
133
58
  # clear all defined rules
134
59
  def clear_rules
@@ -163,4 +88,101 @@ class Bali::RulesForDsl
163
88
  puts "Deprecation Warning: declaring rules with cant_all will be deprecated on major release 3.0, use cannot_all instead"
164
89
  cannot_all
165
90
  end
91
+
92
+ private
93
+ def bali_scrap_actors(*params)
94
+ self.current_subtargets = []
95
+ params.each do |passed_argument|
96
+ if passed_argument.is_a?(Symbol) || passed_argument.is_a?(String)
97
+ self.current_subtargets << passed_argument
98
+ elsif passed_argument.is_a?(NilClass)
99
+ self.current_subtargets << passed_argument
100
+ elsif passed_argument.is_a?(Array)
101
+ self.current_subtargets += passed_argument
102
+ end
103
+ end
104
+ nil
105
+ end
106
+
107
+ def bali_scrap_shortcut_rules(*params)
108
+ self.shortcut_rules = {}
109
+ params.each do |passed_argument|
110
+ if passed_argument.is_a?(Hash)
111
+ self.shortcut_rules = passed_argument
112
+ end
113
+ end
114
+ nil
115
+ end
116
+
117
+ def bali_process_subtarget(subtarget)
118
+ target_class = self.map_rules_dsl.current_rule_class.target_class
119
+ rule_class = self.map_rules_dsl.current_rule_class
120
+
121
+ rule_group = rule_class.rules_for(subtarget)
122
+
123
+ if rule_group.nil?
124
+ rule_group = Bali::RuleGroup.new(target_class, subtarget)
125
+ end
126
+
127
+ self.current_rule_group = rule_group
128
+
129
+ if block_given?
130
+ yield
131
+ else
132
+ # auth_val is either can or cannot
133
+ shortcut_rules.each do |auth_val, operations|
134
+ if operations.is_a?(Array)
135
+ operations.each do |op|
136
+ rule = Bali::Rule.new(auth_val, op)
137
+ rule_group.add_rule(rule)
138
+ end
139
+ else
140
+ operation = operations # well, basically is 1 only
141
+ rule = Bali::Rule.new(auth_val, operation)
142
+ rule_group.add_rule(rule)
143
+ end
144
+ end # each rules
145
+ end # block_given?
146
+
147
+ # add current_rule_group
148
+ rule_class.add_rule_group(rule_group)
149
+
150
+ nil
151
+ end
152
+
153
+ # to define can and cant is basically using this method
154
+ def bali_process_auth_rules(auth_val, args)
155
+ conditional_hash = nil
156
+ operations = []
157
+
158
+ # scan args for options
159
+ args.each do |elm|
160
+ if elm.is_a?(Hash)
161
+ conditional_hash = elm
162
+ else
163
+ operations << elm
164
+ end
165
+ end
166
+
167
+ # add operation one by one
168
+ operations.each do |op|
169
+ rule = Bali::Rule.new(auth_val, op)
170
+ bali_embed_conditions(rule, conditional_hash)
171
+ self.current_rule_group.add_rule(rule)
172
+ end
173
+ end # bali_process_auth_rules
174
+
175
+ # process conditional statement in rule definition
176
+ def bali_embed_conditions(rule, conditional_hash = nil)
177
+ return if conditional_hash.nil?
178
+
179
+ condition_type = conditional_hash.keys[0].to_s.downcase
180
+ condition_type_symb = condition_type.to_sym
181
+
182
+ if condition_type_symb == :if || condition_type_symb == :unless
183
+ rule.decider = conditional_hash.values[0]
184
+ rule.decider_type = condition_type_symb
185
+ end
186
+ nil
187
+ end
166
188
  end # class
@@ -9,3 +9,9 @@ require_relative "rule/rule"
9
9
  require_relative "rule/rule_class"
10
10
  require_relative "rule/rule_group"
11
11
 
12
+ # role extractor
13
+ require_relative "role_extractor"
14
+
15
+ require_relative "judger/judge"
16
+ require_relative "judger/negative_judge"
17
+ require_relative "judger/positive_judge"
@@ -11,9 +11,28 @@ class Bali::AuthorizationError < Bali::Error
11
11
  # whether a class or an object
12
12
  attr_accessor :target
13
13
 
14
+ def target_proper_class
15
+ if target.is_a?(Class)
16
+ target
17
+ else
18
+ target.class
19
+ end
20
+ end
21
+
14
22
  def to_s
15
- # better error message for nil, so that it won't be empty
16
- role = self.role ? self.role : "<nil>"
17
- "Role #{role} is performing #{operation} using precedence #{auth_level}"
23
+ role = humanise_value(self.role)
24
+ operation = humanise_value(self.operation)
25
+ auth_level = humanise_value(self.auth_level)
26
+
27
+ if auth_level == :can
28
+ "Role #{role} is not allowed to perform operation `#{operation}` on #{target_proper_class}"
29
+ else
30
+ "Role #{role} is allowed to perform operation `#{operation}` on #{target_proper_class}"
31
+ end
18
32
  end
33
+
34
+ private
35
+ def humanise_value(val)
36
+ val.nil? ? "<nil>" : val
37
+ end
19
38
  end
@@ -0,0 +1,329 @@
1
+ module Bali::Judger
2
+ # FUZY-ed value is happen when it is not really clear, need further cross checking,
3
+ # whether it is really allowed or not. It happens for example in block with others, such as this:
4
+ #
5
+ # role :finance do
6
+ # cannot :view
7
+ # end
8
+ # others do
9
+ # can :view
10
+ # can :index
11
+ # end
12
+ #
13
+ # In the example above, objecting cannot view on finance will result in STRONG_FALSE, but
14
+ # objecting can index on finance will result in FUZY_TRUE.
15
+ #
16
+ # Eventually, all FUZY value will be normal TRUE or FALSE if no definite counterpart
17
+ # is found/defined
18
+ BALI_FUZY_FALSE = -2
19
+ BALI_FUZY_TRUE = 2
20
+ BALI_FALSE = -1
21
+ BALI_TRUE = 1
22
+
23
+ class Judge
24
+ attr_accessor :original_subtarget
25
+ attr_accessor :subtarget
26
+ attr_accessor :operation
27
+ # record can be the class, or an instance of a class
28
+ attr_accessor :record
29
+
30
+ # determine if this judger should not call other judger
31
+ attr_accessor :cross_checking
32
+
33
+ # this class is abstract, shouldn't be initialized
34
+ def initialize(unconstructable = true)
35
+ if unconstructable
36
+ raise Bali::Error, "Bali::Judge::Judger is unconstructable, properly construct by using build to get a concrete class!"
37
+ end
38
+ self
39
+ end
40
+
41
+ def self.build(auth_level, options = {})
42
+ judge = nil
43
+ if auth_level == :can
44
+ judge = Bali::Judger::PositiveJudge.new
45
+ elsif auth_level == :cannot
46
+ judge = Bali::Judger::NegativeJudge.new
47
+ else
48
+ raise Bali::Error, "Unable to find judge for `#{auth_level}` case"
49
+ end
50
+
51
+ judge.original_subtarget = options[:original_subtarget]
52
+ judge.subtarget = options[:subtarget]
53
+ judge.operation = options[:operation]
54
+ judge.record = options[:record]
55
+ judge.cross_checking = false
56
+
57
+ judge
58
+ end
59
+
60
+ def clone(options = {})
61
+ if options[:reverse]
62
+ new_judge = Bali::Judger::Judge.build(self.reverse_auth_level)
63
+ else
64
+ new_judge = Bali::Judger::Judge.build(self.auth_level)
65
+ end
66
+
67
+ new_judge.subtarget = subtarget
68
+ new_judge.operation = operation
69
+ new_judge.record = record
70
+ new_judge.cross_checking = cross_checking
71
+ new_judge.original_subtarget = original_subtarget
72
+
73
+ new_judge
74
+ end
75
+
76
+ def record_class
77
+ record.is_a?(Class) ? record : record.class
78
+ end
79
+
80
+ def rule_group
81
+ unless @rule_group_checked
82
+ @rule_group = Bali::Integrators::Rule.rule_group_for(record_class, subtarget)
83
+ @rule_group_checked = true
84
+ end
85
+ @rule_group
86
+ end
87
+
88
+ def other_rule_group
89
+ unless @other_rule_group_checked
90
+ @other_rule_group = Bali::Integrators::Rule.rule_group_for(record_class, "__*__")
91
+ @other_rule_group_checked = true
92
+ end
93
+ @other_rule_group
94
+ end
95
+
96
+ def rule
97
+ unless @rule_checked
98
+ # rule group may be nil, for when user checking for undefined rule group
99
+ if rule_group
100
+ @rule = rule_group.get_rule(auth_level, operation)
101
+ else
102
+ self.rule = nil
103
+ end
104
+ end
105
+ @rule
106
+ end
107
+
108
+ def rule=(the_rule)
109
+ @rule = the_rule
110
+ @rule_checked = true
111
+ @rule
112
+ end
113
+
114
+ def otherly_rule
115
+ unless @otherly_rule_checked
116
+ if other_rule_group
117
+ # retrieve rule from others group
118
+ @otherly_rule = other_rule_group.get_rule(auth_level, operation)
119
+ @otherly_rule_checked = true
120
+ end
121
+ end
122
+ @otherly_rule
123
+ end
124
+
125
+ # return either true or false
126
+ # options can specify if returning raw, by specifying holy: true
127
+ def judgement(options = {})
128
+ # the divine judgement will come to thee, O Thou
129
+ # the doer of truth. return raw, untranslated to true/false.
130
+ our_holy_judgement = nil
131
+
132
+ # default of can? is false whenever RuleClass for that class is undefined
133
+ # or RuleGroup for that subtarget is not defined
134
+ if rule_group.nil?
135
+ if other_rule_group.nil?
136
+ # no more chance for checking
137
+ our_holy_judgement = natural_value
138
+ end
139
+ end
140
+
141
+ if our_holy_judgement.nil? && need_to_check_for_intervention?
142
+ our_holy_judgement = check_intervention
143
+ end
144
+
145
+ if our_holy_judgement.nil? &&
146
+ rule_group && rule_group.plant? &&
147
+ rule.nil? && otherly_rule.nil?
148
+ our_holy_judgement = natural_value
149
+ end
150
+
151
+ if our_holy_judgement.nil? && rule.nil?
152
+ cross_check_value = nil
153
+ # default if can? for undefined rule is false, after related clause
154
+ # cannot be found in cannot?
155
+ unless cross_checking
156
+ reversed_self = self.clone reverse: true
157
+ reversed_self.cross_checking = true
158
+ cross_check_value = reversed_self.judgement holy: true
159
+ end
160
+
161
+ # if cross check value nil, then the reverse rule is not defined,
162
+ # let's determine whether he is zeus or plant
163
+ if cross_check_value.nil?
164
+ # rule_group can be nil for when user checking under undefined rule-group
165
+ if rule_group
166
+ if rule_group.plant?
167
+ our_holy_judgement = plant_return_value
168
+ end
169
+
170
+ if rule_group.zeus?
171
+ our_holy_judgement = zeus_return_value
172
+ end
173
+ end # if rule_group exist
174
+ else
175
+ # process value from cross checking
176
+
177
+ if can_use_otherly_rule?(cross_check_value, cross_checking)
178
+ # give chance to check at others block
179
+ self.rule = otherly_rule
180
+ else
181
+ our_holy_judgement = cross_check_reverse_value(cross_check_value)
182
+ end
183
+ end
184
+ end # if our judgement nil and rule is nil
185
+
186
+ # if our holy judgement is still nil, but rule is defined
187
+ if our_holy_judgement.nil? && rule
188
+ if rule.has_decider?
189
+ our_holy_judgement = get_decider_result(rule, original_subtarget, record)
190
+ else
191
+ our_holy_judgement = default_positive_return_value
192
+ end
193
+ end
194
+
195
+ # return fuzy if otherly rule defines contrary to this auth_level
196
+ if our_holy_judgement.nil? && rule.nil? && (other_rule_group && other_rule_group.get_rule(reverse_auth_level, operation))
197
+ if rule_group && (rule_group.zeus? || rule_group.plant?)
198
+ # don't overwrite our holy judgement with fuzy value if rule group
199
+ # zeus/plant, because zeus/plant is more definite than any fuzy values
200
+ # eventhough the rule is abstractly defined
201
+ else
202
+ our_holy_judgement = default_negative_fuzy_return_value
203
+ end
204
+ end
205
+
206
+ # if at this point still nil, well,
207
+ # return the natural value for this judge
208
+ if our_holy_judgement.nil?
209
+ if otherly_rule
210
+ our_holy_judgement = BALI_FUZY_TRUE
211
+ else
212
+ our_holy_judgement = natural_value
213
+ end
214
+ end
215
+
216
+ holy = !!options[:holy]
217
+ return holy ? our_holy_judgement : translate_holy_judgement(our_holy_judgement)
218
+ end
219
+
220
+ private
221
+ # translate response for value above to traditional true/false
222
+ # holy judgement refer to non-standard true/false being used inside Bali
223
+ # which need to be translated from other beings to know
224
+ def translate_holy_judgement(bali_bool_value)
225
+ unless bali_bool_value.is_a?(Integer)
226
+ raise Bali::Error, "Expect bali value to be an Integer, got: `#{bali_bool_value}`"
227
+ end
228
+ if bali_bool_value < 0
229
+ return false
230
+ elsif bali_bool_value > 0
231
+ return true
232
+ end
233
+ end
234
+
235
+ def can_use_otherly_rule?(cross_check_value, is_cross_checking)
236
+ # either if rule from others block is defined, and the result so far is fuzy
237
+ # or, otherly rule is defined, and it is still a cross check
238
+ # plus, the result is not a definite BALI_TRUE/BALI_FALSE
239
+ #
240
+ # rationalisation:
241
+ # 1. Definite answer such as BALI_TRUE and BALI_FALSE is to be prioritised over
242
+ # FUZY answer, because definite answer is not gathered from others block where
243
+ # FUZY answer is. Therefore, it is an intended result
244
+ # 2. If the answer is FUZY, otherly_rule only be considered if the result
245
+ # is either FUZY TRUE or FUZY FALSE, or
246
+ # 3. Or, when already in cross check mode, we cannot retrieve cross_check_value
247
+ # what we can is instead, if otherly rule is available, just to try the odd
248
+ (!otherly_rule.nil? && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
249
+ (!otherly_rule.nil? && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
250
+ (!otherly_rule.nil? && is_cross_checking && cross_check_value.nil?)
251
+ end
252
+
253
+ # if after cross check (checking for cannot) the return is false,
254
+ # meaning for us, (checking for can), the return have to be true
255
+ def cross_check_reverse_value(cross_check_value)
256
+ # either the return is not fuzy, or otherly rule is undefined
257
+ if cross_check_value == BALI_TRUE
258
+ return BALI_FALSE
259
+ elsif cross_check_value == BALI_FALSE
260
+ return BALI_TRUE
261
+ elsif cross_check_value == BALI_FUZY_FALSE
262
+ return BALI_FUZY_TRUE
263
+ elsif cross_check_value == BALI_FUZY_TRUE
264
+ return BALI_FUZY_FALSE
265
+ else
266
+ raise Bali::Error, "Unknown how to process cross check value: `#{cross_check_value}`"
267
+ end
268
+ end # cross_check_reverse_value
269
+
270
+ def check_intervention
271
+ if rule.nil?
272
+ self_clone = self.clone reverse: true
273
+ self_clone.cross_checking = true
274
+
275
+ check_val = self_clone.judgement holy: true
276
+
277
+ # check further whether contradicting rule is defined to overwrite this
278
+ # super-power either can_all or cannot_all rule
279
+ if check_val == BALI_TRUE
280
+ # it is defined, must overwrite
281
+ return BALI_FALSE
282
+ else
283
+ # futher inspection said no such overwriting value is exist
284
+ return BALI_TRUE
285
+ end # check_val
286
+ end # if rule nil
287
+ end # check intervention
288
+
289
+ # what is the result when decider is executed
290
+ # rule: the rule object
291
+ # original subtarget: raw, unprocessed arugment passed as subtarget
292
+ def get_decider_result(rule, original_subtarget, record)
293
+ # must test first
294
+ decider = rule.decider
295
+ case decider.arity
296
+ when 0
297
+ if rule.decider_type == :if
298
+ return decider.() ? BALI_TRUE : BALI_FALSE
299
+ elsif rule.decider_type == :unless
300
+ unless decider.()
301
+ return BALI_TRUE
302
+ else
303
+ return BALI_FALSE
304
+ end
305
+ end
306
+ when 1
307
+ if rule.decider_type == :if
308
+ return decider.(record) ? BALI_TRUE : BALI_FALSE
309
+ elsif rule.decider_type == :unless
310
+ unless decider.(record)
311
+ return BALI_TRUE
312
+ else
313
+ return BALI_FALSE
314
+ end
315
+ end
316
+ when 2
317
+ if rule.decider_type == :if
318
+ return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
319
+ elsif rule.decider_type == :unless
320
+ unless decider.(record, original_subtarget)
321
+ return BALI_TRUE
322
+ else
323
+ return BALI_FALSE
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end # class
329
+ end # module