rohbau 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +27 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +189 -0
- data/README.md.template +119 -0
- data/Rakefile +10 -0
- data/bin/build_readme +1 -0
- data/etc/build_readme.rb +14 -0
- data/examples/runtime.rb +49 -0
- data/examples/service_factory.rb +21 -0
- data/examples/service_factory_validation.rb +11 -0
- data/lib/rohbau/application.rb +70 -0
- data/lib/rohbau/default_memory_gateway.rb +78 -0
- data/lib/rohbau/entity.rb +41 -0
- data/lib/rohbau/event_tube.rb +42 -0
- data/lib/rohbau/index.rb +214 -0
- data/lib/rohbau/it_behaves_like.rb +26 -0
- data/lib/rohbau/minitest/exclude.rb +58 -0
- data/lib/rohbau/registry.rb +45 -0
- data/lib/rohbau/request.rb +24 -0
- data/lib/rohbau/require.rb +20 -0
- data/lib/rohbau/runtime.rb +94 -0
- data/lib/rohbau/runtime_loader.rb +67 -0
- data/lib/rohbau/service_factory.rb +37 -0
- data/lib/rohbau/shared_spec.rb +47 -0
- data/lib/rohbau/shared_specs/default_gateway.rb +305 -0
- data/lib/rohbau/use_case.rb +25 -0
- data/lib/rohbau/version.rb +3 -0
- data/lib/rohbau.rb +5 -0
- data/rohbau.gemspec +39 -0
- data/spec/event_tube_spec.rb +63 -0
- data/spec/runtime_loader_spec.rb +72 -0
- data/spec/service_factory_spec.rb +109 -0
- data/spec/shared_spec_spec.rb +44 -0
- data/spec/spec_helper.rb +12 -0
- metadata +178 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rohbau
|
2
|
+
class EventTube
|
3
|
+
def self.reset
|
4
|
+
@subscriptions = nil
|
5
|
+
end
|
6
|
+
def self.publish(name, event)
|
7
|
+
subscription_handler.handle(name, event)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.subscribe(name, &handler)
|
11
|
+
subscription_handler.add(name, &handler)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.subscription_handler
|
15
|
+
@subscriptions ||= SubscriptionHandler.new
|
16
|
+
end
|
17
|
+
|
18
|
+
class SubscriptionHandler
|
19
|
+
def initialize
|
20
|
+
@subscriptions = Hash.new do |h,k|
|
21
|
+
h[k] = []
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(name, &handler)
|
26
|
+
subscriptions[name] << handler
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def handle(name, event)
|
31
|
+
subscriptions[name].each do |handler|
|
32
|
+
handler.call(event)
|
33
|
+
end
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :subscriptions
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/rohbau/index.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'thread_safe'
|
2
|
+
|
3
|
+
module Rohbau
|
4
|
+
class Index
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@last_uid = 0
|
9
|
+
@entities = ThreadSafe::Hash.new
|
10
|
+
|
11
|
+
@validator = Validator.new(self)
|
12
|
+
|
13
|
+
@options = {
|
14
|
+
:uid_generation => true,
|
15
|
+
:mapper => DefaultMapper,
|
16
|
+
:unmapper => DefaultMapper
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def add(entity)
|
21
|
+
|
22
|
+
if option?(:uid_generation)
|
23
|
+
validate :add, entity
|
24
|
+
|
25
|
+
added_entity = copy(entity).tap do |new_entity|
|
26
|
+
new_entity.uid = next_uid
|
27
|
+
entities[new_entity.uid] = map(new_entity)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
validate :add_with_uid, entity
|
31
|
+
|
32
|
+
added_entity = copy(entity).tap do |new_entity|
|
33
|
+
entities[new_entity.uid] = map(new_entity)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
get added_entity.uid
|
38
|
+
end
|
39
|
+
|
40
|
+
def bulk_add(entities)
|
41
|
+
entities.map do |entity|
|
42
|
+
add entity
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def get(uid)
|
47
|
+
validate :get, uid
|
48
|
+
|
49
|
+
unmap(entities[uid])
|
50
|
+
end
|
51
|
+
|
52
|
+
def update(entity)
|
53
|
+
validate :update, entity
|
54
|
+
|
55
|
+
entities[entity.uid] = map(entity)
|
56
|
+
|
57
|
+
get entity.uid
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete(uid)
|
61
|
+
validate :delete, uid
|
62
|
+
|
63
|
+
unmap entities.delete(uid)
|
64
|
+
end
|
65
|
+
|
66
|
+
def bulk_delete(uids)
|
67
|
+
uids.map do |uid|
|
68
|
+
delete uid
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def all
|
73
|
+
entities.values.map(&method(:unmap))
|
74
|
+
end
|
75
|
+
|
76
|
+
def each(&block)
|
77
|
+
all.each(&block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def size
|
81
|
+
entities.size
|
82
|
+
end
|
83
|
+
|
84
|
+
def has_uid?(uid)
|
85
|
+
entities.key?(uid)
|
86
|
+
end
|
87
|
+
|
88
|
+
def option(key, value = nil)
|
89
|
+
if value != nil
|
90
|
+
@options[key] = value
|
91
|
+
else
|
92
|
+
@options[key]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def option?(key)
|
97
|
+
!!@options[key]
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def map(entity)
|
103
|
+
option(:mapper).call(entity)
|
104
|
+
end
|
105
|
+
|
106
|
+
def unmap(mapped_entity)
|
107
|
+
option(:unmapper).call(mapped_entity)
|
108
|
+
end
|
109
|
+
|
110
|
+
def copy(entity)
|
111
|
+
Marshal.load(Marshal.dump entity)
|
112
|
+
end
|
113
|
+
|
114
|
+
def next_uid
|
115
|
+
"#{@last_uid += 1}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def entities
|
119
|
+
@entities
|
120
|
+
end
|
121
|
+
|
122
|
+
def validate(method, *args)
|
123
|
+
@validator.public_send("validate_#{method}", *args)
|
124
|
+
end
|
125
|
+
|
126
|
+
class Validator
|
127
|
+
def initialize(memory)
|
128
|
+
@memory = memory
|
129
|
+
end
|
130
|
+
|
131
|
+
def validate_add(entity)
|
132
|
+
ensure_entity!(entity)
|
133
|
+
ensure_entity_has_no_uid!(entity)
|
134
|
+
end
|
135
|
+
|
136
|
+
def validate_add_with_uid(entity)
|
137
|
+
ensure_entity!(entity)
|
138
|
+
ensure_entity_has_uid!(entity)
|
139
|
+
ensure_uid_type!(entity.uid)
|
140
|
+
end
|
141
|
+
|
142
|
+
def validate_get(uid)
|
143
|
+
ensure_uid!(uid)
|
144
|
+
ensure_uid_type!(uid)
|
145
|
+
end
|
146
|
+
|
147
|
+
def validate_update(entity)
|
148
|
+
ensure_entity!(entity)
|
149
|
+
ensure_entity_has_uid!(entity)
|
150
|
+
ensure_entity_exists!(entity)
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_delete(uid)
|
154
|
+
ensure_uid_exists!(uid)
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
private
|
159
|
+
def ensure_uid!(uid)
|
160
|
+
if uid.nil?
|
161
|
+
raise argument_error('uid is missing')
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def ensure_uid_type!(uid)
|
166
|
+
if !uid.kind_of?(String) || uid.size == 0
|
167
|
+
raise argument_error('uid is invalid', uid)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def ensure_entity!(entity)
|
172
|
+
if entity.nil?
|
173
|
+
raise argument_error('entity is invalid', entity)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def ensure_entity_has_uid!(entity)
|
178
|
+
if !entity.uid
|
179
|
+
raise argument_error('entity has no uid', entity)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def ensure_entity_has_no_uid!(entity)
|
184
|
+
if entity.uid
|
185
|
+
raise argument_error('entity has uid', entity)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def ensure_entity_exists!(entity)
|
190
|
+
if !@memory.has_uid?(entity.uid)
|
191
|
+
raise argument_error('entity is unknown', entity)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def ensure_uid_exists!(uid)
|
196
|
+
if !@memory.has_uid?(uid)
|
197
|
+
raise argument_error('uid is unknown', uid)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def argument_error(error, object = nil)
|
202
|
+
message = "#{self.class.inspect}: #{error}"
|
203
|
+
message += ": #{object.inspect}" if object
|
204
|
+
ArgumentError.new(message)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class DefaultMapper
|
209
|
+
def self.call(entity)
|
210
|
+
Marshal.load(Marshal.dump entity)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
|
3
|
+
module Rohbau
|
4
|
+
module ItBehavesLike
|
5
|
+
|
6
|
+
def get_shared_example(spec_name)
|
7
|
+
Rohbau::SharedSpec::SpecIndex.get spec_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def it_behaves_like(spec)
|
11
|
+
if spec.kind_of? Proc
|
12
|
+
shared_example = spec
|
13
|
+
else
|
14
|
+
shared_example = get_shared_example(spec)
|
15
|
+
end
|
16
|
+
raise "No shared spec for #{spec.inspect} found" unless shared_example
|
17
|
+
|
18
|
+
instance_eval(&shared_example)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class MiniTest::Spec
|
25
|
+
extend Rohbau::ItBehavesLike
|
26
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Rohbau
|
2
|
+
module Minitest
|
3
|
+
module Exclude
|
4
|
+
|
5
|
+
class SpecNuker
|
6
|
+
def initialize(description_name, description_caller)
|
7
|
+
spec_class = find_spec_class(description_caller)
|
8
|
+
@description_caller = description_caller
|
9
|
+
@description_name = description_name
|
10
|
+
@description_classes = spec_class.children
|
11
|
+
end
|
12
|
+
|
13
|
+
def nuke!(it_desc)
|
14
|
+
it_method_name = find_it_method(it_desc)
|
15
|
+
|
16
|
+
if it_method_name.nil?
|
17
|
+
inspected_method = "\"#{@description_caller}\"##{it_desc.inspect}"
|
18
|
+
msg = "Method #{inspected_method} is not defined"
|
19
|
+
return warn(msg)
|
20
|
+
end
|
21
|
+
|
22
|
+
description_class.send :undef_method, it_method_name
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def find_spec_class(description_class)
|
28
|
+
description_class.superclass
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_it_method(it_desc)
|
32
|
+
description_class.instance_methods.detect do |instance_method|
|
33
|
+
instance_method =~ %r{test_[0-9]+_#{it_desc}}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def description_class
|
38
|
+
@description_classes.detect do |description_class|
|
39
|
+
description_class.name == @description_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module MiniTest::Spec::DSL
|
50
|
+
|
51
|
+
|
52
|
+
# only single nesting in one describe block is supported
|
53
|
+
# for now
|
54
|
+
def exclude(it_desc, reason = "")
|
55
|
+
Rohbau::Minitest::Exclude::SpecNuker.new(self.name, self).nuke!(it_desc)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Rohbau
|
2
|
+
module Registry
|
3
|
+
def self.included(cls)
|
4
|
+
cls.send :extend, ExternalInterface
|
5
|
+
cls.send :include, Implementation
|
6
|
+
end
|
7
|
+
|
8
|
+
module Implementation
|
9
|
+
def cache(name, &block)
|
10
|
+
@cache ||= {}
|
11
|
+
@cache[name] ||= block.call
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ExternalInterface
|
16
|
+
def register(service_name, &constructor)
|
17
|
+
registrations << service_name
|
18
|
+
implementations[service_name] << constructor
|
19
|
+
this = self
|
20
|
+
|
21
|
+
this.send :define_method, service_name do
|
22
|
+
cache service_name do
|
23
|
+
instance_eval(&this.implementations[service_name].last)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def unregister(service_name)
|
29
|
+
implementations[service_name].pop
|
30
|
+
|
31
|
+
if implementations[service_name].empty?
|
32
|
+
self.send :remove_method, service_name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def registrations
|
37
|
+
@registrations ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def implementations
|
41
|
+
@implementations ||= Hash.new { |hash, key| hash[key] = [] }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rohbau
|
2
|
+
class Request
|
3
|
+
|
4
|
+
def initialize(runtime)
|
5
|
+
raise "No Runtime instanciated (#{self.inspect})" unless runtime
|
6
|
+
@runtime = runtime
|
7
|
+
end
|
8
|
+
|
9
|
+
def services
|
10
|
+
@service_factory ||= build_service_factory
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def build_service_factory
|
16
|
+
raise NotImplementedError, "Please provide #{self.class}#build_service_factory"
|
17
|
+
end
|
18
|
+
|
19
|
+
def runtime
|
20
|
+
@runtime
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rohbau
|
2
|
+
module Require
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def require_all(*sub_dirs)
|
6
|
+
called_by = caller_locations(1, 1).first
|
7
|
+
file = called_by.absolute_path
|
8
|
+
|
9
|
+
dir = File.basename(file, '.rb')
|
10
|
+
|
11
|
+
sub_dirs.each do |sub_dir|
|
12
|
+
dir = File.join(dir, sub_dir)
|
13
|
+
end
|
14
|
+
|
15
|
+
files = File.expand_path("../#{dir}/*.rb", file)
|
16
|
+
Dir[files].each { |f| require f }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'thread_safe'
|
3
|
+
|
4
|
+
module Rohbau
|
5
|
+
class Runtime
|
6
|
+
def self.register(name, plugin_class)
|
7
|
+
attr_reader name
|
8
|
+
plugins[name] = plugin_class
|
9
|
+
plugin_class.registered(self) if plugin_class.respond_to?(:registered)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.unregister(name)
|
13
|
+
remove_method name
|
14
|
+
plugins.delete name
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.plugins
|
18
|
+
@plugins ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
on_boot
|
23
|
+
initialize_plugins
|
24
|
+
after_boot
|
25
|
+
end
|
26
|
+
|
27
|
+
def terminate
|
28
|
+
terminate_plugins
|
29
|
+
after_termination
|
30
|
+
end
|
31
|
+
|
32
|
+
def root
|
33
|
+
Pathname.new(Dir.pwd)
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def on_boot
|
39
|
+
# noop
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_boot
|
43
|
+
# noop
|
44
|
+
end
|
45
|
+
|
46
|
+
def after_termination
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def initialize_plugins
|
51
|
+
self.class.plugins.each do |name, plugin_class|
|
52
|
+
instance_variable_set :"@#{name}", plugin_class.new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def notify_plugins(message, *args)
|
57
|
+
self.class.plugins.each do |name, _|
|
58
|
+
plugin = public_send(name)
|
59
|
+
if plugin.instance.respond_to? message
|
60
|
+
plugin.instance.public_send(message, *args)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def terminate_plugins
|
66
|
+
self.class.plugins.each do |name, _|
|
67
|
+
plugin = public_send(name)
|
68
|
+
plugin.terminate if plugin.respond_to? :terminate
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_transaction(&block)
|
73
|
+
if transaction_handling_plugin
|
74
|
+
transaction_handling_plugin.transaction_handler(&block)
|
75
|
+
else
|
76
|
+
block.call
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def transaction_handling_plugin
|
81
|
+
return @handling_plugin if defined? @handling_plugin
|
82
|
+
|
83
|
+
plugin = self.class.plugins.detect do |_, runtime_loader|
|
84
|
+
runtime_loader.instance.respond_to? :transaction_handler
|
85
|
+
end
|
86
|
+
if plugin
|
87
|
+
@handling_plugin = plugin[1].instance
|
88
|
+
else
|
89
|
+
@handling_plugin = nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Rohbau
|
2
|
+
class RuntimeLoader
|
3
|
+
class << self
|
4
|
+
def instance
|
5
|
+
@instance
|
6
|
+
end
|
7
|
+
|
8
|
+
def running?
|
9
|
+
!!instance
|
10
|
+
end
|
11
|
+
|
12
|
+
def terminate
|
13
|
+
instance.terminate if instance.respond_to? :terminate
|
14
|
+
remove_instance_variable :@instance if defined? @instance
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :registrar
|
18
|
+
|
19
|
+
def registered(registrar)
|
20
|
+
self.registrar = registrar
|
21
|
+
end
|
22
|
+
|
23
|
+
def new(*args)
|
24
|
+
super(*args)
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(runtime)
|
30
|
+
return if singleton_assigned?
|
31
|
+
build_singleton(runtime)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def singleton_assigned?
|
37
|
+
self.class.instance_variable_defined? singleton_variable
|
38
|
+
end
|
39
|
+
|
40
|
+
def singleton
|
41
|
+
self.class.instance_variable_get singleton_variable
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize_with_immediate_callback(cls, &callback)
|
45
|
+
cls.send :define_method, :initialize do |*args|
|
46
|
+
callback.call(self)
|
47
|
+
super(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
cls.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def assign_to_singleton(instance)
|
54
|
+
self.class.instance_variable_set singleton_variable, instance
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_singleton(runtime)
|
58
|
+
initialize_with_immediate_callback runtime do |instance|
|
59
|
+
assign_to_singleton(instance)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def singleton_variable
|
64
|
+
:@instance
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rohbau/registry'
|
2
|
+
|
3
|
+
module Rohbau
|
4
|
+
class ServiceFactory
|
5
|
+
include Rohbau::Registry
|
6
|
+
|
7
|
+
def initialize(runtime)
|
8
|
+
raise "No Runtime instanciated" unless runtime
|
9
|
+
@runtime = runtime
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.external_dependencies_complied?
|
13
|
+
missing_dependencies.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.missing_dependencies
|
17
|
+
external_dependencies.reject do |dependency|
|
18
|
+
registrations.include? dependency
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.external_dependencies(*dependencies)
|
23
|
+
if dependencies.any?
|
24
|
+
@external_dependencies = dependencies
|
25
|
+
else
|
26
|
+
@external_dependencies ||= []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def runtime
|
33
|
+
@runtime
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rohbau/it_behaves_like'
|
2
|
+
require 'rohbau/minitest/exclude'
|
3
|
+
|
4
|
+
module Rohbau
|
5
|
+
class SharedSpec < Proc
|
6
|
+
|
7
|
+
def self.for(name, &block)
|
8
|
+
shared_specs[name] = Proc.new(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.get(name)
|
12
|
+
shared_specs[name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.inherited(child_class)
|
16
|
+
SpecIndex.register child_class
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.shared_specs
|
20
|
+
@shared_specs ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
class SpecIndex
|
24
|
+
def self.reset
|
25
|
+
@specs = []
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.register(shared_spec_class)
|
29
|
+
reset unless @specs
|
30
|
+
@specs << shared_spec_class
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.all
|
34
|
+
@specs.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.get(name)
|
38
|
+
@specs.each do |spec|
|
39
|
+
found = spec.get(name)
|
40
|
+
return found if found
|
41
|
+
end
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|