acl9 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +32 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +888 -0
- data/Rakefile +40 -0
- data/TODO +42 -0
- data/VERSION.yml +4 -0
- data/lib/acl9.rb +16 -0
- data/lib/acl9/config.rb +10 -0
- data/lib/acl9/controller_extensions.rb +85 -0
- data/lib/acl9/controller_extensions/dsl_base.rb +229 -0
- data/lib/acl9/controller_extensions/generators.rb +197 -0
- data/lib/acl9/helpers.rb +19 -0
- data/lib/acl9/model_extensions.rb +133 -0
- data/lib/acl9/model_extensions/object.rb +59 -0
- data/lib/acl9/model_extensions/subject.rb +175 -0
- data/test/access_control_test.rb +338 -0
- data/test/dsl_base_test.rb +758 -0
- data/test/helpers_test.rb +93 -0
- data/test/roles_test.rb +310 -0
- data/test/support/controllers.rb +207 -0
- data/test/support/models.rb +47 -0
- data/test/support/schema.rb +69 -0
- data/test/test_helper.rb +31 -0
- metadata +103 -0
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
desc 'Default: run tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'jeweler'
|
10
|
+
Jeweler::Tasks.new do |s|
|
11
|
+
s.name = "acl9"
|
12
|
+
s.summary = "Yet another role-based authorization system for Rails"
|
13
|
+
s.email = "olegdashevskii@gmail.com"
|
14
|
+
s.homepage = "http://github.com/be9/acl9"
|
15
|
+
s.description = "Role-based authorization system for Rails with a nice DSL for access control lists"
|
16
|
+
s.authors = ["oleg dashevskii"]
|
17
|
+
s.files = FileList["[A-Z]*", "{lib,test}/**/*.rb"]
|
18
|
+
s.add_development_dependency "jeremymcanally-context", ">= 0.5.5"
|
19
|
+
s.add_development_dependency "jnunemaker-matchy", ">= 0.4.0"
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |test|
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.pattern = 'test/**/*_test.rb'
|
29
|
+
test.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
require 'yard'
|
34
|
+
|
35
|
+
YARD::Rake::YardocTask.new do |t|
|
36
|
+
t.files = ['lib/**/*.rb']
|
37
|
+
#t.options = ['--any', '--extra', '--opts'] # optional
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
* Complex roles with ANDing.
|
2
|
+
|
3
|
+
Something like:
|
4
|
+
|
5
|
+
access_control do
|
6
|
+
allow all, :except => :destroy
|
7
|
+
allow complex_role, :to => :destroy do
|
8
|
+
is :strong
|
9
|
+
is :decisive
|
10
|
+
is :owner, :of => :object
|
11
|
+
is_not :banned
|
12
|
+
is_not :fake
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
* Acl9-based menu generator.
|
17
|
+
|
18
|
+
If you get Access Denied on /secrets/index, probably you shouldn't see "Secrets" item
|
19
|
+
in the menu at all.
|
20
|
+
|
21
|
+
It can be very DRY. Say, we introduce :menu => true option to access_control method which
|
22
|
+
will make it register a lambda (can see/cannot see) in some global hash (indexed by controller name).
|
23
|
+
|
24
|
+
Then, given an URL, you'll be able to check it against this hash. /secrets/index is mapped to
|
25
|
+
SecretsController#index, so you run access_control_hash['SecretsController'].call('index') and
|
26
|
+
show the link only if true is returned.
|
27
|
+
|
28
|
+
The problem here is with objects. SecretsController's access_control block can reference instance
|
29
|
+
variables during the permission check, but we have only current instantiated controller which can be any.
|
30
|
+
|
31
|
+
Another option is to distinguish visible part from access control part.
|
32
|
+
|
33
|
+
menu do
|
34
|
+
item 'Home', home_path
|
35
|
+
item 'Secrets', secrets_path do
|
36
|
+
allow :trusted
|
37
|
+
end
|
38
|
+
|
39
|
+
# ...
|
40
|
+
end
|
41
|
+
|
42
|
+
Here only "trusted" users will see "Secrets" item.
|
data/VERSION.yml
ADDED
data/lib/acl9.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'acl9', 'config')
|
2
|
+
|
3
|
+
if defined? ActiveRecord::Base
|
4
|
+
require File.join(File.dirname(__FILE__), 'acl9', 'model_extensions')
|
5
|
+
|
6
|
+
ActiveRecord::Base.send(:include, Acl9::ModelExtensions)
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
if defined? ActionController::Base
|
11
|
+
require File.join(File.dirname(__FILE__), 'acl9', 'controller_extensions')
|
12
|
+
require File.join(File.dirname(__FILE__), 'acl9', 'helpers')
|
13
|
+
|
14
|
+
ActionController::Base.send(:include, Acl9::ControllerExtensions)
|
15
|
+
Acl9Helpers = Acl9::Helpers unless defined?(Acl9Helpers)
|
16
|
+
end
|
data/lib/acl9/config.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'controller_extensions', 'generators')
|
2
|
+
|
3
|
+
module Acl9
|
4
|
+
module ControllerExtensions
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def access_control(*args, &block)
|
11
|
+
opts = args.extract_options!
|
12
|
+
|
13
|
+
case args.size
|
14
|
+
when 0 then true
|
15
|
+
when 1
|
16
|
+
meth = args.first
|
17
|
+
|
18
|
+
if meth.is_a? Symbol
|
19
|
+
opts[:as_method] = meth
|
20
|
+
else
|
21
|
+
raise ArgumentError, "access_control argument must be a :symbol!"
|
22
|
+
end
|
23
|
+
else
|
24
|
+
raise ArgumentError, "Invalid arguments for access_control"
|
25
|
+
end
|
26
|
+
|
27
|
+
subject_method = opts[:subject_method] || Acl9::config[:default_subject_method]
|
28
|
+
|
29
|
+
raise ArgumentError, "Block must be supplied to access_control" unless block
|
30
|
+
|
31
|
+
filter = opts[:filter]
|
32
|
+
filter = true if filter.nil?
|
33
|
+
|
34
|
+
case helper = opts[:helper]
|
35
|
+
when true
|
36
|
+
raise ArgumentError, "you should specify :helper => :method_name" if !opts[:as_method]
|
37
|
+
when nil then nil
|
38
|
+
else
|
39
|
+
if opts[:as_method]
|
40
|
+
raise ArgumentError, "you can't specify both method name and helper name"
|
41
|
+
else
|
42
|
+
opts[:as_method] = helper
|
43
|
+
filter = false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
method = opts[:as_method]
|
48
|
+
|
49
|
+
query_method_available = true
|
50
|
+
generator = case
|
51
|
+
when method && filter
|
52
|
+
Acl9::Dsl::Generators::FilterMethod.new(subject_method, method)
|
53
|
+
when method && !filter
|
54
|
+
query_method_available = false
|
55
|
+
Acl9::Dsl::Generators::BooleanMethod.new(subject_method, method)
|
56
|
+
else
|
57
|
+
Acl9::Dsl::Generators::FilterLambda.new(subject_method)
|
58
|
+
end
|
59
|
+
|
60
|
+
generator.acl_block!(&block)
|
61
|
+
|
62
|
+
generator.install_on(self, opts)
|
63
|
+
|
64
|
+
if query_method_available && (query_method = opts.delete(:query_method))
|
65
|
+
case query_method
|
66
|
+
when true
|
67
|
+
if method
|
68
|
+
query_method = "#{method}?"
|
69
|
+
else
|
70
|
+
raise ArgumentError, "You must specify :query_method as Symbol"
|
71
|
+
end
|
72
|
+
when Symbol, String
|
73
|
+
# okay here
|
74
|
+
else
|
75
|
+
raise ArgumentError, "Invalid value for :query_method"
|
76
|
+
end
|
77
|
+
|
78
|
+
second_generator = Acl9::Dsl::Generators::BooleanMethod.new(subject_method, query_method)
|
79
|
+
second_generator.acl_block!(&block)
|
80
|
+
second_generator.install_on(self, opts)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
module Acl9
|
2
|
+
module Dsl
|
3
|
+
class Base
|
4
|
+
attr_reader :allows, :denys
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@default_action = nil
|
8
|
+
|
9
|
+
@allows = []
|
10
|
+
@denys = []
|
11
|
+
|
12
|
+
@original_args = args
|
13
|
+
end
|
14
|
+
|
15
|
+
def acl_block!(&acl_block)
|
16
|
+
instance_eval(&acl_block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_action
|
20
|
+
if @default_action.nil? then :deny else @default_action end
|
21
|
+
end
|
22
|
+
|
23
|
+
def allowance_expression
|
24
|
+
allowed_expr = if @allows.size > 0
|
25
|
+
@allows.map { |clause| "(#{clause})" }.join(' || ')
|
26
|
+
else
|
27
|
+
"false"
|
28
|
+
end
|
29
|
+
|
30
|
+
not_denied_expr = if @denys.size > 0
|
31
|
+
@denys.map { |clause| "!(#{clause})" }.join(' && ')
|
32
|
+
else
|
33
|
+
"true"
|
34
|
+
end
|
35
|
+
|
36
|
+
[allowed_expr, not_denied_expr].
|
37
|
+
map { |expr| "(#{expr})" }.
|
38
|
+
join(default_action == :deny ? ' && ' : ' || ')
|
39
|
+
end
|
40
|
+
|
41
|
+
alias to_s allowance_expression
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def default(default_action)
|
46
|
+
raise ArgumentError, "default can only be called once in access_control block" if @default_action
|
47
|
+
|
48
|
+
unless [:allow, :deny].include? default_action
|
49
|
+
raise ArgumentError, "invalid value for default (can be :allow or :deny)"
|
50
|
+
end
|
51
|
+
|
52
|
+
@default_action = default_action
|
53
|
+
end
|
54
|
+
|
55
|
+
def allow(*args)
|
56
|
+
@current_rule = :allow
|
57
|
+
_parse_and_add_rule(*args)
|
58
|
+
end
|
59
|
+
|
60
|
+
def deny(*args)
|
61
|
+
@current_rule = :deny
|
62
|
+
_parse_and_add_rule(*args)
|
63
|
+
end
|
64
|
+
|
65
|
+
def actions(*args, &block)
|
66
|
+
raise ArgumentError, "actions should receive at least 1 action as argument" if args.size < 1
|
67
|
+
|
68
|
+
subsidiary = self.class.new(*@original_args)
|
69
|
+
|
70
|
+
class <<subsidiary
|
71
|
+
def actions(*args)
|
72
|
+
raise ArgumentError, "You cannot use actions inside another actions block"
|
73
|
+
end
|
74
|
+
|
75
|
+
def default(*args)
|
76
|
+
raise ArgumentError, "You cannot use default inside an actions block"
|
77
|
+
end
|
78
|
+
|
79
|
+
def _set_action_clause(to, except)
|
80
|
+
raise ArgumentError, "You cannot use :to/:except inside actions block" if to || except
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
subsidiary.acl_block!(&block)
|
85
|
+
|
86
|
+
action_check = _action_check_expression(args)
|
87
|
+
|
88
|
+
squash = lambda do |rules|
|
89
|
+
_either_of(rules) + ' && ' + action_check
|
90
|
+
end
|
91
|
+
|
92
|
+
@allows << squash.call(subsidiary.allows) if subsidiary.allows.size > 0
|
93
|
+
@denys << squash.call(subsidiary.denys) if subsidiary.denys.size > 0
|
94
|
+
end
|
95
|
+
|
96
|
+
alias action actions
|
97
|
+
|
98
|
+
def logged_in; false end
|
99
|
+
def anonymous; nil end
|
100
|
+
def all; true end
|
101
|
+
|
102
|
+
alias everyone all
|
103
|
+
alias everybody all
|
104
|
+
alias anyone all
|
105
|
+
|
106
|
+
def _parse_and_add_rule(*args)
|
107
|
+
options = args.extract_options!
|
108
|
+
|
109
|
+
_set_action_clause(options.delete(:to), options.delete(:except))
|
110
|
+
|
111
|
+
object = _role_object(options)
|
112
|
+
|
113
|
+
role_checks = args.map do |who|
|
114
|
+
case who
|
115
|
+
when anonymous() then "#{_subject_ref}.nil?"
|
116
|
+
when logged_in() then "!#{_subject_ref}.nil?"
|
117
|
+
when all() then "true"
|
118
|
+
else
|
119
|
+
"!#{_subject_ref}.nil? && #{_subject_ref}.has_role?('#{who.to_s.singularize}', #{object})"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
[:if, :unless].each do |cond|
|
124
|
+
val = options[cond]
|
125
|
+
raise ArgumentError, "#{cond} option must be a Symbol" if val && !val.is_a?(Symbol)
|
126
|
+
end
|
127
|
+
|
128
|
+
condition = [
|
129
|
+
(_method_ref(options[:if]) if options[:if]),
|
130
|
+
("!#{_method_ref(options[:unless])}" if options[:unless])
|
131
|
+
].compact.join(' && ')
|
132
|
+
|
133
|
+
condition = nil if condition.blank?
|
134
|
+
|
135
|
+
_add_rule(case role_checks.size
|
136
|
+
when 0
|
137
|
+
raise ArgumentError, "allow/deny should have at least 1 argument"
|
138
|
+
when 1 then role_checks.first
|
139
|
+
else
|
140
|
+
_either_of(role_checks)
|
141
|
+
end, condition)
|
142
|
+
end
|
143
|
+
|
144
|
+
def _either_of(exprs)
|
145
|
+
exprs.map { |expr| "(#{expr})" }.join(' || ')
|
146
|
+
end
|
147
|
+
|
148
|
+
def _add_rule(what, condition)
|
149
|
+
anded = [what] + [@action_clause, condition].compact
|
150
|
+
anded[0] = "(#{anded[0]})" if anded.size > 1
|
151
|
+
|
152
|
+
(@current_rule == :allow ? @allows : @denys) << anded.join(' && ')
|
153
|
+
end
|
154
|
+
|
155
|
+
def _set_action_clause(to, except)
|
156
|
+
raise ArgumentError, "both :to and :except cannot be specified in the rule" if to && except
|
157
|
+
|
158
|
+
@action_clause = nil
|
159
|
+
|
160
|
+
action_list = to || except
|
161
|
+
return unless action_list
|
162
|
+
|
163
|
+
expr = _action_check_expression(action_list)
|
164
|
+
|
165
|
+
@action_clause = if to
|
166
|
+
"#{expr}"
|
167
|
+
else
|
168
|
+
"!#{expr}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def _action_check_expression(action_list)
|
173
|
+
unless action_list.is_a?(Array)
|
174
|
+
action_list = [ action_list.to_s ]
|
175
|
+
end
|
176
|
+
|
177
|
+
case action_list.size
|
178
|
+
when 0 then "true"
|
179
|
+
when 1 then "(#{_action_ref} == '#{action_list.first}')"
|
180
|
+
else
|
181
|
+
set_of_actions = "Set.new([" + action_list.map { |act| "'#{act}'"}.join(',') + "])"
|
182
|
+
|
183
|
+
"#{set_of_actions}.include?(#{_action_ref})"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
VALID_PREPOSITIONS = %w(of for in on at by).freeze unless defined? VALID_PREPOSITIONS
|
188
|
+
|
189
|
+
def _role_object(options)
|
190
|
+
object = nil
|
191
|
+
|
192
|
+
VALID_PREPOSITIONS.each do |prep|
|
193
|
+
if options[prep.to_sym]
|
194
|
+
raise ArgumentError, "You may only use one preposition to specify object" if object
|
195
|
+
|
196
|
+
object = options[prep.to_sym]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
case object
|
201
|
+
when Class
|
202
|
+
object.to_s
|
203
|
+
when Symbol
|
204
|
+
_object_ref object
|
205
|
+
when nil
|
206
|
+
"nil"
|
207
|
+
else
|
208
|
+
raise ArgumentError, "object specified by preposition can only be a Class or a Symbol"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def _subject_ref
|
213
|
+
raise
|
214
|
+
end
|
215
|
+
|
216
|
+
def _object_ref(object)
|
217
|
+
raise
|
218
|
+
end
|
219
|
+
|
220
|
+
def _action_ref
|
221
|
+
raise
|
222
|
+
end
|
223
|
+
|
224
|
+
def _method_ref(method)
|
225
|
+
raise
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'dsl_base')
|
2
|
+
|
3
|
+
module Acl9
|
4
|
+
##
|
5
|
+
# This exception is raised whenever ACL block finds that the current user
|
6
|
+
# is not authorized for the controller action he wants to execute.
|
7
|
+
# @example How to catch this exception in ApplicationController
|
8
|
+
# class ApplicationController < ActionController::Base
|
9
|
+
# rescue_from 'Acl9::AccessDenied', :with => :access_denied
|
10
|
+
#
|
11
|
+
# # ...other stuff...
|
12
|
+
# private
|
13
|
+
#
|
14
|
+
# def access_denied
|
15
|
+
# if current_user
|
16
|
+
# # It's presumed you have a template with words of pity and regret
|
17
|
+
# # for unhappy user who is not authorized to do what he wanted
|
18
|
+
# render :template => 'home/access_denied'
|
19
|
+
# else
|
20
|
+
# # In this case user has not even logged in. Might be OK after login.
|
21
|
+
# flash[:notice] = 'Access denied. Try to log in first.'
|
22
|
+
# redirect_to login_path
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
class AccessDenied < StandardError; end
|
28
|
+
|
29
|
+
##
|
30
|
+
# This exception is raised when acl9 has generated invalid code for the
|
31
|
+
# filtering method or block. Should never happen, and it's a bug when it
|
32
|
+
# happens.
|
33
|
+
class FilterSyntaxError < StandardError; end
|
34
|
+
|
35
|
+
module Dsl
|
36
|
+
module Generators
|
37
|
+
class BaseGenerator < Acl9::Dsl::Base
|
38
|
+
def initialize(*args)
|
39
|
+
@subject_method = args[0]
|
40
|
+
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def _access_denied
|
47
|
+
"raise Acl9::AccessDenied"
|
48
|
+
end
|
49
|
+
|
50
|
+
def _subject_ref
|
51
|
+
"#{_controller_ref}send(:#{@subject_method})"
|
52
|
+
end
|
53
|
+
|
54
|
+
def _object_ref(object)
|
55
|
+
"#{_controller_ref}instance_variable_get('@#{object}')"
|
56
|
+
end
|
57
|
+
|
58
|
+
def _action_ref
|
59
|
+
"#{_controller_ref}action_name"
|
60
|
+
end
|
61
|
+
|
62
|
+
def _method_ref(method)
|
63
|
+
"#{_controller_ref}send(:#{method})"
|
64
|
+
end
|
65
|
+
|
66
|
+
def _controller_ref
|
67
|
+
@controller ? "#{@controller}." : ''
|
68
|
+
end
|
69
|
+
|
70
|
+
def install_on(controller_class, options)
|
71
|
+
debug_dump(controller_class) if options[:debug]
|
72
|
+
end
|
73
|
+
|
74
|
+
def debug_dump(klass)
|
75
|
+
return unless logger
|
76
|
+
logger.debug "=== Acl9 access_control expression dump (#{klass.to_s})"
|
77
|
+
logger.debug self.to_s
|
78
|
+
logger.debug "======"
|
79
|
+
end
|
80
|
+
|
81
|
+
def logger
|
82
|
+
ActionController::Base.logger
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class FilterLambda < BaseGenerator
|
87
|
+
def initialize(subject_method)
|
88
|
+
super
|
89
|
+
|
90
|
+
@controller = 'controller'
|
91
|
+
end
|
92
|
+
|
93
|
+
def install_on(controller_class, options)
|
94
|
+
super
|
95
|
+
|
96
|
+
controller_class.send(:before_filter, options, &self.to_proc)
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_proc
|
100
|
+
code = <<-RUBY
|
101
|
+
lambda do |controller|
|
102
|
+
unless #{allowance_expression}
|
103
|
+
#{_access_denied}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
RUBY
|
107
|
+
|
108
|
+
self.instance_eval(code, __FILE__, __LINE__)
|
109
|
+
rescue SyntaxError
|
110
|
+
raise FilterSyntaxError, code
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
################################################################
|
115
|
+
|
116
|
+
class FilterMethod < BaseGenerator
|
117
|
+
def initialize(subject_method, method_name)
|
118
|
+
super
|
119
|
+
|
120
|
+
@method_name = method_name
|
121
|
+
@controller = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def install_on(controller_class, options)
|
125
|
+
super
|
126
|
+
_add_method(controller_class)
|
127
|
+
controller_class.send(:before_filter, @method_name, options)
|
128
|
+
end
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
def _add_method(controller_class)
|
133
|
+
code = self.to_method_code
|
134
|
+
controller_class.send(:class_eval, code, __FILE__, __LINE__)
|
135
|
+
rescue SyntaxError
|
136
|
+
raise FilterSyntaxError, code
|
137
|
+
end
|
138
|
+
|
139
|
+
def to_method_code
|
140
|
+
<<-RUBY
|
141
|
+
def #{@method_name}
|
142
|
+
unless #{allowance_expression}
|
143
|
+
#{_access_denied}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
RUBY
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
################################################################
|
151
|
+
|
152
|
+
class BooleanMethod < FilterMethod
|
153
|
+
def install_on(controller_class, opts)
|
154
|
+
debug_dump(controller_class) if opts[:debug]
|
155
|
+
|
156
|
+
_add_method(controller_class)
|
157
|
+
|
158
|
+
if opts[:helper]
|
159
|
+
controller_class.send(:helper_method, @method_name)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
165
|
+
def to_method_code
|
166
|
+
<<-RUBY
|
167
|
+
def #{@method_name}(*args)
|
168
|
+
options = args.extract_options!
|
169
|
+
|
170
|
+
unless args.size <= 1
|
171
|
+
raise ArgumentError, "call #{@method_name} with 0, 1 or 2 arguments"
|
172
|
+
end
|
173
|
+
|
174
|
+
action_name = args.empty? ? self.action_name : args.first.to_s
|
175
|
+
|
176
|
+
return #{allowance_expression}
|
177
|
+
end
|
178
|
+
RUBY
|
179
|
+
end
|
180
|
+
|
181
|
+
def _object_ref(object)
|
182
|
+
"(options[:#{object}] || #{super})"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
################################################################
|
187
|
+
|
188
|
+
class HelperMethod < BooleanMethod
|
189
|
+
def initialize(subject_method, method)
|
190
|
+
super
|
191
|
+
|
192
|
+
@controller = 'controller'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|