bali 2.4.0 → 6.0.1

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 (54) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +44 -2
  5. data/gemfiles/Gemfile-rails.5.0.x +7 -0
  6. data/gemfiles/Gemfile-rails.5.1.x +6 -0
  7. data/gemfiles/Gemfile-rails.5.2.x +6 -0
  8. data/gemfiles/Gemfile-rails.6.0.x +5 -0
  9. data/gemfiles/Gemfile-rails.edge +14 -0
  10. data/lib/bali.rb +33 -22
  11. data/lib/bali/config.rb +12 -0
  12. data/lib/bali/dsl_error.rb +3 -0
  13. data/lib/bali/{foundations/exceptions/bali_error.rb → error.rb} +0 -0
  14. data/lib/bali/judge.rb +221 -0
  15. data/lib/bali/printer.rb +42 -31
  16. data/lib/bali/rails/action_controller.rb +17 -0
  17. data/lib/bali/rails/action_view.rb +12 -0
  18. data/lib/bali/rails/active_record.rb +11 -0
  19. data/lib/bali/railtie.rb +13 -0
  20. data/lib/bali/role.rb +110 -0
  21. data/lib/bali/rspec/able_to_matcher.rb +39 -0
  22. data/lib/bali/rule.rb +17 -0
  23. data/lib/bali/ruler.rb +38 -0
  24. data/lib/bali/rules.rb +51 -0
  25. data/lib/bali/statics/active_record.rb +2 -0
  26. data/lib/bali/statics/authorizer.rb +65 -0
  27. data/lib/bali/statics/record.rb +13 -0
  28. data/lib/bali/statics/scope_ruler.rb +39 -0
  29. data/lib/bali/tasks/bali/print_rules.rake +9 -0
  30. data/lib/bali/version.rb +1 -1
  31. data/lib/generators/rails/USAGE +8 -0
  32. data/lib/generators/rails/rules_generator.rb +17 -0
  33. data/lib/generators/rails/templates/rules.rb +4 -0
  34. data/lib/generators/rspec/rules_generator.rb +12 -0
  35. data/lib/generators/rspec/templates/rules_spec.rb +7 -0
  36. metadata +131 -49
  37. data/lib/bali/dsl/map_rules_dsl.rb +0 -75
  38. data/lib/bali/dsl/rules_for_dsl.rb +0 -130
  39. data/lib/bali/foundations/all_foundations.rb +0 -17
  40. data/lib/bali/foundations/exceptions/authorization_error.rb +0 -38
  41. data/lib/bali/foundations/exceptions/dsl_error.rb +0 -3
  42. data/lib/bali/foundations/exceptions/objection_error.rb +0 -3
  43. data/lib/bali/foundations/judger/judge.rb +0 -329
  44. data/lib/bali/foundations/judger/negative_judge.rb +0 -40
  45. data/lib/bali/foundations/judger/positive_judge.rb +0 -41
  46. data/lib/bali/foundations/role_extractor.rb +0 -61
  47. data/lib/bali/foundations/rule/rule.rb +0 -55
  48. data/lib/bali/foundations/rule/rule_class.rb +0 -54
  49. data/lib/bali/foundations/rule/rule_group.rb +0 -91
  50. data/lib/bali/integrators/all_integrators.rb +0 -8
  51. data/lib/bali/integrators/rule_class_integrator.rb +0 -27
  52. data/lib/bali/integrators/rule_group_integrator.rb +0 -29
  53. data/lib/bali/integrators/rule_integrator.rb +0 -56
  54. data/lib/bali/objector.rb +0 -173
@@ -1,17 +0,0 @@
1
- # ./exceptions
2
- require_relative "exceptions/bali_error"
3
- require_relative "exceptions/dsl_error"
4
- require_relative "exceptions/objection_error"
5
- require_relative "exceptions/authorization_error"
6
-
7
- # rule
8
- require_relative "rule/rule"
9
- require_relative "rule/rule_class"
10
- require_relative "rule/rule_group"
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"
@@ -1,38 +0,0 @@
1
- # Error that will be raised when subtarget cannot do something he wanted to do,
2
- # or when subtarget do something he should not be allowed to do.
3
- class Bali::AuthorizationError < Bali::Error
4
- attr_accessor :operation
5
- attr_accessor :auth_level
6
- attr_accessor :role
7
-
8
- # it may be nil, depends on whether rule checking is using symbol/user
9
- attr_accessor :subtarget
10
-
11
- # whether a class or an object
12
- attr_accessor :target
13
-
14
- def target_proper_class
15
- if target.is_a?(Class)
16
- target
17
- else
18
- target.class
19
- end
20
- end
21
-
22
- def to_s
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
32
- end
33
-
34
- private
35
- def humanise_value(val)
36
- val.nil? ? "<nil>" : val
37
- end
38
- end
@@ -1,3 +0,0 @@
1
- # this error is thrown when there is incorrect syntax in the DSL
2
- class Bali::DslError < Bali::Error
3
- end
@@ -1,3 +0,0 @@
1
- # this error is thrown when objection is cannot be executed
2
- class Bali::ObjectionError < Bali::Error
3
- end
@@ -1,329 +0,0 @@
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::Integrator::RuleGroup.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::Integrator::RuleGroup.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
@@ -1,40 +0,0 @@
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
@@ -1,41 +0,0 @@
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