rails-acu 1.2.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 (117) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.project +18 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +30 -0
  6. data/Gemfile +30 -0
  7. data/Gemfile.lock +169 -0
  8. data/MIT-LICENSE +20 -0
  9. data/README.md +221 -0
  10. data/Rakefile +26 -0
  11. data/bin/rails +13 -0
  12. data/lib/acu/configs.rb +30 -0
  13. data/lib/acu/engine.rb +9 -0
  14. data/lib/acu/errors.rb +37 -0
  15. data/lib/acu/helpers/helpers.rb +9 -0
  16. data/lib/acu/injectors.rb +15 -0
  17. data/lib/acu/listeners.rb +18 -0
  18. data/lib/acu/monitor.rb +201 -0
  19. data/lib/acu/rules.rb +134 -0
  20. data/lib/acu/utilities.rb +14 -0
  21. data/lib/acu/version.rb +3 -0
  22. data/lib/generators/acu/install_generator.rb +20 -0
  23. data/lib/generators/templates/rules.rb +34 -0
  24. data/lib/generators/templates/setup.rb +30 -0
  25. data/lib/rails-acu.rb +26 -0
  26. data/rails-acu-1.2.0.gem +0 -0
  27. data/rails-acu.gemspec +23 -0
  28. data/spec/dummy/Rakefile +6 -0
  29. data/spec/dummy/app/assets/config/manifest.js +5 -0
  30. data/spec/dummy/app/assets/javascripts/admin/manage.js +2 -0
  31. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  32. data/spec/dummy/app/assets/javascripts/books.js +2 -0
  33. data/spec/dummy/app/assets/javascripts/cable.js +12 -0
  34. data/spec/dummy/app/assets/javascripts/comments.js +2 -0
  35. data/spec/dummy/app/assets/javascripts/home.js +2 -0
  36. data/spec/dummy/app/assets/stylesheets/admin/manage.css +4 -0
  37. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  38. data/spec/dummy/app/assets/stylesheets/books.css +4 -0
  39. data/spec/dummy/app/assets/stylesheets/comments.css +4 -0
  40. data/spec/dummy/app/assets/stylesheets/home.css +4 -0
  41. data/spec/dummy/app/assets/stylesheets/scaffold.css +84 -0
  42. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  43. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  44. data/spec/dummy/app/controllers/admin/manage_controller.rb +19 -0
  45. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  46. data/spec/dummy/app/controllers/home_controller.rb +7 -0
  47. data/spec/dummy/app/helpers/admin/manage_helper.rb +2 -0
  48. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  49. data/spec/dummy/app/helpers/home_helper.rb +2 -0
  50. data/spec/dummy/app/jobs/application_job.rb +2 -0
  51. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  52. data/spec/dummy/app/models/application_record.rb +3 -0
  53. data/spec/dummy/app/models/user.rb +8 -0
  54. data/spec/dummy/app/models/user_type.rb +3 -0
  55. data/spec/dummy/app/views/admin/manage/add.html.erb +2 -0
  56. data/spec/dummy/app/views/admin/manage/delete.html.erb +2 -0
  57. data/spec/dummy/app/views/admin/manage/index.html.erb +2 -0
  58. data/spec/dummy/app/views/admin/manage/list.html.erb +2 -0
  59. data/spec/dummy/app/views/admin/manage/prove.html.erb +2 -0
  60. data/spec/dummy/app/views/admin/manage/show.html.erb +2 -0
  61. data/spec/dummy/app/views/home/contact.html.erb +2 -0
  62. data/spec/dummy/app/views/home/index.html.erb +21 -0
  63. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  64. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  65. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  66. data/spec/dummy/bin/bundle +3 -0
  67. data/spec/dummy/bin/rails +4 -0
  68. data/spec/dummy/bin/rake +4 -0
  69. data/spec/dummy/bin/setup +34 -0
  70. data/spec/dummy/bin/update +29 -0
  71. data/spec/dummy/config.ru +5 -0
  72. data/spec/dummy/config/application.rb +23 -0
  73. data/spec/dummy/config/boot.rb +5 -0
  74. data/spec/dummy/config/cable.yml +9 -0
  75. data/spec/dummy/config/database.yml +25 -0
  76. data/spec/dummy/config/environment.rb +5 -0
  77. data/spec/dummy/config/environments/development.rb +54 -0
  78. data/spec/dummy/config/environments/production.rb +86 -0
  79. data/spec/dummy/config/environments/test.rb +42 -0
  80. data/spec/dummy/config/initializers/acu_rules.rb +31 -0
  81. data/spec/dummy/config/initializers/acu_setup.rb +14 -0
  82. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  83. data/spec/dummy/config/initializers/assets.rb +11 -0
  84. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  85. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  86. data/spec/dummy/config/initializers/devise.rb +277 -0
  87. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  88. data/spec/dummy/config/initializers/inflections.rb +16 -0
  89. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  90. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  91. data/spec/dummy/config/initializers/session_store.rb +3 -0
  92. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  93. data/spec/dummy/config/locales/devise.en.yml +64 -0
  94. data/spec/dummy/config/locales/en.yml +23 -0
  95. data/spec/dummy/config/puma.rb +47 -0
  96. data/spec/dummy/config/routes.rb +19 -0
  97. data/spec/dummy/config/secrets.yml +22 -0
  98. data/spec/dummy/config/spring.rb +6 -0
  99. data/spec/dummy/db/migrate/20170329111257_create_books.rb +9 -0
  100. data/spec/dummy/db/migrate/20170329111323_create_comments.rb +10 -0
  101. data/spec/dummy/db/migrate/20170329114943_devise_create_users.rb +42 -0
  102. data/spec/dummy/db/migrate/20170329120950_create_admin_user_types.rb +15 -0
  103. data/spec/dummy/db/migrate/20170329121612_add_user_type_id_to_users.rb +5 -0
  104. data/spec/dummy/db/schema.rb +59 -0
  105. data/spec/dummy/db/seeds.rb +39 -0
  106. data/spec/dummy/public/404.html +67 -0
  107. data/spec/dummy/public/422.html +67 -0
  108. data/spec/dummy/public/500.html +66 -0
  109. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  110. data/spec/dummy/public/apple-touch-icon.png +0 -0
  111. data/spec/dummy/public/favicon.ico +0 -0
  112. data/spec/dummy/spec/controllers/admin/manage_controller_spec.rb +72 -0
  113. data/spec/dummy/spec/controllers/application_controller_spec.rb +14 -0
  114. data/spec/dummy/spec/controllers/home_controller_spec.rb +560 -0
  115. data/spec/rails_helper.rb +59 -0
  116. data/spec/spec_helper.rb +104 -0
  117. metadata +268 -0
@@ -0,0 +1,26 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Acu'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
6
+ ENGINE_PATH = File.expand_path('../../lib/acu/engine', __FILE__)
7
+
8
+ # Set up gems listed in the Gemfile.
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
10
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11
+
12
+ require 'rails/all'
13
+ require 'rails/engine/commands'
@@ -0,0 +1,30 @@
1
+ module Acu
2
+ module Configs
3
+
4
+ mattr_accessor :base_controller
5
+ @@base_controller = 'ApplicationController'
6
+
7
+ mattr_accessor :allow_by_default
8
+ @@allow_by_default = false
9
+
10
+ mattr_accessor :audit_log_file
11
+ @@audit_log_file = nil
12
+
13
+ mattr_accessor :use_cache
14
+ @@use_cache = false
15
+
16
+ mattr_accessor :cache_namespace
17
+ @@cache_namespace = 'acu'
18
+
19
+ mattr_accessor :cache_expires_in
20
+ @@cache_expires_in = nil
21
+
22
+ mattr_accessor :cache_race_condition_ttl
23
+ @@cache_race_condition_ttl = nil
24
+
25
+ # for getting options
26
+ def self.get name
27
+ eval("@@#{name}")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module Acu
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Acu
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ module Acu
2
+ module Errors
3
+
4
+ class AccessDenied < StandardError
5
+ end
6
+
7
+ class UncheckedPermissions < StandardError
8
+ end
9
+
10
+ class InvalidSyntax < StandardError
11
+ end
12
+
13
+ class AmbiguousRule < StandardError
14
+ end
15
+
16
+ class InvalidData < StandardError
17
+ end
18
+
19
+ class MissingData < InvalidData
20
+ end
21
+
22
+ class MissingEntity < MissingData
23
+ end
24
+
25
+ class MissingUser < MissingData
26
+ end
27
+
28
+ class MissingAction < MissingData
29
+ end
30
+
31
+ class MissingController < MissingData
32
+ end
33
+
34
+ class MissingNamespace < MissingData
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ def acu_is? symbol
2
+ flag = false
3
+ [symbol].flatten.each { |s| flag |= Acu::Monitor.valid_for? s }
4
+ flag
5
+ end
6
+
7
+ def acu_as symbol
8
+ yield if acu_is? symbol
9
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'helpers/helpers'
2
+
3
+ module Acu
4
+ module Injectors
5
+ class << self
6
+
7
+ ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |**args|
8
+ eval((Acu::Configs.get :base_controller).to_s).class_eval do
9
+ before_action { Monitor::gaurd }
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module Acu
2
+ module Listeners
3
+
4
+ @@collective_data = { }
5
+
6
+ class << self
7
+
8
+ attr_reader :collective_data
9
+
10
+ ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |**args|
11
+ # collects the request parameters for the given [controller, action to be used in Monitor]
12
+ @@collective_data = @@collective_data.merge([:request, :parameters].reverse.inject(args[:params] || { }) { |v, k| {k => v}})
13
+ end
14
+ # get access to collected data
15
+ def data; @@collective_data end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,201 @@
1
+ require_relative 'errors'
2
+
3
+ module Acu
4
+
5
+ class Monitor
6
+
7
+ @kwargs = { }
8
+
9
+ class << self
10
+
11
+ protected :new
12
+ attr_reader :kwargs
13
+
14
+ def by kwargs
15
+ @kwargs = @kwargs.merge(kwargs)
16
+ end
17
+
18
+ def clear_args
19
+ @kwargs = { }
20
+ end
21
+
22
+ def gaurd
23
+ # fetch the request & process it
24
+ _info = process Acu::Listeners.data[:request]
25
+
26
+ # return if we hit the cache
27
+ return if hit_cache _info
28
+
29
+ rules = Rules.rules.select do |cond, _|
30
+ flag = true;
31
+
32
+ # check if this is a global rule!
33
+ next true if cond.empty?
34
+
35
+ {namespace: nil, controller: :namespace, action: :controller}.each do |current, parent|
36
+ t = -1
37
+ # either mentioned explicitly
38
+ if cond[current]
39
+ t = (cond[current][:name].to_s == eval("_info.#{current}").to_s) ? 1 : 0
40
+ # or in `only|except` tags
41
+ elsif parent and cond[parent]
42
+ # if nothing mentioned in parent, assume for all
43
+ t = 1 if not(cond[parent][:only] or cond[parent][:except])
44
+ # flag true if it checked in namespace's only tag
45
+ {only: {on_true: 1, on_false: 0} , except: {on_true: 0, on_false: 1}}.each do |tag, val|
46
+ if cond[parent][tag]
47
+ case cond[parent][tag].include? eval("_info.#{current}").to_sym
48
+ when true
49
+ t = val[:on_true]
50
+ break
51
+ when false
52
+ t = val[:on_false]
53
+ end
54
+ end
55
+ end
56
+ end
57
+ flag &= (t == 1) if t.between? 0, 1;
58
+ break if not flag
59
+ end
60
+ flag
61
+ end
62
+ # flag so we can process all the related rule and it all passed this should be false
63
+ # if any failed, and exception will be raised
64
+ _granted = -1
65
+ _entitled_entities = [];
66
+ # for each mached rule
67
+ rules.each do |_, rule|
68
+ # for each entity and it's actions in the rule
69
+ rule.each do |entity, action|
70
+ # check it the current request can relay to the entity?
71
+ if valid_for? entity
72
+ _entitled_entities << entity.to_s
73
+ # current entity is granted to have the access?
74
+ if is_allowed? action
75
+ # grant the access if already not denied
76
+ _granted = 1 if _granted == -1
77
+ else
78
+ # deny it, period!
79
+ _granted = 0
80
+ end
81
+ end
82
+ end
83
+ end
84
+ # if the access is granted? i.e if all the rules are satisfied with the request
85
+ return if _granted == 1 and access_granted _info, _entitled_entities
86
+ # if the access is denied? i.e at least one of rules are NOT satisfied with the request
87
+ return if _granted == 0 and access_denied _info, _entitled_entities
88
+ # if we reached here it measn that have found no rule to deny/allow the request and we have to fallback to the defaults
89
+ access_denied _info, [:__ACU_BY_DEFAULT__], by_default: true if not Configs.get :allow_by_default
90
+ access_granted _info, [:__ACU_BY_DEFAULT__], by_default: true
91
+ end
92
+
93
+ def valid_for? entity
94
+ # check for existance
95
+ raise Errors::MissingEntity.new("whois :#{entity}?") if not Rules.entities[entity]
96
+ # fetch the entity's identity
97
+ e = Rules.entities[entity]
98
+ # fetch the related args to the entity from the `kwargs`
99
+ kwargs = @kwargs.reject { |x| !e[:args].include?(x) }
100
+ # if fetched args and pre-defined arg didn't match?
101
+ raise Errors::MissingData.new("at least one of arguments for `whois :#{entity}` is not provided!") if kwargs.length != e[:args].length
102
+ # send varibles in order the have defined
103
+ e[:callback].call(*e[:args].map { |i| kwargs[i] })
104
+ end
105
+
106
+ def clear_cache
107
+ return if not Configs.get :use_cache
108
+ Rails.cache.clear namespace: (Configs.get :cache_namespace)
109
+ end
110
+
111
+ protected
112
+
113
+ def hit_cache _info
114
+ # return [didn't hit] if not allowed to use cache
115
+ return false if not Configs.get :use_cache
116
+ # fetch the relative entities to this request
117
+ _entitled_entities = Rules.entities.select { |name, _| valid_for? name }
118
+ # fetch the cache-name
119
+ cname = cache_name _info, _entitled_entities
120
+ # return [didn't hit] if not found in cache
121
+ return false if not Rails.cache.exist? cname, cache_options
122
+ # check if the request is allowed in cache?
123
+ if is_allowed?(Rails.cache.read(cname, cache_options).to_s.to_sym)
124
+ # grant the access
125
+ access_granted _info, _entitled_entities.keys, from_cache: true
126
+ else
127
+ # deny the access
128
+ access_denied _info, _entitled_entities.keys, from_cache: true
129
+ end
130
+ # hit the cache
131
+ return true
132
+ end
133
+
134
+ def cache_name _info, entities
135
+ "%s-%s" %[_info.to_a.join('::'), (entities.kind_of?(Array) ? entities : entities.keys).join("-")]
136
+ end
137
+
138
+ def is_allowed? action
139
+ case action
140
+ when Rules.GRANT_SYMBOL
141
+ return true
142
+ when Rules.DENY_SYMBOL
143
+ return false
144
+ else
145
+ log_audit "> access DENIED to undefined action as `:#{action}`"
146
+ raise Exception.new("action `#{action}` is undefined!")
147
+ end
148
+ end
149
+
150
+ def cache_options
151
+ out = { }
152
+ # fetch cache options from config
153
+ [:namespace, :expires_in, :race_condition_ttl].each { |k| out[k] = Configs.get "cache_#{k}".to_sym }
154
+ return out
155
+ end
156
+
157
+ def log_audit log
158
+ # fetch the log file from configuration
159
+ file = Configs.get :audit_log_file
160
+ # log if allowed?
161
+ Logger.new(Configs.get :audit_log_file).info(log) if file and not file.blank?
162
+ end
163
+
164
+ def access_granted _info, entities, by_default: false, from_cache: false
165
+ # log the event
166
+ log_audit ("[-]" + (from_cache ? '[c]' : '') + " access GRANTED to `#{_info}` as `:#{entities.uniq.join(", :")}`" + (by_default ? " [autherized by :allow_by_default]" : ""))
167
+ # cache the event if not already from cache
168
+ Rails.cache.write(cache_name(_info, entities), Rules.GRANT_SYMBOL, cache_options) if not from_cache and Configs.get :use_cache
169
+ # grant the access
170
+ true
171
+ end
172
+
173
+ def access_denied _info, entities, by_default: false, from_cache: false
174
+ # log the event
175
+ log_audit ("[x]" + (from_cache ? '[c]' : '') + " access DENIED to `#{_info}` as `:#{entities.uniq.join(", :")}`" + (by_default ? " [autherized by :allow_by_default]" : ""))
176
+ # cache the event if not already from cache
177
+ Rails.cache.write(cache_name(_info, entities), Rules.DENY_SYMBOL, cache_options) if not from_cache and Configs.get :use_cache
178
+ # deny the access
179
+ raise Errors::AccessDenied.new("you don't have the enough access for process this request!")
180
+ end
181
+
182
+ def process request
183
+ # validate the request parameters
184
+ raise Errors::InvalidData.new("the request object needs to provided!") if not(request and request[:parameters])
185
+ # fetch the params
186
+ p = request[:parameters]
187
+ # try find the namespace/controller set
188
+ nc = p["controller"].split('/');
189
+
190
+ n = nc.length > 1 ? nc.first : nil
191
+ c = nc.length > 1 ? nc.second : nc.first
192
+ a = p["action"]
193
+
194
+ # return it with structure
195
+ Struct.new(:namespace, :controller, :action).new(n, c, a)
196
+ end
197
+
198
+ end # /class << self
199
+ end # /class Monitor
200
+
201
+ end # /module Acu
@@ -0,0 +1,134 @@
1
+ require_relative 'utilities'
2
+
3
+ module Acu
4
+ class Rules
5
+
6
+ @rules = { }
7
+ @entities = { }
8
+
9
+ @GRANT_SYMBOL = :allow
10
+ @DENY_SYMBOL = :deny
11
+
12
+ class << self
13
+
14
+ protected :new
15
+ attr_reader :rules
16
+ attr_reader :entities
17
+ attr_reader :GRANT_SYMBOL
18
+ attr_reader :DENY_SYMBOL
19
+
20
+ include Utilities
21
+
22
+ def initialize
23
+ reset
24
+ end
25
+
26
+ def reset
27
+ @rules = { }
28
+ @entities = { }
29
+ end
30
+
31
+ # only: only the defined `controllers` in the `namespace`
32
+ # except: except the defined `controllers` in the `namespace`
33
+ def namespace name = nil, except: nil, only: nil
34
+ only = nil if only and not (only.kind_of?(Array) or only.length == 0)
35
+ except = nil if except and not (except.kind_of?(Array) or except.length == 0)
36
+ raise Errors::AmbiguousRule.new('cannot have both `only` and `except` options at the same time for namespace `%s`' %name) if only and except
37
+ pass namespace: { name: name ? name.downcase : name, except: except, only: only } do
38
+ yield
39
+ end
40
+ end
41
+
42
+ # only: only the defined `actions` in the `controller`
43
+ # except: except the defined `actions` in the `controller`
44
+ def controller name, except: nil, only: nil
45
+ only = nil if only and not (only.kind_of?(Array) or only.length == 0)
46
+ except = nil if except and not (except.kind_of?(Array) or except.length == 0)
47
+ raise Errors::AmbiguousRule.new("there is already an `except` or `only` constraints defined in container namespace `#{@_params[:namespace][:name]}`") if @_params[:namespace] and (@_params[:namespace][:except] || @_params[:namespace][:only])
48
+ raise Errors::AmbiguousRule.new('cannot have both `only` and `except` options at the same time for controller `%s`' %name) if only and except
49
+ pass controller: { name: name.downcase, except: except, only: only } do
50
+ yield
51
+ end
52
+ end
53
+
54
+ def action name
55
+ raise Errors::AmbiguousRule.new("at least one of the parent `controller` or `namespace` needs to be defined for the this action") if not (@_params[:namespace] || @_params[:controller])
56
+ raise Errors::AmbiguousRule.new("there is already an `except` or `only` constraints defined in container controller `#{@_params[:controller][:name]}`") if @_params[:controller] and (@_params[:controller][:except] || @_params[:controller][:only])
57
+ pass action: { name: name.downcase } do
58
+ yield
59
+ end
60
+ end
61
+
62
+ def define(&block)
63
+ helper_initialize
64
+ self.instance_eval(&block)
65
+ end
66
+
67
+ # locks the rules to be read-only
68
+ def lock
69
+ self.freeze
70
+ end
71
+
72
+ def whois(symbol, args: nil, &block)
73
+ @entities[symbol] = {
74
+ args: [args || []].flatten,
75
+ callback: block
76
+ }
77
+ end
78
+
79
+ #################### the ops ######################
80
+ # at this point we assign the class varible rules #
81
+ ###################################################
82
+
83
+ def allow symbol, on: []
84
+ op symbol, @GRANT_SYMBOL, on
85
+ end
86
+
87
+ def deny symbol, on: []
88
+ op symbol, @DENY_SYMBOL, on
89
+ end
90
+
91
+ ################### end of ops ####################
92
+
93
+ protected
94
+
95
+ def op symbol, opr, on
96
+ symbol = [symbol].flatten if symbol
97
+ raise Errors::InvalidData.new("invalid argument") if not symbol or symbol.to_s.blank? or opr.to_s.blank?
98
+ raise Errors::AmbiguousRule.new("cannot have `on` argument inside the action `#{@_params[:action][:name]}`") if not on.empty? and @_params[:action]
99
+ raise Errors::InvalidData.new("the symbol `#{symbol}` is not defined by `whois`") if not symbol.all? { |s| @entities.include? s }
100
+ return if on.empty? and symbol.each { |s| build_rule({"#{s}": opr}) }
101
+ # for each action in the `on` create a new rule
102
+ on.each do |a|
103
+ action a do
104
+ symbol.each { |s| build_rule({"#{s}": opr}) }
105
+ end
106
+ end
107
+ end
108
+
109
+ def build_rule rule
110
+ @rules[@_params.clone] ||= {}
111
+ @rules[@_params.clone] = rules[@_params.clone].merge(rule);
112
+ end
113
+
114
+ def build_rule_entry
115
+ n = @_params[:namespace]
116
+ c = @_params[:controller]
117
+ a = @_params[:action]
118
+ raise Errors::AmbiguousRule.new('invalid input') if not ( n or c or a )
119
+ raise Errors::AmbiguousRule.new('cannot have rule for controller `%s` inside the namespace `%s` that `except`ed it!' %[c[:name], n[:name]]) if n and n[:except] and c and n[:except].include? c[:name]
120
+ raise Errors::AmbiguousRule.new('cannot have rule for action `%s` inside the controler `%s` that `except`ed it!' %[a[:name], c[:name]]) if c and c[:except] and a and c[:except].include? a[:name]
121
+ raise Errors::AmbiguousRule.new('cannot have rule for controller `%s` inside the namespace `%s` that has bounded to `only` some other controllers!' %[c[:name], n[:name]]) if n and n[:only] and c and not(n[:only].include? c[:name])
122
+ raise Errors::AmbiguousRule.new('cannot have rule for action `%s` inside the controller `%s` that has bounded to `only` some other actions!' %[a[:name], c[:name]]) if c and c[:only] and a and not(c[:only].include? a[:name])
123
+
124
+ entries = [];
125
+
126
+ entries << :namespace if n
127
+ entries << :controller if c
128
+ entries << :action if a
129
+
130
+ return entries
131
+ end
132
+ end
133
+ end
134
+ end