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