bali 2.4.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- M2I3NWNjNmVlNWM4YzQyMGU0YzAyNzBmZGJmNTBjMTUxMGYxOWMxYg==
5
- data.tar.gz: !binary |-
6
- MzhkMjlhYzA4NDU3M2M4ODE1ODkxZTU0ZDA3NDJjZGUwODNmOTZhYg==
2
+ SHA256:
3
+ metadata.gz: 5f2c3170fb8da9fc0a76becddae855901f97cef723cd9db86073e0e2c15d177c
4
+ data.tar.gz: e9555504164bfb3155d281cfbf4d40629c347d178b76b7eaed2c83caacdcfc4a
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MTJhOTRkODJhMWM5OTIzNjA0NTQ0OWVjMTMzOTgwZDFlZDBhZjQzNjQ0N2Y2
10
- YWViMDExYzMyZjI5Y2YyOTYxNDdhZWJmYWU5YTVlNDYwODgwZDA1NDhhZWRm
11
- NTliMTI1ZGEyMjNjY2QyNGEwNTM5YjNjNDc5OGIxYjk2MTYyNTI=
12
- data.tar.gz: !binary |-
13
- NTMzMzBjNTczZmM2MDE1YmI0NjA0ZjY1MDRhZGZmNWRiNTRjZGYzMDRmMWI4
14
- Zjk4ZTEzNGZkZDg0NzFmZjg0YjYwNDQ2OGVlYjgwNzkwNTY4MGFlZDE1NjI3
15
- ZDI1ZjlhNmFhODA4OGQzZDFhMjBkNjdjMjk1ZTI0YTkzZmQ4MTY=
6
+ metadata.gz: d2241cfc888b7b1c3ea4210c6a0dfc26ba790e8c190631b86d616000fbe4e927bea425f73b1547025c849f9ffc6b01c9f5389129ba9707258c79bf6a31c2c248
7
+ data.tar.gz: 472f04be142d2e7c42549f2927218c3ca57e880c11c86f2a6d409d0682ea76303299a33f065e070fa8ab38821a408a5ad37507f5d7189210d5baf68c231d4be4
data/.gitignore CHANGED
@@ -7,10 +7,12 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /spec/test_app/tmp
10
11
 
11
12
  *.swp
12
13
  *.swo
13
14
  *.gemspec
14
15
  *.gem
16
+ *.log
15
17
 
16
18
  plugins
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --require spec_helper
@@ -1,4 +1,46 @@
1
1
  language: ruby
2
+
3
+ env:
4
+ global:
5
+ - CC_TEST_REPORTER_ID=76cedf4d3aa437009b269ff68b901102ca24baeb46245eb94fca42e21bba20a7
6
+
2
7
  rvm:
3
- - 2.2.2
4
- before_install: gem install bundler -v 1.10.6
8
+ - 2.6.5
9
+
10
+ before_install:
11
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
12
+ - chmod +x ./cc-test-reporter
13
+ - ./cc-test-reporter before-build
14
+ - sudo apt update -qq
15
+ - gem i bundler -v '<2'
16
+
17
+ cache: bundler
18
+
19
+ script:
20
+ - bundle exec rspec
21
+
22
+ after_script:
23
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
24
+
25
+ matrix:
26
+ include:
27
+ - rvm: 2.7.0
28
+ gemfile: gemfiles/Gemfile-rails.6.0.x
29
+ - rvm: 2.6.3
30
+ gemfile: gemfiles/Gemfile-rails.6.0.x
31
+ - rvm: 2.6.3
32
+ gemfile: gemfiles/Gemfile-rails.5.2.x
33
+ - rvm: 2.4.4
34
+ gemfile: gemfiles/Gemfile-rails.5.2.x
35
+ - rvm: 2.6.3
36
+ gemfile: gemfiles/Gemfile-rails.5.1.x
37
+ - rvm: 2.6.3
38
+ gemfile: gemfiles/Gemfile-rails.5.0.x
39
+ - rvm: 2.4.4
40
+ gemfile: gemfiles/Gemfile-rails.5.0.x
41
+ - rvm: ruby-head
42
+ gemfile: gemfiles/Gemfile-rails.edge
43
+
44
+ allow_failures:
45
+ - rvm: ruby-head
46
+ - gemfile: gemfiles/Gemfile-rails.edge
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', '~> 5.0.0'
6
+ gem 'rspec-rails', '~> 3.9'
7
+ gem 'sqlite3', '< 1.4'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', '~> 5.1.0'
6
+ gem 'rspec-rails', '~> 3.9'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', git: 'https://github.com/rails/rails', branch: '5-2-stable'
6
+ gem 'rspec-rails', '~> 3.9'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', '~> 6.0.0'
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ git_source(:github) do |repo_name|
6
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
7
+ "https://github.com/#{repo_name}.git"
8
+ end
9
+
10
+ github 'rails/rails' do
11
+ gem 'rails'
12
+ end
13
+
14
+ gem 'rspec-rails', '~> 4.0'
@@ -1,33 +1,44 @@
1
1
  require_relative "bali/version"
2
2
 
3
- # load the DSL syntax maker definition, ordered by proper order of invocation
4
- require_relative "bali/dsl/map_rules_dsl"
5
- require_relative "bali/dsl/rules_for_dsl"
6
-
7
- require_relative "bali/objector"
8
- require_relative "bali/printer"
3
+ begin
4
+ require "rails"
5
+ require "rails/generators"
6
+ rescue LoadError => e
7
+ # ignores
8
+ end
9
9
 
10
- require_relative "bali/foundations/all_foundations"
11
- require_relative "bali/integrators/all_integrators"
10
+ require "zeitwerk"
11
+ loader = Zeitwerk::Loader.for_gem
12
+ loader.ignore("#{__dir__}/generators")
13
+ loader.ignore("#{__dir__}/bali/rails")
14
+ loader.ignore("#{__dir__}/bali/rspec")
15
+ loader.setup
12
16
 
13
17
  module Bali
14
- # mapping class to a RuleClass
15
- RULE_CLASS_MAP = {}
18
+ extend self
19
+
20
+ def config
21
+ @config ||= Bali::Config.new
22
+ end
16
23
 
17
- # {
18
- # User: :roles,
19
- # AdminUser: :admin_roles
20
- # }
21
- TRANSLATED_SUBTARGET_ROLES = {}
24
+ def configure
25
+ yield config
26
+ end
22
27
 
23
- extend self
24
- def map_rules(&block)
25
- dsl_map_rules = Bali::MapRulesDsl.new
26
- dsl_map_rules.instance_eval(&block)
28
+ if defined? Rails
29
+ require "bali/railtie"
30
+ require "bali/rails/action_controller"
31
+ require "bali/rails/action_view"
32
+ require "bali/rails/active_record"
27
33
  end
28
34
 
29
- def clear_rules
30
- Bali::RULE_CLASS_MAP.clear
31
- true
35
+ if defined? RSpec
36
+ begin
37
+ require "rspec/matchers"
38
+ require "bali/rspec/able_to_matcher"
39
+ rescue LoadError => e
40
+ end
32
41
  end
33
42
  end
43
+
44
+ loader.eager_load
@@ -0,0 +1,12 @@
1
+ class Bali::Config
2
+ attr_accessor :rules_path
3
+ attr_accessor :suffix
4
+
5
+ def initialize
6
+ if Rails.respond_to?(:root) && Rails.root
7
+ @rules_path = Rails.root.join("app", "rules")
8
+ end
9
+
10
+ @suffix = "Rules"
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ # Error class to represent incorrect usage of syntax
2
+ class Bali::DslError < Bali::Error
3
+ end
@@ -0,0 +1,221 @@
1
+ class Bali::Judge
2
+ # Fuzy value is possible when the evaluation is not yet clear cut, for example in this case:
3
+ #
4
+ # role :finance do
5
+ # cant :view
6
+ # end
7
+ #
8
+ # others do
9
+ # can :view
10
+ # can :index
11
+ # end
12
+ #
13
+ # Checking cant view for finance role results in a definite false, but
14
+ # checking on index for the same role results in FUZY_TRUE. Eventually, all FUZY value will be
15
+ # normal TRUE or FALSE if no definite counterpart is found.
16
+
17
+ FUZY_FALSE = -2
18
+ FUZY_TRUE = 2
19
+ DEFINITE_FALSE = -1
20
+ DEFINITE_TRUE = 1
21
+
22
+ attr_accessor :term,
23
+ :actor,
24
+ :role,
25
+ :operation,
26
+ :record,
27
+ :should_cross_check
28
+
29
+ class << self
30
+ def default_judgement_value(term)
31
+ case term
32
+ when :can then false
33
+ when :cant then true
34
+ end
35
+ end
36
+
37
+ def check(term, actor_or_roles, operation, record)
38
+ if operation.nil?
39
+ # eg: user.can? :sign_in
40
+ operation = actor_or_roles
41
+ actor_or_roles = nil
42
+ end
43
+
44
+ judgement_value = default_value = default_judgement_value(term)
45
+ roles = Bali::Role.formalize actor_or_roles
46
+
47
+ roles.each do |role|
48
+ judge = Bali::Judge.new(
49
+ term: term,
50
+ role: role,
51
+ actor: actor_or_roles,
52
+ operation: operation,
53
+ record: record
54
+ )
55
+
56
+ judgement_value = judge.judgement
57
+ break if judgement_value != default_value
58
+ end
59
+
60
+ judgement_value
61
+ end
62
+ end
63
+
64
+ def initialize(term:,
65
+ actor:,
66
+ role:,
67
+ operation:,
68
+ record:,
69
+ should_cross_check: true)
70
+
71
+ @term = term
72
+ @role = role
73
+ @actor = actor
74
+ @operation = operation
75
+ @record = record
76
+ @should_cross_check = should_cross_check
77
+ end
78
+
79
+ def judgement
80
+ judgement = natural_value if no_rule_group?
81
+
82
+ if judgement.nil? && rule.nil?
83
+ cross_check_value = nil
84
+ # default if can? for undefined rule is false, after related clause
85
+ # cant be found in cant?
86
+ cross_check_value = cross_check_judge.judgement if should_cross_check
87
+
88
+ # if cross check value nil, then the reverse rule is not defined,
89
+ # let's determine whether they can do anything or not
90
+ if cross_check_value.nil?
91
+ judgement = deduce_from_defined_disposition
92
+ else
93
+ # process value from cross checking
94
+ if otherly_rule && (cross_check_value == FUZY_FALSE || cross_check_value == FUZY_TRUE)
95
+ # give chance to check at others block
96
+ @rule = otherly_rule
97
+ else
98
+ judgement = reverse_value(cross_check_value)
99
+ end
100
+ end
101
+ end
102
+
103
+ judgement ||= deduce_by_evaluation ||
104
+ deduce_from_fuzy_rules ||
105
+ natural_value
106
+
107
+ return !should_cross_check ?
108
+ judgement :
109
+
110
+ # translate response for value above to traditional true/false
111
+ # holy judgement refer to non-standard true/false being used inside Bali
112
+ # which need to be translated from other beings to know
113
+ judgement > 0
114
+ end
115
+
116
+ private
117
+
118
+ def record_class
119
+ record.is_a?(Class) ? record : record.class
120
+ end
121
+
122
+ def ruler
123
+ @ruler ||= Bali::Ruler.for record_class
124
+ end
125
+
126
+ def rule_group_for(role)
127
+ ruler.nil? ? nil : ruler[role]
128
+ end
129
+
130
+ def rule_group
131
+ @rule_group ||= rule_group_for role
132
+ end
133
+
134
+ def other_rule_group
135
+ @other_rule_group ||= rule_group_for nil
136
+ end
137
+
138
+ def no_rule_group?
139
+ rule_group.nil? && other_rule_group.nil?
140
+ end
141
+
142
+ def rule
143
+ # rule group may be nil, for when user checking for undefined rule group
144
+ @rule ||= rule_group ? rule_group.find_rule(term, operation) : nil
145
+ end
146
+
147
+ def otherly_rule
148
+ @otherly_rule ||= other_rule_group ? other_rule_group.find_rule(term, operation) : nil
149
+ end
150
+
151
+ def cross_check_judge
152
+ @cross_check_judge ||= begin
153
+ Bali::Judge.new(
154
+ term: reversed_term,
155
+ role: role,
156
+ operation: operation,
157
+ record: record,
158
+ should_cross_check: false,
159
+ actor: actor
160
+ )
161
+ end
162
+ end
163
+
164
+ def reversed_term
165
+ case term
166
+ when :can then :cant
167
+ when :cant then :can
168
+ end
169
+ end
170
+
171
+ def natural_value
172
+ term == :cant ? DEFINITE_TRUE : DEFINITE_FALSE
173
+ end
174
+
175
+ def reverse_value(value)
176
+ case value
177
+ when DEFINITE_TRUE then DEFINITE_FALSE
178
+ when DEFINITE_FALSE then DEFINITE_TRUE
179
+ when FUZY_FALSE then FUZY_TRUE
180
+ when FUZY_TRUE then FUZY_FALSE
181
+ end
182
+ end
183
+
184
+ def evaluate(rule, actor, record)
185
+ conditional = rule.conditional
186
+ evaluation = case conditional.arity
187
+ when 0 then conditional.()
188
+ when 1 then conditional.(record)
189
+ when 2 then conditional.(record, actor)
190
+ end
191
+
192
+ evaluation ? DEFINITE_TRUE : DEFINITE_FALSE
193
+ end
194
+
195
+ def deduce_by_evaluation
196
+ return unless rule
197
+
198
+ rule.conditional? ?
199
+ evaluate(rule, actor, record) :
200
+ judgement = DEFINITE_TRUE
201
+ end
202
+
203
+ def deduce_from_defined_disposition
204
+ return unless rule_group
205
+
206
+ if rule_group.can_all?
207
+ reverse_value(natural_value)
208
+ elsif rule_group.cant_all?
209
+ natural_value
210
+ end
211
+ end
212
+
213
+ def deduce_from_fuzy_rules
214
+ reversed_otherly_rule = other_rule_group.find_rule(reversed_term, operation)
215
+
216
+ if reversed_otherly_rule
217
+ term == :cant ? FUZY_TRUE : FUZY_FALSE
218
+ end
219
+ end
220
+
221
+ end
@@ -2,62 +2,73 @@ require "stringio"
2
2
  require "date"
3
3
 
4
4
  # module that would allow all defined rules to be printed for check
5
- module Bali::Printer
6
- module_function
7
-
5
+ class Bali::Printer
6
+ include Singleton
7
+
8
8
  SEPARATOR = " " * 6
9
9
  SUBTARGET_TITLE_SEPARATOR = SEPARATOR + ("-" * 80) + "\n"
10
10
 
11
- def pretty_print
11
+ def self.printable
12
+ instance.printable
13
+ end
14
+
15
+ def self.pretty_print
16
+ printable
17
+ end
18
+
19
+ def load_rule_classes
20
+ return unless Bali.config.rules_path.present?
21
+
22
+ Dir["#{Bali.config.rules_path}/**/*.rb"].each do |rule_class_path|
23
+ require rule_class_path
24
+ end
25
+ rescue LoadError
26
+ # ignore
27
+ end
28
+
29
+ def printable
30
+ load_rule_classes
12
31
  output = StringIO.new
13
32
 
14
33
  # build up the string for pretty printing rules
15
- Bali::RULE_CLASS_MAP.each do |klass, rule_class|
16
- output << "===== #{klass.to_s} =====\n\n"
17
-
18
- rule_class.rule_groups.each do |subtarget, rule_group|
19
- print_rule_group(rule_group, output)
20
- end
34
+ rule_classes = ObjectSpace.each_object(Class).select { |cls| cls < Bali::Rules }
35
+ rule_classes.sort! { |a, b| a.to_s <=> b.to_s }
36
+ rule_classes.each do |rule_class|
37
+ output << "===== #{rule_class.model_class} =====\n\n"
21
38
 
22
- if rule_class.others_rule_group.rules.any?
23
- print_rule_group(rule_class.others_rule_group, output)
39
+ rule_class.ruler.roles.each do |subtarget, role|
40
+ print_role role, output
24
41
  end
42
+
25
43
  output << "\n\n"
26
44
  end
27
45
 
28
- output << "\n\n"
29
- output << DateTime.now.strftime("Printed at %d-%m-%Y %I:%M%p %Z")
46
+ output << DateTime.now.strftime("Printed at %Y-%m-%d %I:%M%p %Z")
30
47
 
31
48
  output.string
32
49
  end
33
50
 
34
- def print_rule_group(rule_group, target_io)
35
- target = rule_group.target.to_s
36
- subtarget = rule_group.subtarget.to_s.capitalize
37
- subtarget = "Others" if subtarget == "__*__"
38
- is_zeus = rule_group.zeus?
39
- is_plant = rule_group.plant?
51
+ def print_role role, target_io
52
+ subtarget = role.name.to_s.capitalize
53
+ subtarget = "By default" if subtarget.blank?
54
+ can_all = role.can_all?
40
55
  counter = 0
41
56
 
42
- target_io << "#{SEPARATOR}#{subtarget}, can all: #{is_zeus}, cannot all: #{is_plant}\n"
57
+ target_io << "#{SEPARATOR}#{subtarget}\n"
43
58
  target_io << SUBTARGET_TITLE_SEPARATOR
44
-
45
- if is_zeus
59
+
60
+ if can_all
46
61
  target_io << "#{SEPARATOR} #{counter+=1}. #{subtarget} can do anything except if explicitly stated otherwise\n"
47
- elsif is_plant
48
- target_io << "#{SEPARATOR} #{counter+=1}. #{subtarget} cannot do anything except if explicitly stated otherwise\n"
49
62
  end
50
-
51
- rule_group.rules.each do |rule|
63
+
64
+ role.rules.each do |rule|
52
65
  written_rule = StringIO.new
53
- written_rule << "#{SEPARATOR} #{counter+=1}. #{subtarget} #{rule.auth_val} #{rule.operation} #{target}"
54
- if rule.has_decider?
66
+ written_rule << "#{SEPARATOR} #{counter+=1}. #{subtarget} #{rule.term} #{rule.operation}"
67
+ if rule.conditional?
55
68
  written_rule << ", with condition"
56
69
  end
57
70
  written_rule << "\n"
58
71
  target_io << written_rule.string
59
72
  end
60
-
61
- target_io << "\n"
62
73
  end
63
74
  end