rohbau 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.
- 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
|
+
|