rohbau 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+