kantox-roles 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/README.md +224 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/kantox-roles.gemspec +40 -0
- data/lib/kantox/roles.rb +199 -0
- data/lib/kantox/roles/helpers.rb +138 -0
- data/lib/kantox/roles/logger.rb +102 -0
- data/lib/kantox/roles/strategies/aspect.rb +13 -0
- data/lib/kantox/roles/strategies/cancancan.rb +48 -0
- data/lib/kantox/roles/strategies/pundit.rb +70 -0
- data/lib/kantox/roles/strategies/strategy_error.rb +12 -0
- data/lib/kantox/roles/strategies/wrapper.rb +15 -0
- data/lib/kantox/roles/version.rb +5 -0
- data/lib/rails/generators/kantox/policy_spec_helper.rb.tmpl +35 -0
- data/lib/rails/generators/kantox/pundit_policy_generator.rb +234 -0
- metadata +178 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5120829150bcbfcb385b48107800baf61da72262
|
4
|
+
data.tar.gz: 756e3b82936790094c8d00ed90ff16e8d0610f7c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 96fd86d02e2c44e7b51c108c4e4f9fabb0fabe306dd5b38e12f2fc40fdcefd896bf95131dc5a1ab8a9627e5a5018a0b0e41eea213021dc58f7757d828ec93891
|
7
|
+
data.tar.gz: 17b617b94f4df54811ca013132aae12f812cd1cb0cde138fadda24e53d03879249453069bb3bedff6acdd33fedbbd9045061b51ca0b4b66ba89215449ea078c0
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
# Kantox::Roles
|
2
|
+
|
3
|
+
Kantox Roles is the library to transparently handle an authorization. It is
|
4
|
+
fully backend-agnostic. Current implementation contains a working example
|
5
|
+
of pundit wrapping.
|
6
|
+
|
7
|
+
The main goal is to separate the wheat from the chaff and not to pollute
|
8
|
+
model/controller classes with authorization stuff.
|
9
|
+
|
10
|
+
With Kantox Roles one is to define the rules in one or more yaml files:
|
11
|
+
|
12
|
+
```yaml
|
13
|
+
'Kantox::Managed':
|
14
|
+
:yo : 'aspect'
|
15
|
+
:yo2 : :aspect
|
16
|
+
:yo3 :
|
17
|
+
:runner: 'Kantox::Strategies::Wrapper#wrap'
|
18
|
+
:yo5 :
|
19
|
+
:lambda: '->(context, im) { 42 }'
|
20
|
+
:yo6 :
|
21
|
+
:kantox_managedhandler:
|
22
|
+
:params: ['param1', 'param2']
|
23
|
+
'Kantox::WildManaged':
|
24
|
+
'y*' : :aspect
|
25
|
+
```
|
26
|
+
|
27
|
+
There are four different kinds of handlers available:
|
28
|
+
|
29
|
+
* **simple** — like `aspect` above. There should be `Kantox::Strategies#aspect`
|
30
|
+
module function available. It will be called with parameters `context` and
|
31
|
+
`im` supplying the context instance (usually an instance of guarded controller
|
32
|
+
class) and the name of the guarded method in `Module::Class#method` notation.
|
33
|
+
The aforementioned function should _raise an exception_ of type
|
34
|
+
`Kantox::Strategies::StrategyError` whether the authority check is not passed.
|
35
|
+
* **runner** — the most powerful yet complicated guard. Should have the
|
36
|
+
executable module function as parameter in `Module::Class#method` notation.
|
37
|
+
This function will be called, yielding _`context`, `im` **and** the guarded
|
38
|
+
method, converted to `proc`_ as parameters. The typical usage:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
def my_runner context, params = nil
|
42
|
+
fail Kantox::Strategies::MyRunnerError unless check_passed
|
43
|
+
params[:user] = :demo if demo_mode
|
44
|
+
params[:credit_card].gsub /\d/, 'X'
|
45
|
+
yield *params if block_given?
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
As seen above, the full control over context is provided by this guard.
|
50
|
+
|
51
|
+
* **lambda** — the simple lambda, getting `context` and `im`. Is actually
|
52
|
+
a syntax sugar for the _simple_ guard
|
53
|
+
* **object** — used when there is a need to pass complicated parameters and/or
|
54
|
+
some other stuff to the guard. The class `Kantox::Managedhandler` must have
|
55
|
+
a constructor, accepting one argument (the hash of parameters) and `to_proc`
|
56
|
+
method, accepting two arguments (`context`, `im`). The class will be
|
57
|
+
instantiated with a parameters list and `to_proc` would be called on context.
|
58
|
+
|
59
|
+
## Methods to guard
|
60
|
+
|
61
|
+
Wildcard notation is allowed. That said, `'y*'` will guard all the methods
|
62
|
+
starting with `y`, and `'*'` will guard everything on the respective class.
|
63
|
+
|
64
|
+
## Deny ⇒ Allow
|
65
|
+
|
66
|
+
As soon as a method guard is specified in yaml file, the method is considered
|
67
|
+
as guarded. If there was an error finding the guard (class not exists, method
|
68
|
+
can not be instantiated etc,) the authorization request will be _rejected_.
|
69
|
+
|
70
|
+
## Rails integration
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
# controllers/admin/admin_controller.rb
|
74
|
+
|
75
|
+
require 'kantox/roles'
|
76
|
+
# Specify the top-parent class to guard
|
77
|
+
Kantox::Roles.init Admin::AdminController
|
78
|
+
# load strategies
|
79
|
+
Dir['strategies/**/*.yml'].each do |f|
|
80
|
+
Kantox::Roles.configure f
|
81
|
+
end
|
82
|
+
# load stopwords for logger
|
83
|
+
Kantox::Helpers.logger_stopwords File.join 'strategies', 'stopwords.txt'
|
84
|
+
Kantox::Helpers.info "Strategies were read: #{Kantox::Roles.options}"
|
85
|
+
```
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
# config/application.rb
|
89
|
+
|
90
|
+
# Policies are to be loaded on init explicitly
|
91
|
+
Dir[File.expand_path('../../app/policies/**/*', __FILE__)].each do |f|
|
92
|
+
require f[/(.*?)\.rb$/, 1] unless File.directory? f
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
```yaml
|
97
|
+
# strategiest/roles.yml
|
98
|
+
|
99
|
+
'Admin::TodosController' :
|
100
|
+
'*' : :pundit
|
101
|
+
...
|
102
|
+
```
|
103
|
+
|
104
|
+
## Pundit plug-in
|
105
|
+
|
106
|
+
This section shows how to integrate `pundit` to act as backend guard.
|
107
|
+
Everything one needs is to implement policies.
|
108
|
+
|
109
|
+
Everything in `TodosController` is to be handled by pundit. So, it’s time
|
110
|
+
to implement our `pundit` guard. Besides some syntax sugar stuff it is (complete implementation for `kantox-flow` may be seen [here](https://github.com/kantox/kantox-roles/wiki/Pundit-Policy):
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
# app/policies/pundit.rb
|
114
|
+
|
115
|
+
def pundit context, im
|
116
|
+
begin # Whoever does not reply on classify would be punished :)
|
117
|
+
model = context.instance_eval 'controller_path.classify'
|
118
|
+
policy = PolicyFactory.lookup model.split('::').last
|
119
|
+
unless policy.new(context.current_user, model).send("#{im.split('#').last}?")
|
120
|
+
fail PunditError.new(context, im, policy)
|
121
|
+
end
|
122
|
+
rescue NameError => e
|
123
|
+
Kantox::Helpers.err "Error punditing «#{context}». Will reject request.\nOriginal error: #{e}"
|
124
|
+
throw PunditError.new(context, im)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
module_function :pundit
|
128
|
+
```
|
129
|
+
Needless to say, the above handler is to be written once per backend. Pundit one is shipped with `Kantox::Roles`. The last but not least is to specify a strategy:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
# app/policies/todo_policy.rb
|
133
|
+
|
134
|
+
module Kantox
|
135
|
+
module Policies
|
136
|
+
def historic?
|
137
|
+
@user.admin? and [true, false].sample
|
138
|
+
end
|
139
|
+
alias_method :index?, :historic?
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
The above will randomly accept admin’s requests to a controller. I am pretty
|
146
|
+
sure one would do more sophisticated check here.
|
147
|
+
|
148
|
+
That’s it.
|
149
|
+
|
150
|
+
## Policies Generator
|
151
|
+
|
152
|
+
`Kantox::Roles` has it’s generator for creating policies.
|
153
|
+
|
154
|
+
### Generator invocation
|
155
|
+
|
156
|
+
```
|
157
|
+
$ bundle exec rails generate kantox:pundit_policy Todo --users Administrator SalesPerson
|
158
|
+
```
|
159
|
+
|
160
|
+
The above will generate (assuming that `Todo` is a proper model name and `TodosController` exists:
|
161
|
+
|
162
|
+
* Policy itself, in `app/policies`,
|
163
|
+
* Spec for a policy in `spec/policies`,
|
164
|
+
* Default strategy in `strategies`.
|
165
|
+
* [optional] if this is a first run, the `pundit:install` generator will be invoked
|
166
|
+
to generate default pundit `ApplicationPolicy`.
|
167
|
+
|
168
|
+
By default all the public controller methods will be guarded with `method?` pundit guards,
|
169
|
+
returning `true` if the current user was listed during generation process.
|
170
|
+
|
171
|
+
![Generate Policy](https://github.com/kantox/kantox-flow/wiki/kantox-roles-generator-1.png)
|
172
|
+
|
173
|
+
### Generated specs
|
174
|
+
|
175
|
+
The generator above would generate smart specs, more or less ready to use. For
|
176
|
+
an example above, it would generate specs, checking whether _all the specified
|
177
|
+
users are **allowed**_ to access the controller, and _all others are restricted_.
|
178
|
+
|
179
|
+
![Generated Specs](https://github.com/kantox/kantox-flow/wiki/kantox-roles-generator-2.png)
|
180
|
+
|
181
|
+
### Vandal-proof
|
182
|
+
|
183
|
+
The generator will gracefully reject a request to damage anything as well as
|
184
|
+
to generate policies for inexisting controller.
|
185
|
+
|
186
|
+
![Vandal-proof](https://github.com/kantox/kantox-flow/wiki/kantox-roles-generator-3.png)
|
187
|
+
|
188
|
+
## Installation
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
gem 'kantox-roles'
|
192
|
+
```
|
193
|
+
|
194
|
+
## TODOs
|
195
|
+
|
196
|
+
* generate policies and rspecs by `yaml`
|
197
|
+
* ~~pointcuts *syntax* — DSL vs YAML/JSON~~
|
198
|
+
* automatic *tests* for pointcuts — by adding them to descriptions
|
199
|
+
* allow⇒deny vs **deny⇒allow**
|
200
|
+
* ~~*supersupervisor* to change rights of supervisors~~
|
201
|
+
* ~~*groups* with *roles*, or smth morre sophisticated? What?~~
|
202
|
+
* ~~where to store rights? 3rd party?~~
|
203
|
+
* *edit rights* from the interface.
|
204
|
+
|
205
|
+
## Usage
|
206
|
+
|
207
|
+
Add a line to `configuration` file (or explicitly by calling `Kantox::Roles.configure` with either hash, or passing a block.)
|
208
|
+
|
209
|
+
Optionally, one may specify a custom handler as denoted by `:runner` key in config.
|
210
|
+
|
211
|
+
|
212
|
+
## Development
|
213
|
+
|
214
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
215
|
+
|
216
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
217
|
+
|
218
|
+
## Contributing
|
219
|
+
|
220
|
+
1. Fork it ( https://github.com/[my-github-username]/kantox-roles/fork )
|
221
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
222
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
223
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
224
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "kantox/roles"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'kantox/roles/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.required_ruby_version = '~> 2.1'
|
8
|
+
|
9
|
+
spec.name = 'kantox-roles'
|
10
|
+
spec.version = Kantox::Roles::VERSION
|
11
|
+
spec.authors = ['Kantox LTD']
|
12
|
+
spec.email = ['aleksei.matiushkin@kantox.com']
|
13
|
+
spec.license = 'Kantox LTD Commercial'
|
14
|
+
|
15
|
+
spec.summary = 'OmniRoles mechanism for Kantox Role Management'
|
16
|
+
spec.description = 'Roles Management interface for virtually every backend, mostly like OmniAuth for authentication'
|
17
|
+
spec.homepage = 'http://kantox.com'
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/(test|spec|features)\//) }
|
20
|
+
spec.bindir = 'bin'
|
21
|
+
spec.executables = spec.files.grep(/^exe\//) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = %w(lib)
|
23
|
+
|
24
|
+
if spec.respond_to?(:metadata)
|
25
|
+
# spec.metadata['allowed_push_host'] = 'http://fury.io'
|
26
|
+
end
|
27
|
+
|
28
|
+
spec.add_dependency 'hashie', '~> 3'
|
29
|
+
spec.add_dependency 'pundit'
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
32
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
33
|
+
|
34
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
35
|
+
|
36
|
+
spec.add_development_dependency 'rspec', '~> 2.12'
|
37
|
+
spec.add_development_dependency 'cucumber', '~> 1.3'
|
38
|
+
spec.add_development_dependency 'yard', '~> 0'
|
39
|
+
# spec.add_development_dependency 'yard-cucumber', '~> 0'
|
40
|
+
end
|
data/lib/kantox/roles.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
Dir[File.expand_path('../**/*', __FILE__)].each do |f|
|
4
|
+
require f[/(.*?)\.rb$/, 1] unless File.directory? f
|
5
|
+
end
|
6
|
+
|
7
|
+
module Kantox
|
8
|
+
LOCK_EMOJI = '🔒'
|
9
|
+
|
10
|
+
module Exceptions
|
11
|
+
class StandardError < ::StandardError; end
|
12
|
+
class NotAuthorized < StandardError; end
|
13
|
+
class NotAllowed < NotAuthorized; end
|
14
|
+
end
|
15
|
+
|
16
|
+
# This error is called when a programmer was as lame as to code wrong
|
17
|
+
class LameError < RuntimeError ; end
|
18
|
+
|
19
|
+
module Roles
|
20
|
+
class << self
|
21
|
+
attr_reader :controller
|
22
|
+
|
23
|
+
# Main initializer. This method is to be called before any controller
|
24
|
+
# is instantiated to prevent leakage of controllers handled.
|
25
|
+
# @param controller [Class] the class of main controller to handle
|
26
|
+
def init controller
|
27
|
+
return @controller if @controller == controller
|
28
|
+
|
29
|
+
fail LameError.new("Roles were already initialized for [#{@controller}]. Tried: [#{controller}]") \
|
30
|
+
unless @controller.nil? ||
|
31
|
+
# The following must satisfy zeus. Zeus reloads controller class
|
32
|
+
# making a comparision to fail. This is an ugly hack to f*ck zeus.
|
33
|
+
Rails.env.development? &&
|
34
|
+
!ENV['ZEUS_MASTER_FD'].nil? &&
|
35
|
+
@controller.name == controller.name
|
36
|
+
|
37
|
+
@controller = controller
|
38
|
+
if @controller.method(:inherited).owner == @controller
|
39
|
+
Kantox::Helpers.warn "#{controller}#inherited was already defined. Realiasing."
|
40
|
+
# FIXME @controller.send :alias_method, "∃inherited", :inherited
|
41
|
+
end
|
42
|
+
|
43
|
+
@controller.class_eval '
|
44
|
+
def self.inherited subclass
|
45
|
+
Kantox::Roles.postpone_strategies subclass
|
46
|
+
end
|
47
|
+
'
|
48
|
+
end
|
49
|
+
|
50
|
+
# Configures Roles by hash or yaml from string or file. Whether code block
|
51
|
+
# is passed, it is processed with @options instance.
|
52
|
+
# @param hos [String|Hash] the input data to configure
|
53
|
+
def configure hos = nil
|
54
|
+
@options = Kantox::Helpers.merge_hash_or_string options, hos
|
55
|
+
yield @options if block_given?
|
56
|
+
# @options.select! { |_, v| v.is_a? Enumerator } # FIXME WARNING OR LIKE
|
57
|
+
apply_all_strategies # FIXME Do we need this?
|
58
|
+
@options
|
59
|
+
end
|
60
|
+
|
61
|
+
def apply_all_strategies
|
62
|
+
options.keys.each { |klazz| postpone_strategies klazz }
|
63
|
+
end
|
64
|
+
|
65
|
+
def postpone_strategies klazz
|
66
|
+
return if postponed[klazz.to_s] # FIXME FIXME FIXME
|
67
|
+
|
68
|
+
case klazz
|
69
|
+
when Class
|
70
|
+
(@postponed[klazz.to_s] = TracePoint.new(:end) do |tp|
|
71
|
+
if tp.self == klazz
|
72
|
+
apply_strategies klazz.to_s
|
73
|
+
Kantox::Helpers.info "Strategies successfully applied to #{klazz} (callback in inherited)."
|
74
|
+
tp.disable
|
75
|
+
end
|
76
|
+
end).enable
|
77
|
+
when String, Symbol
|
78
|
+
if Kernel.const_defined?(klazz)
|
79
|
+
apply_strategies klazz
|
80
|
+
else
|
81
|
+
(@postponed[klazz] = TracePoint.new(:end) do |tp|
|
82
|
+
if tp.self.name == klazz
|
83
|
+
apply_strategies klazz
|
84
|
+
Kantox::Helpers.info "Strategies successfully applied to #{klazz} (eager waiting)."
|
85
|
+
tp.disable
|
86
|
+
end
|
87
|
+
end).enable
|
88
|
+
end
|
89
|
+
else fail LameError.new("Postpone strategies were called with inknown parameter type [#{klazz} :: #{klazz.class}]")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def apply_strategies klazz
|
94
|
+
return if options[klazz].nil? # no strategies to apply
|
95
|
+
Kantox::Helpers.debug "Request to apply strategies to [#{klazz}]"
|
96
|
+
|
97
|
+
options[klazz].each do |m, strategies|
|
98
|
+
next unless Kernel.const_defined? klazz
|
99
|
+
|
100
|
+
Kernel.const_get(klazz).instance_methods.select do |im|
|
101
|
+
im =~ Regexp.new("\\A#{m.gsub('*', '(?:.*?)')}\\z")
|
102
|
+
end.each do |sim|
|
103
|
+
[*strategies].each do |strategy|
|
104
|
+
apply_strategy klazz, sim, strategy
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def apply_strategy klazz, m, strategy
|
111
|
+
km = "#{klazz}##{m}"
|
112
|
+
case applied[km]
|
113
|
+
when :success
|
114
|
+
Kantox::Helpers.debug("Trying to reapply #{strategy} to #{km}. «Skipped».")
|
115
|
+
return
|
116
|
+
when NilClass, :error
|
117
|
+
Kantox::Helpers.debug("Will apply strategy [#{strategy}] to [#{km}]")
|
118
|
+
else
|
119
|
+
fail LameError.new
|
120
|
+
end
|
121
|
+
|
122
|
+
if (im = Kantox::Helpers.get_instance_method(km)).nil?
|
123
|
+
@applied[km] = :error
|
124
|
+
return
|
125
|
+
end
|
126
|
+
|
127
|
+
pc = patch_code im, strategy
|
128
|
+
patch = "
|
129
|
+
def #{im[:method][:name]} *args
|
130
|
+
begin
|
131
|
+
#{pc}
|
132
|
+
rescue Kantox::Strategies::StrategyError => e
|
133
|
+
Kantox::Helpers.info '«Denied #{klazz}##{m}» due to #{strategy} strategy.'
|
134
|
+
raise Kantox::Exceptions::NotAllowed, e.message
|
135
|
+
rescue Kantox::Exceptions::NotAllowed => e
|
136
|
+
Kantox::Helpers.info '«Denied #{klazz}##{m}» (deep) due to #{strategy} strategy.'
|
137
|
+
raise Kantox::Exceptions::NotAllowed, e.message
|
138
|
+
rescue Kantox::Exceptions::StandardError => e
|
139
|
+
Kantox::Helpers.catched 'Guarged «#{klazz}##{m}» throws a [kantox] exception.', e
|
140
|
+
raise e
|
141
|
+
rescue ::StandardError => e
|
142
|
+
Kantox::Helpers.catched 'Guarged «#{klazz}##{m}» throws a [generic] exception.', e
|
143
|
+
raise e
|
144
|
+
end
|
145
|
+
end
|
146
|
+
"
|
147
|
+
im[:class].send :alias_method, "∃#{im[:method][:name]}", "#{im[:method][:name]}"
|
148
|
+
im[:class].class_eval patch
|
149
|
+
applied[km] = :success
|
150
|
+
end
|
151
|
+
|
152
|
+
def patch_code im, strategy
|
153
|
+
Kantox::Helpers.debug "Strategy: #{strategy.class} :: [#{strategy}] "
|
154
|
+
args_as_string = im[:params][:string].empty? ? '' : '*args'
|
155
|
+
|
156
|
+
case strategy
|
157
|
+
when String, Symbol
|
158
|
+
"
|
159
|
+
strategy = Kantox::Helpers.get_simple_strategy('#{strategy}')
|
160
|
+
fail Kantox::Strategies::StrategyError(nil, '#{im[:method][:name]}') if strategy.nil?
|
161
|
+
strategy.call(self, '#{im[:class]}##{im[:method][:name]}', *args)
|
162
|
+
∃#{im[:method][:name]} #{args_as_string}
|
163
|
+
"
|
164
|
+
when Array
|
165
|
+
case strategy.first.to_s
|
166
|
+
when 'runner'
|
167
|
+
"
|
168
|
+
strategy = Kantox::Helpers.get_#{strategy.first}_strategy('#{strategy.last}')
|
169
|
+
fail Kantox::Strategies::StrategyError(nil, '#{im[:method][:name]}') if strategy.nil?
|
170
|
+
p = self.method('∃#{im[:method][:name]}').to_proc
|
171
|
+
strategy.call(self, *args, &p)
|
172
|
+
"
|
173
|
+
when 'lambda'
|
174
|
+
"
|
175
|
+
strategy = Kantox::Helpers.get_#{strategy.first}_strategy('#{strategy.last}')
|
176
|
+
fail Kantox::Strategies::StrategyError(nil, '#{im[:method][:name]}') if strategy.nil?
|
177
|
+
strategy.call(self, '#{im[:class]}##{im[:method][:name]}')
|
178
|
+
∃#{im[:method][:name]} #{args_as_string}
|
179
|
+
"
|
180
|
+
else
|
181
|
+
# Will try to instantiate the class with a given name
|
182
|
+
"
|
183
|
+
strategy = Kantox::Helpers.get_object_strategy('#{strategy.first}', '#{strategy.last}')
|
184
|
+
fail Kantox::Strategies::StrategyError(nil, '#{im[:method][:name]}') if strategy.nil?
|
185
|
+
strategy.to_proc.call(self, '#{im[:class]}##{im[:method][:name]}')
|
186
|
+
∃#{im[:method][:name]} #{args_as_string}
|
187
|
+
"
|
188
|
+
end
|
189
|
+
else
|
190
|
+
fail LameError.new "Unknown strategy: #{strategy}."
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def applied ; @applied ||= {} ; end
|
195
|
+
def postponed ; @postponed ||= {} ; end
|
196
|
+
def options ; @options ||= Hashie::Mash.new ; end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|