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
@@ -0,0 +1,43 @@
|
|
1
|
+
module Rainman
|
2
|
+
# AlreadyImplemented is raised when attempting to create a driver action
|
3
|
+
# that has already been defined.
|
4
|
+
class AlreadyImplemented < StandardError
|
5
|
+
def initialize(method)
|
6
|
+
super "Method #{method.inspect} already exists!"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# InvalidHandler is raised when attempting to access a handler that has
|
11
|
+
# not yet been registered.
|
12
|
+
class InvalidHandler < StandardError
|
13
|
+
def initialize(handler)
|
14
|
+
super "Handler #{handler.inspect} is invalid! Maybe you need to " <<
|
15
|
+
"call 'register_handler #{handler.inspect}'?"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# NoHandler is raised when attempting to do something that needs a handler,
|
20
|
+
# but no default or current handler can be found.
|
21
|
+
class NoHandler < StandardError
|
22
|
+
def initialize
|
23
|
+
super "No handler is set! Maybe you need to " <<
|
24
|
+
"call 'set_default_handler'?"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# MissingParameter is raised when trying to send a request to a runner that
|
29
|
+
# is missing parameters/arguments.
|
30
|
+
class MissingParameter < StandardError
|
31
|
+
def initialize(param)
|
32
|
+
super "Missing parameter #{param.inspect}!"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# MissingBlock is raised when trying to run an action that is missing a
|
37
|
+
# required block parameter.
|
38
|
+
class MissingBlock < LocalJumpError
|
39
|
+
def initialize(method)
|
40
|
+
super "Can't call #{method.inspect} without a block!"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rainman
|
2
|
+
# The Handler module contains methods that are added to handler classes at
|
3
|
+
# runtime. They are available as class methods.
|
4
|
+
module Handler
|
5
|
+
# Public: The name of this handler.
|
6
|
+
#
|
7
|
+
# Returns a Symbol.
|
8
|
+
def handler_name
|
9
|
+
@handler_name
|
10
|
+
end
|
11
|
+
|
12
|
+
# Public: Get the the handler's parent_klass.
|
13
|
+
#
|
14
|
+
# Returns Rainman::Driver.self
|
15
|
+
def parent_klass
|
16
|
+
@parent_klass
|
17
|
+
end
|
18
|
+
|
19
|
+
# These instance methods are available to handler instances.
|
20
|
+
module InstanceMethods
|
21
|
+
# Public: A Runner is automatically available to handler instances.
|
22
|
+
#
|
23
|
+
# Returns a Rainman::Runner.
|
24
|
+
def runner
|
25
|
+
@runner ||= Rainman::Runner.new(self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Extended hook; this adds the InstanceMethods module to handler
|
30
|
+
# classes.
|
31
|
+
#
|
32
|
+
# base - The Module/Class that was extended with this module.
|
33
|
+
def self.extended(base)
|
34
|
+
base.send(:include, InstanceMethods)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Rainman
|
2
|
+
# The Runner class delegates actions to handlers. It runs validations
|
3
|
+
# before executing the action.
|
4
|
+
#
|
5
|
+
# Examples
|
6
|
+
#
|
7
|
+
# Runner.new(current_handler_instance).tap do |r|
|
8
|
+
# r.transfer
|
9
|
+
# end
|
10
|
+
class Runner
|
11
|
+
# Public: Gets the handler Class.
|
12
|
+
attr_reader :handler
|
13
|
+
|
14
|
+
# Public: Initialize a runner.
|
15
|
+
#
|
16
|
+
# handler - A handler Class instance.
|
17
|
+
#
|
18
|
+
# Examples
|
19
|
+
#
|
20
|
+
# Runner.new(current_handler_instance)
|
21
|
+
def initialize(handler)
|
22
|
+
@handler = handler
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Get the Symbol name of the handler.
|
26
|
+
#
|
27
|
+
# Returns a Symbol.
|
28
|
+
def name
|
29
|
+
handler.class.handler_name
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Get the handler's parent_klass
|
33
|
+
#
|
34
|
+
# Returns Rainman::Driver.self
|
35
|
+
def parent_klass
|
36
|
+
handler.class.parent_klass
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Delegates the given method to the handler.
|
40
|
+
#
|
41
|
+
# context - Set the context for the method (class/instance)
|
42
|
+
# method - The method to send to the handler.
|
43
|
+
# args - Arguments to be supplied to the method (optional).
|
44
|
+
# block - Block to be supplied to the method (optional).
|
45
|
+
#
|
46
|
+
# Examples
|
47
|
+
#
|
48
|
+
# execute(handler, :register)
|
49
|
+
# execute(handler.parent_class, :register, { params: [] })
|
50
|
+
# execute(handler, :register, :one, :argument) do
|
51
|
+
# # some code
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Raises MissingParameter if validation fails due to missing parameters.
|
55
|
+
#
|
56
|
+
# Returns the result of the handler action.
|
57
|
+
def execute(context, method, *args, &block)
|
58
|
+
# verify params here
|
59
|
+
context.send(method, *args, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Internal: Method missing hook used to proxy methods to a handler.
|
63
|
+
#
|
64
|
+
# method - The missing method name.
|
65
|
+
# args - Arguments to be supplied to the method (optional).
|
66
|
+
# block - Block to be supplied to the method (optional).
|
67
|
+
#
|
68
|
+
# Raises NameError if handler does not respond to method.
|
69
|
+
#
|
70
|
+
# Returns the value of execute.
|
71
|
+
def method_missing(method, *args, &block)
|
72
|
+
if handler.respond_to?(method)
|
73
|
+
execute(handler, method, *args, &block)
|
74
|
+
elsif parent_klass.respond_to?(method)
|
75
|
+
execute(parent_klass, method, *args, &block)
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# This file contains a few methods from ActiveSupport that are used by
|
2
|
+
# Rainman. If ActiveSupport has been loaded, those methods will be used rather
|
3
|
+
# than those in this file.
|
4
|
+
|
5
|
+
# From activesupport/lib/active_support/inflector/methods.rb
|
6
|
+
class String
|
7
|
+
# Public: Convert a string into a constant. The constant must exist in
|
8
|
+
# ObjectSpace.
|
9
|
+
#
|
10
|
+
# Raises NameError if the constant does not exist.
|
11
|
+
#
|
12
|
+
# Returns a constant.
|
13
|
+
def constantize
|
14
|
+
names = split('::')
|
15
|
+
names.shift if names.empty? || names.first.empty?
|
16
|
+
|
17
|
+
constant = Object
|
18
|
+
names.each do |name|
|
19
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
20
|
+
end
|
21
|
+
constant
|
22
|
+
end unless respond_to?(:constantize)
|
23
|
+
|
24
|
+
# Public: Camel-case a string.
|
25
|
+
#
|
26
|
+
# Examples
|
27
|
+
#
|
28
|
+
# "foo_bar" #=> "FooBar"
|
29
|
+
# "foo_bar/baz" #=> "FooBar::Baz"
|
30
|
+
#
|
31
|
+
# Returns a String.
|
32
|
+
def camelize(first_letter_in_uppercase = true)
|
33
|
+
if first_letter_in_uppercase
|
34
|
+
gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
35
|
+
else
|
36
|
+
self[0].chr.downcase + camelize(self)[1..-1]
|
37
|
+
end
|
38
|
+
end unless respond_to?(:camelize)
|
39
|
+
end
|
40
|
+
|
41
|
+
# From lib/active_support/core_ext/hash/reverse_merge.rb
|
42
|
+
class Hash
|
43
|
+
# Public: Reverse merge a hash.
|
44
|
+
#
|
45
|
+
# Example
|
46
|
+
#
|
47
|
+
# a = { :one => :A }
|
48
|
+
# b = a.reverse_merge(:one => :B, :two => :two)
|
49
|
+
# a #=> { :one => :A }
|
50
|
+
# b #=> { :one => :A, :two => :two }
|
51
|
+
#
|
52
|
+
# Returns a new Hash.
|
53
|
+
def reverse_merge(other_hash)
|
54
|
+
other_hash.merge(self)
|
55
|
+
end unless respond_to?(:reverse_merge)
|
56
|
+
|
57
|
+
# Public: Reverse merge a hash in-place.
|
58
|
+
#
|
59
|
+
# Example
|
60
|
+
#
|
61
|
+
# a = { :one => :A }
|
62
|
+
# a.reverse_merge!(:one => :B, :two => :two)
|
63
|
+
# a #=> { :one => :A, :two => :two }
|
64
|
+
#
|
65
|
+
# Returns a Hash.
|
66
|
+
def reverse_merge!(other_hash)
|
67
|
+
# right wins if there is no left
|
68
|
+
merge!( other_hash ){|key,left,right| left }
|
69
|
+
end unless respond_to?(:reverse_merge!)
|
70
|
+
|
71
|
+
if respond_to?(:reverse_merge!) && ! respond_to?(:reverse_update)
|
72
|
+
# Alias Hash#reverse_update
|
73
|
+
alias_method :reverse_update, :reverse_merge!
|
74
|
+
end
|
75
|
+
end
|
data/lib/rainman.rb
ADDED
data/rainman.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/rainman/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Justin Mazzi"]
|
6
|
+
gem.email = ["jmazzi@gmail.com"]
|
7
|
+
gem.description = %q{A library for writing drivers using the abstract factory pattern}
|
8
|
+
gem.summary = %q{Rainman is an experiment in writing drivers and handlers. It is a Ruby implementation of the abstract factory pattern. Abstract factories provide the general API used to interact with any number of interfaces. Interfaces perform actual operations. Rainman provides a simple DSL for implementing this design.}
|
9
|
+
gem.homepage = "http://www.eng5.com"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split("\n")
|
12
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
13
|
+
gem.name = "rainman"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.version = Rainman::VERSION
|
16
|
+
|
17
|
+
gem.add_development_dependency 'rspec', '~> 2.7.0'
|
18
|
+
gem.add_development_dependency 'autotest-standalone', '~> 4.5.8'
|
19
|
+
gem.add_development_dependency 'rake', '~> 0.9.2.2'
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.expand_path('../../example/domain.rb', __FILE__)
|
3
|
+
|
4
|
+
describe "Rainman integration" do
|
5
|
+
describe Domain do
|
6
|
+
describe "handlers" do
|
7
|
+
subject { Domain.handlers }
|
8
|
+
|
9
|
+
its([:enom]) { should == Domain::Enom }
|
10
|
+
its([:opensrs]) { should == Domain::Opensrs }
|
11
|
+
end
|
12
|
+
|
13
|
+
its(:default_handler) { should == :opensrs }
|
14
|
+
|
15
|
+
it "has instance methods for each namespace/action" do
|
16
|
+
methods = subject.instance_methods.map(&:to_sym)
|
17
|
+
methods.should include(:nameservers, :list, :transfer)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "Opensrs integration" do
|
21
|
+
its(:list) { should == :opensrs_list }
|
22
|
+
its(:transfer) { should == :opensrs_transfer }
|
23
|
+
its("nameservers.list") { should == :opensrs_ns_list }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "Enom integration" do
|
27
|
+
before :all do
|
28
|
+
subject.set_default_handler :enom
|
29
|
+
end
|
30
|
+
|
31
|
+
after :all do
|
32
|
+
subject.set_default_handler :opensrs
|
33
|
+
end
|
34
|
+
|
35
|
+
its(:list) { should == :enom_list }
|
36
|
+
its(:transfer) { should == :enom_transfer }
|
37
|
+
its("nameservers.list") { should == :enom_ns_list }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe "Rainman::Driver" do
|
5
|
+
before do
|
6
|
+
Rainman::Driver.instance_variable_set(:@all, [])
|
7
|
+
@module = Module.new do
|
8
|
+
def self.name
|
9
|
+
'MissDaisy'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
@module.extend Rainman::Driver
|
13
|
+
Object.send(:remove_const, :MissDaisy) if Object.const_defined?(:MissDaisy)
|
14
|
+
Object.const_set(:MissDaisy, @module)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "::extended" do
|
18
|
+
it "extends base with base" do
|
19
|
+
m = Module.new
|
20
|
+
m.should_receive(:extend).with(m)
|
21
|
+
Rainman::Driver.extended(m)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "::all" do
|
26
|
+
it "returns an array of registered drivers" do
|
27
|
+
Rainman::Driver.all.should == [@module]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#handlers" do
|
32
|
+
it "returns an empty hash" do
|
33
|
+
@module.handlers.should == {}
|
34
|
+
end
|
35
|
+
|
36
|
+
it "raises exception when accessing an unknown key" do
|
37
|
+
expect { @module.handlers[:foo] }.to raise_error(Rainman::InvalidHandler)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises exception when accessing a nil key" do
|
41
|
+
expect { @module.handlers[nil] }.to raise_error(Rainman::NoHandler)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#with_handler" do
|
46
|
+
before do
|
47
|
+
@klass = Class.new do
|
48
|
+
def hi; :hi_handler!; end
|
49
|
+
def self.handler_name; :blah; end
|
50
|
+
end
|
51
|
+
@handler = @klass.new
|
52
|
+
runner = Rainman::Runner.new(@handler)
|
53
|
+
@handler.stub(:runner).and_return(runner)
|
54
|
+
@module.stub(:current_handler_instance).and_return(@handler)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should temporarily change the current handler" do
|
58
|
+
old_handler = :old_lady
|
59
|
+
@module.should_receive(:set_current_handler).with(:blah)
|
60
|
+
@module.should_receive(:set_current_handler).with(old_handler)
|
61
|
+
@module.stub(:current_handler).and_return(old_handler)
|
62
|
+
@module.with_handler(:blah) {}
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should raise an error without a block" do
|
66
|
+
expect { @module.with_handler(:blah) }.to raise_error(Rainman::MissingBlock)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "yields the runner" do
|
70
|
+
res = @module.with_handler :blah do |runner|
|
71
|
+
runner.should be_a(Rainman::Runner)
|
72
|
+
runner.hi
|
73
|
+
end
|
74
|
+
res.should == :hi_handler!
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#set_default_handler" do
|
79
|
+
it "sets @default_handler" do
|
80
|
+
@module.set_default_handler :blah
|
81
|
+
@module.instance_variable_get(:@default_handler).should == :blah
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#default_handler" do
|
86
|
+
it "gets @default_handler" do
|
87
|
+
expected = @module.instance_variable_get(:@default_handler)
|
88
|
+
@module.default_handler.should eq(expected)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#included" do
|
93
|
+
it "extends base with Forwardable" do
|
94
|
+
klass = Class.new
|
95
|
+
klass.should_receive(:extend).with(::Forwardable)
|
96
|
+
klass.stub(:def_delegators)
|
97
|
+
klass.send(:include, @module)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "sets up delegation for singleton methods" do
|
101
|
+
klass = Class.new
|
102
|
+
klass.should_receive(:def_delegators).with(@module, *@module.singleton_methods)
|
103
|
+
klass.send(:include, @module)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#handler_instances" do
|
108
|
+
it "returns @handler_instances" do
|
109
|
+
@module.send(:handler_instances).should == {}
|
110
|
+
@module.instance_variable_set(:@handler_instances, { :foo => :test })
|
111
|
+
@module.send(:handler_instances).should == { :foo => :test }
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should call handler_setup if it exists" do
|
115
|
+
module MissDaisy
|
116
|
+
extend Rainman::Driver
|
117
|
+
class WithSetup
|
118
|
+
attr_reader :setup
|
119
|
+
|
120
|
+
def setup_handler
|
121
|
+
@setup = true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class WithoutSetup
|
126
|
+
attr_reader :setup
|
127
|
+
end
|
128
|
+
|
129
|
+
register_handler :with_setup
|
130
|
+
register_handler :without_setup
|
131
|
+
define_action :setup
|
132
|
+
end
|
133
|
+
|
134
|
+
MissDaisy.set_current_handler :with_setup
|
135
|
+
MissDaisy.setup.should be_true
|
136
|
+
MissDaisy.set_current_handler :without_setup
|
137
|
+
MissDaisy.setup.should_not be_true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#set_current_handler" do
|
142
|
+
it "sets @current_handler" do
|
143
|
+
@module.set_current_handler :blah
|
144
|
+
@module.instance_variable_get(:@current_handler).should == :blah
|
145
|
+
@module.set_current_handler :other
|
146
|
+
@module.instance_variable_get(:@current_handler).should == :other
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "#current_handler_instance" do
|
151
|
+
before do
|
152
|
+
@class = Class.new
|
153
|
+
@klass = @class.new
|
154
|
+
@module.handlers[:abc] = @class
|
155
|
+
@module.send(:set_current_handler, :abc)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "returns the handler instance" do
|
159
|
+
@module.send(:handler_instances).merge!(:abc => @klass)
|
160
|
+
@module.send(:current_handler_instance).should == @klass
|
161
|
+
end
|
162
|
+
|
163
|
+
it "sets the handler instance" do
|
164
|
+
@module.handlers[:abc] = @class
|
165
|
+
@class.should_receive(:new).and_return(@klass)
|
166
|
+
@module.send(:current_handler_instance).should be_a(@class)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "#current_handler" do
|
171
|
+
it "returns @current_handler if set" do
|
172
|
+
@module.instance_variable_set(:@current_handler, :blah)
|
173
|
+
@module.send(:current_handler).should == :blah
|
174
|
+
end
|
175
|
+
|
176
|
+
it "returns @default_handler if @current_handler is not set" do
|
177
|
+
@module.instance_variable_set(:@current_handler, nil)
|
178
|
+
@module.instance_variable_set(:@default_handler, :blah)
|
179
|
+
@module.send(:current_handler).should == :blah
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "#register_handler" do
|
184
|
+
before do
|
185
|
+
@bob = Class.new do
|
186
|
+
def self.name; 'Bob'; end
|
187
|
+
end
|
188
|
+
@module.const_set(:Bob, @bob)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "adds the handler to handlers" do
|
192
|
+
@module.send(:register_handler, :bob)
|
193
|
+
@module.handlers.should have_key(:bob)
|
194
|
+
@module.handlers[:bob].should == @bob
|
195
|
+
end
|
196
|
+
|
197
|
+
it "extends handler with handler methods" do
|
198
|
+
@bob.should_receive(:extend).with(Rainman::Handler)
|
199
|
+
@module.send(:register_handler, :bob)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#define_action" do
|
204
|
+
it "creates the method" do
|
205
|
+
@module.should_not respond_to(:blah)
|
206
|
+
@module.send(:define_action, :blah)
|
207
|
+
@module.should respond_to(:blah)
|
208
|
+
|
209
|
+
klass = Class.new.new
|
210
|
+
runner = Rainman::Runner.new(klass)
|
211
|
+
klass.stub(:runner).and_return(runner)
|
212
|
+
@module.stub(:current_handler_instance).and_return(klass)
|
213
|
+
runner.should_receive(:send).with(:blah)
|
214
|
+
|
215
|
+
@module.blah
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "#create_method" do
|
220
|
+
it "raises AlreadyImplemented if the method has been defined" do
|
221
|
+
@module.instance_eval do
|
222
|
+
def blah; end
|
223
|
+
end
|
224
|
+
|
225
|
+
expect do
|
226
|
+
@module.send(:create_method, :blah)
|
227
|
+
end.to raise_error(Rainman::AlreadyImplemented)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "adds the method" do
|
231
|
+
@module.should_not respond_to(:blah)
|
232
|
+
@module.send(:create_method, :blah, lambda { :hi })
|
233
|
+
@module.should respond_to(:blah)
|
234
|
+
@module.blah.should == :hi
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "#inject_handler_methods" do
|
239
|
+
before do
|
240
|
+
@bob = Class.new do
|
241
|
+
def self.name; 'Bob'; end
|
242
|
+
end
|
243
|
+
@module.const_set(:Bob, @bob)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "extends Handler" do
|
247
|
+
@bob.should_receive(:extend).with(Rainman::Handler)
|
248
|
+
@module.send(:inject_handler_methods, @bob, :bob)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "sets @handler_name class var" do
|
252
|
+
@module.send(:inject_handler_methods, @bob, :bob)
|
253
|
+
@bob.handler_name.should == :bob
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "#namespace" do
|
258
|
+
def create_ns_class(name, base)
|
259
|
+
klass = Class.new do
|
260
|
+
def hi; self.class.handler_name; end
|
261
|
+
def bye; :nonono!; end
|
262
|
+
def self.handler_name; name; end
|
263
|
+
def self.validations; { :global => Rainman::Option.new(:global) }; end
|
264
|
+
end
|
265
|
+
|
266
|
+
set_const(base, name.to_s.camelize.to_sym, klass)
|
267
|
+
end
|
268
|
+
|
269
|
+
def set_const(base, name, const)
|
270
|
+
base.send(:remove_const, name) if base.const_defined?(name)
|
271
|
+
base.const_set(name, const)
|
272
|
+
end
|
273
|
+
|
274
|
+
before do
|
275
|
+
create_ns_class :abc, @module
|
276
|
+
create_ns_class :xyz, @module
|
277
|
+
create_ns_class :bob, @module::Abc
|
278
|
+
create_ns_class :bob, @module::Xyz
|
279
|
+
|
280
|
+
@module.send(:register_handler, :abc)
|
281
|
+
@module.send(:register_handler, :xyz)
|
282
|
+
@module.set_default_handler :abc
|
283
|
+
@module.send(:namespace, :bob) do
|
284
|
+
define_action :hi
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
it "sets an instance variable" do
|
289
|
+
[:abc, :xyz].each do |name|
|
290
|
+
@module.with_handler(name) { |h| h.bob.hi }
|
291
|
+
ivar = @module.instance_variable_get(:@bob)
|
292
|
+
ivar.should be_a(Hash)
|
293
|
+
ivar.should have_key(name)
|
294
|
+
ivar[name].should be_a(Module)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
it "raises exception calling a method that isn't registered" do
|
299
|
+
expect { @module.bob.bye }.to raise_error(NoMethodError)
|
300
|
+
end
|
301
|
+
|
302
|
+
it "raises no exception calling a method that is registered" do
|
303
|
+
@module.bob.hi.should == "MissDaisy::Abc::Bob"
|
304
|
+
end
|
305
|
+
|
306
|
+
it "creates a method for the namespace" do
|
307
|
+
@module.should respond_to(:bob)
|
308
|
+
end
|
309
|
+
|
310
|
+
it "returns an anonymous Module" do
|
311
|
+
@module.bob.should be_a(Module)
|
312
|
+
end
|
313
|
+
|
314
|
+
it "uses the right handler" do
|
315
|
+
[:abc, :xyz].each do |h|
|
316
|
+
expected = "MissDaisy::#{h.to_s.capitalize}::Bob"
|
317
|
+
@module.with_handler(h) do |handler|
|
318
|
+
handler.bob.hi.should == expected
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Rainman Exceptions" do
|
4
|
+
|
5
|
+
def self.test_exception(klass, opts = {})
|
6
|
+
describe "#{klass.to_s}" do
|
7
|
+
it "raises with message" do
|
8
|
+
const = Rainman.const_get(klass)
|
9
|
+
args = opts[:args] || []
|
10
|
+
mesg = opts[:message]
|
11
|
+
expect { raise const, *args }.to raise_error(const, mesg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
test_exception :AlreadyImplemented,
|
17
|
+
:args => :blah,
|
18
|
+
:message => "Method :blah already exists!"
|
19
|
+
|
20
|
+
test_exception :InvalidHandler,
|
21
|
+
:args => :blah,
|
22
|
+
:message => "Handler :blah is invalid! Maybe you need to call " <<
|
23
|
+
"'register_handler :blah'?"
|
24
|
+
|
25
|
+
test_exception :NoHandler,
|
26
|
+
:message => "No handler is set! Maybe you need to call " <<
|
27
|
+
"'set_default_handler'?"
|
28
|
+
|
29
|
+
test_exception :MissingParameter,
|
30
|
+
:args => :blah,
|
31
|
+
:message => "Missing parameter :blah!"
|
32
|
+
|
33
|
+
test_exception :MissingBlock,
|
34
|
+
:args => :blah,
|
35
|
+
:message => "Can't call :blah without a block!"
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rainman::Handler do
|
4
|
+
before do
|
5
|
+
Rainman::Driver.instance_variable_set(:@all, [])
|
6
|
+
@module = Module.new do
|
7
|
+
def self.name
|
8
|
+
'MissDaisy'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
@module.extend Rainman::Driver
|
12
|
+
Object.send(:remove_const, :MissDaisy) if Object.const_defined?(:MissDaisy)
|
13
|
+
Object.const_set(:MissDaisy, @module)
|
14
|
+
|
15
|
+
@class = Class.new do
|
16
|
+
extend Rainman::Handler
|
17
|
+
end
|
18
|
+
@class.instance_variable_set(:@handler_name, :blah)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#handler_name" do
|
22
|
+
it "returns @handler_name" do
|
23
|
+
@class.handler_name.should == :blah
|
24
|
+
@class.handler_name.should eq @class.instance_variable_get(:@handler_name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|