rainman 0.1.0
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.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +252 -0
- data/Rakefile +16 -0
- data/example/domain/enom/nameservers.rb +10 -0
- data/example/domain/enom.rb +13 -0
- data/example/domain/opensrs/nameservers.rb +10 -0
- data/example/domain/opensrs.rb +13 -0
- data/example/domain.rb +28 -0
- data/lib/rainman/driver.rb +287 -0
- data/lib/rainman/exceptions.rb +43 -0
- data/lib/rainman/handler.rb +37 -0
- data/lib/rainman/runner.rb +81 -0
- data/lib/rainman/support.rb +75 -0
- data/lib/rainman/version.rb +3 -0
- data/lib/rainman.rb +9 -0
- data/rainman.gemspec +20 -0
- data/spec/integration_spec.rb +40 -0
- data/spec/rainman/driver_spec.rb +323 -0
- data/spec/rainman/exceptions_spec.rb +36 -0
- data/spec/rainman/handler_spec.rb +27 -0
- data/spec/rainman/runner_spec.rb +33 -0
- data/spec/rainman/support_spec.rb +33 -0
- data/spec/rainman_spec.rb +4 -0
- data/spec/spec_helper.rb +1 -0
- metadata +125 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 Site5 LLC <http://www.site5.com/>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person ob-
|
4
|
+
taining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without restric-
|
6
|
+
tion, including without limitation the rights to use, copy, modi-
|
7
|
+
fy, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
the Software, and to permit persons to whom the Software is fur-
|
9
|
+
nished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
16
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN-
|
17
|
+
FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
# Rainman [![Rainman Build Status][Build Icon]][Build Status]
|
2
|
+
|
3
|
+
Rainman is an experiment in writing drivers and handlers. It is a Ruby
|
4
|
+
implementation of the [abstract factory pattern][1]. Abstract factories provide
|
5
|
+
the general API used to interact with any number of interfaces. Interfaces
|
6
|
+
perform actual operations. Rainman provides a simple DSL for implementing this
|
7
|
+
design.
|
8
|
+
|
9
|
+
[1]: http://en.wikipedia.org/wiki/Abstract_factory_pattern
|
10
|
+
|
11
|
+
[Build Icon]: https://secure.travis-ci.org/site5/rainman.png?branch=master
|
12
|
+
[Build Status]: http://travis-ci.org/site5/rainman
|
13
|
+
|
14
|
+
## Drivers & Handlers
|
15
|
+
|
16
|
+
In Rainman, drivers represent abstract factories and handlers represent the
|
17
|
+
interfaces those factories interact with. In simpler terms, drivers define
|
18
|
+
_what_ things you can do; handlers define _how_ to do those things.
|
19
|
+
|
20
|
+
## Creating a driver
|
21
|
+
|
22
|
+
Rainman drivers are implemented as Modules. They must be extended with
|
23
|
+
`Rainman::Driver` and use the driver DSL to define their public API. An
|
24
|
+
example Domain driver might look like this:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'rainman'
|
28
|
+
|
29
|
+
# The Domain module handles creating and deleting domains, and listing
|
30
|
+
# nameservers
|
31
|
+
module Domain
|
32
|
+
extend Rainman::Driver
|
33
|
+
|
34
|
+
# Register Domain::Abc as a handler.
|
35
|
+
register_handler :abc
|
36
|
+
|
37
|
+
# Register Domain::Xyz as a handler.
|
38
|
+
register_handler :xyz
|
39
|
+
|
40
|
+
# Register Domain.create as a public method
|
41
|
+
define_action :create
|
42
|
+
|
43
|
+
# Register Domain.destroy as a public method
|
44
|
+
define_action :destroy
|
45
|
+
|
46
|
+
# Register Domain.namservers.list as a public method
|
47
|
+
namespace :nameservers do
|
48
|
+
define_action :list
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
## Implementing handlers
|
54
|
+
|
55
|
+
Driver handlers are implemented as classes. They must be within the namespace
|
56
|
+
of the driver Module. Using the example above, here are example handlers for
|
57
|
+
Abc and Xyz:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class Domain::Abc
|
61
|
+
# Public: Creates a new domain.
|
62
|
+
#
|
63
|
+
# Returns a Hash.
|
64
|
+
def create(params = {})
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: Destroy a domain
|
68
|
+
#
|
69
|
+
# Returns true or false.
|
70
|
+
def destroy(params = {})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Domain::Xyz
|
75
|
+
# Public: Creates a new domain.
|
76
|
+
#
|
77
|
+
# Returns a Hash.
|
78
|
+
def create(params = {})
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Destroy a domain
|
82
|
+
#
|
83
|
+
# Returns true or false.
|
84
|
+
def destroy(params = {})
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
The example driver above also defined `nameservers` namespace with a `list`
|
90
|
+
action (eg: `Domain.nameservers.list`). To implement this, a Nameservers class
|
91
|
+
is created within each handler's namespace:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class Domain::Abc::Nameserver
|
95
|
+
|
96
|
+
# Public: Lists nameservers for this domain.
|
97
|
+
#
|
98
|
+
# Returns an Array.
|
99
|
+
def list(params = {})
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Domain::Xyz::Nameserver
|
104
|
+
|
105
|
+
# Public: Lists nameservers for this domain.
|
106
|
+
#
|
107
|
+
# Returns an Array.
|
108
|
+
def list(params = {})
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
## Handler setup
|
114
|
+
If your handler requires any sort of setup that can't be handled in your
|
115
|
+
initialize method (e.g. you're subclassing and can't override
|
116
|
+
initialize), you can define a setup_handler method. Rainman will
|
117
|
+
automatically call this method for you after the class is initialized.
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
class Domain::Abc
|
121
|
+
attr_accessor :config
|
122
|
+
|
123
|
+
def initialized
|
124
|
+
@config = { :username => 'username', :password => 'password' }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Domain::Xyz < Domain::Abc
|
129
|
+
def setup_handler
|
130
|
+
@config[:username] = 'other'
|
131
|
+
end
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
## Using a driver
|
136
|
+
|
137
|
+
With a driver and handler defined, the driver can now be used in a few
|
138
|
+
different ways.
|
139
|
+
|
140
|
+
### General
|
141
|
+
|
142
|
+
A driver's actions are available as singleton methods. By default, actions are
|
143
|
+
sent to the current handler, or a default handler if a handler is not currently
|
144
|
+
in use.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
# Create a domain
|
148
|
+
Domain.create({})
|
149
|
+
|
150
|
+
# Destroy a domain
|
151
|
+
Domain.destroy({})
|
152
|
+
|
153
|
+
# List domain nameservers
|
154
|
+
Domain.nameservers.list({})
|
155
|
+
```
|
156
|
+
|
157
|
+
### Changing handlers
|
158
|
+
|
159
|
+
It is possible to change the handler used at runtime using the `with_handler`
|
160
|
+
method. This method temporarily changes the current handler. This means, if
|
161
|
+
you have a default handler set, and use `with_handler`, that default handler
|
162
|
+
is preserved.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
Domain.with_handler(:abc) do |driver|
|
166
|
+
# Here, current_handler is now set to :abc
|
167
|
+
driver.create
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
You can also change the current handler for the duration of your code/session.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
Domain.set_current_handler :xyz
|
175
|
+
Domain.create # create an :xyz domain
|
176
|
+
|
177
|
+
Domain.set_current_handler :abc
|
178
|
+
Domain.create # create an :abc domain
|
179
|
+
Domain.transfer # transfer an :abc domain
|
180
|
+
```
|
181
|
+
|
182
|
+
It is highly suggested you stick to using `with_handler` unless you have a
|
183
|
+
reason.
|
184
|
+
|
185
|
+
### Including drivers in other classes
|
186
|
+
|
187
|
+
A driver can be included in another class and its actions are available as
|
188
|
+
instance methods.
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
class Service
|
192
|
+
include Domain
|
193
|
+
end
|
194
|
+
|
195
|
+
s = Service.new
|
196
|
+
|
197
|
+
s.create
|
198
|
+
|
199
|
+
s.destroy
|
200
|
+
|
201
|
+
s.nameservers.list
|
202
|
+
|
203
|
+
s.with_handler(:abc) do |handler|
|
204
|
+
handler.create
|
205
|
+
end
|
206
|
+
|
207
|
+
s.set_current_handler :xyz
|
208
|
+
s.create
|
209
|
+
```
|
210
|
+
|
211
|
+
If you want to namespace a driver in another class, it's as easy as:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
class Service
|
215
|
+
def domain
|
216
|
+
Domain
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
s = Service.new
|
221
|
+
|
222
|
+
s.domain.create
|
223
|
+
|
224
|
+
s.domain.destroy
|
225
|
+
|
226
|
+
s.domain.nameservers.list
|
227
|
+
|
228
|
+
s.domain.with_handler(:abc) do |handler|
|
229
|
+
handler.create
|
230
|
+
end
|
231
|
+
|
232
|
+
s.domain.set_current_handler :xyz
|
233
|
+
```
|
234
|
+
|
235
|
+
## Note on Patches/Pull Requests
|
236
|
+
|
237
|
+
* Fork the project.
|
238
|
+
* Make your feature addition or bug fix.
|
239
|
+
* Add tests for it. This is important so I don't break it in a future version
|
240
|
+
unintentionally.
|
241
|
+
* Commit, do not bump version. (If you want to have your own version, that is
|
242
|
+
fine but bump version in a commit by itself I can ignore when I pull).
|
243
|
+
* Send me a pull request. Bonus points for topic branches.
|
244
|
+
|
245
|
+
## Contributors
|
246
|
+
|
247
|
+
* [Justin Mazzi](https://github.com/jmazzi)
|
248
|
+
* [Joshua Priddle](https://github.com/itspriddle)
|
249
|
+
|
250
|
+
## Copyright
|
251
|
+
|
252
|
+
Copyright (c) 2011 Site5 LLC. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
begin
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
rescue LoadError
|
6
|
+
puts "Please install rspec (bundle install)"
|
7
|
+
exit
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec::Core::RakeTask.new :spec
|
11
|
+
task :default => :spec
|
12
|
+
|
13
|
+
desc "Open an irb session preloaded with this library"
|
14
|
+
task :console do
|
15
|
+
sh "irb -rubygems -r ./lib/rainman.rb -I ./lib -r ./example/domain.rb"
|
16
|
+
end
|
data/example/domain.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rainman'
|
2
|
+
|
3
|
+
# Load handlers
|
4
|
+
$:.unshift File.expand_path('..', __FILE__)
|
5
|
+
require 'domain/enom'
|
6
|
+
require 'domain/enom/nameservers'
|
7
|
+
require 'domain/opensrs'
|
8
|
+
require 'domain/opensrs/nameservers'
|
9
|
+
|
10
|
+
# The Domain module will contain all methods for interacting with various
|
11
|
+
# domain handlers.
|
12
|
+
module Domain
|
13
|
+
extend Rainman::Driver
|
14
|
+
|
15
|
+
register_handler :enom
|
16
|
+
|
17
|
+
register_handler :opensrs
|
18
|
+
|
19
|
+
namespace :nameservers do
|
20
|
+
define_action :list
|
21
|
+
end
|
22
|
+
|
23
|
+
define_action :list
|
24
|
+
|
25
|
+
define_action :transfer
|
26
|
+
|
27
|
+
set_default_handler :opensrs
|
28
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Rainman
|
4
|
+
# The Rainman::Driver module contains methods for defining Drivers and
|
5
|
+
# proxying their associated actions to the appropriate handlers.
|
6
|
+
module Driver
|
7
|
+
# Public: Extended hook; this is run when a module extends itself with
|
8
|
+
# the Rainman::Driver module.
|
9
|
+
def self.extended(base)
|
10
|
+
all << base
|
11
|
+
base.extend(base)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Get a list of all Drivers (eg: Modules that are extended with
|
15
|
+
# Rainman::Driver).
|
16
|
+
#
|
17
|
+
# Returns an Array.
|
18
|
+
def self.all
|
19
|
+
@all ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Registered handlers.
|
23
|
+
#
|
24
|
+
# Keys are the handler name (eg: :my_handler); values are the handler
|
25
|
+
# class (eg: MyHandler).
|
26
|
+
#
|
27
|
+
# Raises NoHandler if an attempt to access a key of nil is made, (eg:
|
28
|
+
# handlers[nil]).
|
29
|
+
#
|
30
|
+
# Raises InvalidHandler if an attempt to access an invalid key is made.
|
31
|
+
#
|
32
|
+
# Returns a Hash.
|
33
|
+
def handlers
|
34
|
+
@handlers ||= Hash.new do |hash, key|
|
35
|
+
if key.nil?
|
36
|
+
raise NoHandler
|
37
|
+
else
|
38
|
+
raise InvalidHandler, key
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Temporarily change a Driver's current handler. The handler is
|
44
|
+
# changed for the duration of the block supplied. This is useful to perform
|
45
|
+
# actions using multiple handlers without changing defaults.
|
46
|
+
#
|
47
|
+
# name - The Symbol name of the handler to use.
|
48
|
+
#
|
49
|
+
# Example
|
50
|
+
#
|
51
|
+
# with_handler(:enom) do |handler|
|
52
|
+
# handler.transfer
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# Yields a Runner instance if a block is given.
|
56
|
+
#
|
57
|
+
# Returns a Runner instance or the result of a block.
|
58
|
+
def with_handler(name, &block)
|
59
|
+
raise MissingBlock, :with_handler unless block_given?
|
60
|
+
|
61
|
+
old_handler = current_handler
|
62
|
+
|
63
|
+
begin
|
64
|
+
set_current_handler name
|
65
|
+
yield current_handler_instance.runner
|
66
|
+
ensure
|
67
|
+
set_current_handler old_handler
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Public: Sets the default handler used for this Driver.
|
72
|
+
#
|
73
|
+
# name - The Symbol name to set as the default handler. Should be a key
|
74
|
+
# from handlers.
|
75
|
+
#
|
76
|
+
# Returns the Symbol name.
|
77
|
+
def set_default_handler(name)
|
78
|
+
@default_handler = name
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Get the default handler used for this Driver.
|
82
|
+
#
|
83
|
+
# Returns the Symbol name of this Driver's default handler.
|
84
|
+
def default_handler
|
85
|
+
@default_handler
|
86
|
+
end
|
87
|
+
|
88
|
+
# Public: Sets the current handler. Name should be an underscored symbol
|
89
|
+
# representing a class name in the current context.
|
90
|
+
#
|
91
|
+
# name - The Symbol name of the handler to use. Can be set to nil to
|
92
|
+
# clear the current handler.
|
93
|
+
#
|
94
|
+
# Example
|
95
|
+
#
|
96
|
+
# set_current_handler :my_handler #=> sets handler to MyHandler
|
97
|
+
# set_current_handler nil #=> clears handler
|
98
|
+
#
|
99
|
+
# Returns the Symbol name of the current handler or nothing.
|
100
|
+
def set_current_handler(name)
|
101
|
+
@current_handler = name
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# Private: Included hook; this is invoked when a Driver module is
|
107
|
+
# included in another class. It sets up delegation so that the including
|
108
|
+
# class can access a Driver's singleton methods as instance methods.
|
109
|
+
#
|
110
|
+
# base - The Module/Class that included this module.
|
111
|
+
#
|
112
|
+
# Example
|
113
|
+
#
|
114
|
+
# class Service
|
115
|
+
# include Domain
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# s = Service.new
|
119
|
+
# s.transfer #=> calls Domain.transfer
|
120
|
+
#
|
121
|
+
# Returns nothing.
|
122
|
+
def included(base)
|
123
|
+
base.extend(::Forwardable)
|
124
|
+
base.def_delegators self, *singleton_methods
|
125
|
+
end
|
126
|
+
|
127
|
+
# Private: A hash containing handler instances. This prevents handlers
|
128
|
+
# from being initialized multiple times in a single session.
|
129
|
+
#
|
130
|
+
# Returns a Hash containing instances of handlers that have been
|
131
|
+
# initialized.
|
132
|
+
def handler_instances
|
133
|
+
@handler_instances ||= {}
|
134
|
+
end
|
135
|
+
|
136
|
+
# Private: Get or set an instance of the current handler class. This
|
137
|
+
# method stores the instance in handler_instances, and should be used
|
138
|
+
# instead of manually initializing handlers.
|
139
|
+
#
|
140
|
+
# Returns an instance of the current handler class.
|
141
|
+
def current_handler_instance
|
142
|
+
handler_instances[current_handler] ||= handlers[current_handler].new.tap do |_handler|
|
143
|
+
_handler.setup_handler if _handler.respond_to?(:setup_handler)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Private: Get the current handler in use by this Driver.
|
148
|
+
#
|
149
|
+
# Returns the Symbol name of the current handler, or default handler if
|
150
|
+
# a current handler is not set.
|
151
|
+
def current_handler
|
152
|
+
@current_handler || @default_handler
|
153
|
+
end
|
154
|
+
|
155
|
+
# Private: Register a handler for use with the current Driver.
|
156
|
+
#
|
157
|
+
# If a block is given it is evaluated within the context of the handler
|
158
|
+
# Class.
|
159
|
+
#
|
160
|
+
# name - The Symbol handler name.
|
161
|
+
# opts - A Hash containing optional arguments:
|
162
|
+
# :class_name - The class name to use.
|
163
|
+
#
|
164
|
+
# Examples
|
165
|
+
#
|
166
|
+
# register_handler :bob
|
167
|
+
#
|
168
|
+
# Returns the handler Class.
|
169
|
+
def register_handler(name, opts = {})
|
170
|
+
opts.reverse_merge!(
|
171
|
+
:class_name => "#{self.name}::#{name.to_s.camelize}"
|
172
|
+
)
|
173
|
+
|
174
|
+
klass = opts[:class_name].constantize
|
175
|
+
|
176
|
+
handlers[name] = inject_handler_methods(klass, name.to_sym)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Private: Create a new namespace.
|
180
|
+
#
|
181
|
+
# name - The Symbol handler name.
|
182
|
+
# opts - Arguments (unused currently).
|
183
|
+
# block - A required block used to create actions within the namespace
|
184
|
+
#
|
185
|
+
# Example
|
186
|
+
#
|
187
|
+
# namespace :nameservers do
|
188
|
+
# define_action :list
|
189
|
+
# end
|
190
|
+
#
|
191
|
+
# Raises Rainman::MissingBlock if called without a block.
|
192
|
+
#
|
193
|
+
# Returns a Module.
|
194
|
+
def namespace(name, opts = {}, &block)
|
195
|
+
raise MissingBlock, :namespace unless block_given?
|
196
|
+
|
197
|
+
create_method(name) do
|
198
|
+
key = "@#{name}"
|
199
|
+
|
200
|
+
if instance_variable_defined?(key)
|
201
|
+
ns = instance_variable_get(key)
|
202
|
+
else
|
203
|
+
ns = instance_variable_set(key, {})
|
204
|
+
end
|
205
|
+
|
206
|
+
unless ns[current_handler]
|
207
|
+
mod = Module.new do
|
208
|
+
extend self
|
209
|
+
extend ActionMethods
|
210
|
+
class << self
|
211
|
+
attr_accessor :current_handler_instance
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
klass = current_handler_instance.class.const_get(name.to_s.camelize)
|
216
|
+
mod.current_handler_instance = inject_handler_methods(klass, name).new
|
217
|
+
|
218
|
+
mod.instance_eval(&block)
|
219
|
+
ns[current_handler] = mod
|
220
|
+
end
|
221
|
+
|
222
|
+
ns[current_handler]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Private: Injects Handler methods into the given class/module.
|
227
|
+
#
|
228
|
+
# base - The base Class/Module.
|
229
|
+
# handler_name - The Symbol name of the handler class.
|
230
|
+
#
|
231
|
+
# Example
|
232
|
+
#
|
233
|
+
# inject_handler_methods(SomeHandler, :some_handler)
|
234
|
+
#
|
235
|
+
# Returns base Class/Module.
|
236
|
+
def inject_handler_methods(base, handler_name)
|
237
|
+
base.extend(Handler)
|
238
|
+
base.instance_variable_set(:@handler_name, handler_name)
|
239
|
+
base.instance_variable_set(:@parent_klass, self)
|
240
|
+
base
|
241
|
+
end
|
242
|
+
|
243
|
+
# These methods are used to create handler actions.
|
244
|
+
module ActionMethods
|
245
|
+
# Private: Define a new action.
|
246
|
+
#
|
247
|
+
# name - The Symbol handler name.
|
248
|
+
# opts - Options (unused currently).
|
249
|
+
#
|
250
|
+
# Example
|
251
|
+
#
|
252
|
+
# define_action :blah
|
253
|
+
#
|
254
|
+
# Returns a Proc.
|
255
|
+
def define_action(name, opts = {})
|
256
|
+
create_method(name) do |*args, &block|
|
257
|
+
current_handler_instance.runner.send(name, *args, &block)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Private: Creates a new method.
|
262
|
+
#
|
263
|
+
# method - The method name.
|
264
|
+
# args - Arguments to be supplied to the method (optional).
|
265
|
+
# block - Block to be supplied to the method (optional).
|
266
|
+
#
|
267
|
+
# Examples
|
268
|
+
#
|
269
|
+
# create_method :blah do
|
270
|
+
# # code to execute
|
271
|
+
# end
|
272
|
+
#
|
273
|
+
# Raises Rainman::AlreadyImplemented if the method already exists.
|
274
|
+
#
|
275
|
+
# Returns a Proc.
|
276
|
+
def create_method(method, *args, &block)
|
277
|
+
if respond_to?(method, true)
|
278
|
+
raise AlreadyImplemented, "#{inspect}::#{method}"
|
279
|
+
else
|
280
|
+
define_method(method, *args, &block)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
include ActionMethods
|
286
|
+
end
|
287
|
+
end
|