rohbau 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +18 -0
  3. data/.rubocop_todo.yml +149 -0
  4. data/.travis.yml +2 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +1 -1
  7. data/README.md +404 -20
  8. data/README.md.template +104 -20
  9. data/Rakefile +1 -1
  10. data/bin/build_readme +3 -1
  11. data/examples/email_service/email_service.rb +5 -0
  12. data/examples/event_tube.rb +9 -0
  13. data/examples/my_application.rb +13 -0
  14. data/examples/runtime.rb +7 -24
  15. data/examples/service_factory_validation.rb +4 -4
  16. data/examples/use_case.rb +10 -0
  17. data/examples/user_entity.rb +20 -0
  18. data/examples/user_service/create_user_use_case.rb +14 -0
  19. data/examples/user_service/event_tube.rb +6 -0
  20. data/examples/user_service/request.rb +16 -0
  21. data/examples/user_service/runtime.rb +13 -0
  22. data/examples/user_service/service_factory.rb +10 -0
  23. data/examples/user_service/user_gateway.rb +16 -0
  24. data/examples/user_service.rb +1 -0
  25. data/examples/verify/examples.txt +31 -0
  26. data/examples/verify/examples_spec.rb +25 -0
  27. data/lib/rohbau/application.rb +5 -8
  28. data/lib/rohbau/default_memory_gateway.rb +0 -2
  29. data/lib/rohbau/entity.rb +1 -3
  30. data/lib/rohbau/event_tube.rb +2 -1
  31. data/lib/rohbau/index.rb +4 -5
  32. data/lib/rohbau/interface.rb +130 -0
  33. data/lib/rohbau/it_behaves_like.rb +0 -2
  34. data/lib/rohbau/minitest/exclude.rb +0 -6
  35. data/lib/rohbau/registry.rb +4 -6
  36. data/lib/rohbau/request.rb +2 -4
  37. data/lib/rohbau/request_cache.rb +19 -0
  38. data/lib/rohbau/require.rb +0 -1
  39. data/lib/rohbau/runtime.rb +0 -2
  40. data/lib/rohbau/service_factory.rb +1 -2
  41. data/lib/rohbau/shared_spec.rb +0 -2
  42. data/lib/rohbau/shared_specs/default_gateway.rb +2 -6
  43. data/lib/rohbau/use_case.rb +0 -3
  44. data/lib/rohbau/version.rb +1 -1
  45. data/lib/rohbau.rb +19 -1
  46. data/rakelib/build_readme.rake +23 -0
  47. data/rakelib/ci.rake +5 -0
  48. data/rakelib/examples.rake +16 -0
  49. data/rakelib/rubocop.rake +18 -0
  50. data/spec/event_tube_spec.rb +2 -3
  51. data/spec/interface_spec.rb +111 -0
  52. data/spec/runtime_loader_spec.rb +1 -2
  53. data/spec/service_factory_spec.rb +6 -9
  54. data/spec/shared_spec_spec.rb +0 -2
  55. metadata +43 -20
  56. data/etc/build_readme.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d05d28272851e7d359b3640cdb9e4a21250f704
4
- data.tar.gz: 183ca9c967b841d7dc8e4bc6dbdd57e222eab5fc
3
+ metadata.gz: aba07a5627f76c6095247bcbfbd5ef8f4cc3e221
4
+ data.tar.gz: 79c36065afca542bc02b79a77eca590e4b9bdde4
5
5
  SHA512:
6
- metadata.gz: 9b144522c39a33baa618b912f17e3f9a2a6f743a910194cc052409cd890992a0994eb629da418eb5b5d8cb1816f64159f944c9daada4b55546b1b58f71a86529
7
- data.tar.gz: 9e6e8cd4e34c668c81172180d3ca99b64af7d5b32e9b1d19a35a72c420e2ad15eda820b0c122f6529bb2a946fe56c0a1d7dbc854bb5b4e458791edfd661e96f7
6
+ metadata.gz: f3951dabbee7a5af8d4e279935b33bed6475ec31a4b90ed8446ad03b6b89c93496ddcc26f6c1feaf849aee1316bf8ef0b9b6eda065a071e40bf6e34347459ed7
7
+ data.tar.gz: 54b582e38ce22964cdc44b87f0b3a2f2aa776bd6241b0bd74f5dcbe7180970635c8cbff8b72ed15256ff9197f9d4f12391790567e6a4ffeca0171c95d14b456c
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ # Generated by `rubocop --auto-gen-config`
2
+ inherit_from: .rubocop_todo.yml
3
+
4
+ Metrics/LineLength:
5
+ Exclude:
6
+ - rohbau.gemspec
7
+ - examples/**/*.rb
8
+
9
+ Style/AlignParameters:
10
+ EnforcedStyle: with_fixed_indentation
11
+
12
+ # Prefer foo: :bar
13
+ Style/HashSyntax:
14
+ EnforcedStyle: hash_rockets
15
+
16
+ # Let Inch check that
17
+ Style/Documentation:
18
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,149 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2015-03-08 22:50:04 +0100 using RuboCop version 0.29.1.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 1
9
+ Lint/HandleExceptions:
10
+ Enabled: false
11
+
12
+ # Offense count: 1
13
+ # Cop supports --auto-correct.
14
+ Lint/UnusedBlockArgument:
15
+ Enabled: false
16
+
17
+ # Offense count: 2
18
+ # Cop supports --auto-correct.
19
+ Lint/UnusedMethodArgument:
20
+ Enabled: false
21
+
22
+ # Offense count: 2
23
+ Lint/UselessComparison:
24
+ Enabled: false
25
+
26
+ # Offense count: 2
27
+ Lint/Void:
28
+ Enabled: false
29
+
30
+ # Offense count: 1
31
+ Metrics/AbcSize:
32
+ Max: 20
33
+
34
+ # Offense count: 3
35
+ # Configuration parameters: CountComments.
36
+ Metrics/MethodLength:
37
+ Max: 14
38
+
39
+ # Offense count: 2
40
+ Style/AccessorMethodName:
41
+ Enabled: false
42
+
43
+ # Offense count: 2
44
+ Style/CaseEquality:
45
+ Enabled: false
46
+
47
+ # Offense count: 2
48
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
49
+ Style/ClassAndModuleChildren:
50
+ Enabled: false
51
+
52
+ # Offense count: 2
53
+ # Cop supports --auto-correct.
54
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
55
+ Style/ClassCheck:
56
+ Enabled: false
57
+
58
+ # Offense count: 3
59
+ Style/DoubleNegation:
60
+ Enabled: false
61
+
62
+ # Offense count: 1
63
+ Style/EachWithObject:
64
+ Enabled: false
65
+
66
+ # Offense count: 8
67
+ # Configuration parameters: MinBodyLength.
68
+ Style/GuardClause:
69
+ Enabled: false
70
+
71
+ # Offense count: 6
72
+ # Configuration parameters: MaxLineLength.
73
+ Style/IfUnlessModifier:
74
+ Enabled: false
75
+
76
+ # Offense count: 1
77
+ # Cop supports --auto-correct.
78
+ # Configuration parameters: IncludeSemanticChanges.
79
+ Style/NonNilCheck:
80
+ Enabled: false
81
+
82
+ # Offense count: 2
83
+ # Cop supports --auto-correct.
84
+ # Configuration parameters: PreferredDelimiters.
85
+ Style/PercentLiteralDelimiters:
86
+ Enabled: false
87
+
88
+ # Offense count: 1
89
+ # Cop supports --auto-correct.
90
+ Style/PerlBackrefs:
91
+ Enabled: false
92
+
93
+ # Offense count: 1
94
+ # Configuration parameters: NamePrefix, NamePrefixBlacklist.
95
+ Style/PredicateName:
96
+ Enabled: false
97
+
98
+ # Offense count: 1
99
+ # Cop supports --auto-correct.
100
+ Style/Proc:
101
+ Enabled: false
102
+
103
+ # Offense count: 3
104
+ # Cop supports --auto-correct.
105
+ Style/RedundantSelf:
106
+ Enabled: false
107
+
108
+ # Offense count: 3
109
+ # Configuration parameters: MaxSlashes.
110
+ Style/RegexpLiteral:
111
+ Enabled: false
112
+
113
+ # Offense count: 12
114
+ # Cop supports --auto-correct.
115
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
116
+ Style/SignalException:
117
+ Enabled: false
118
+
119
+ # Offense count: 1
120
+ # Cop supports --auto-correct.
121
+ Style/SpecialGlobalVars:
122
+ Enabled: false
123
+
124
+ # Offense count: 31
125
+ # Cop supports --auto-correct.
126
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
127
+ Style/StringLiterals:
128
+ Enabled: false
129
+
130
+ # Offense count: 1
131
+ Style/StructInheritance:
132
+ Enabled: false
133
+
134
+ # Offense count: 7
135
+ # Cop supports --auto-correct.
136
+ # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, Whitelist.
137
+ Style/TrivialAccessors:
138
+ Enabled: false
139
+
140
+ # Offense count: 1
141
+ # Cop supports --auto-correct.
142
+ Style/UnneededPercentQ:
143
+ Enabled: false
144
+
145
+ # Offense count: 1
146
+ # Cop supports --auto-correct.
147
+ # Configuration parameters: WordRegex.
148
+ Style/WordArray:
149
+ MinSize: 2
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  cache: bundler
4
+ script: "bundle exec rake ci"
4
5
  rvm:
5
6
  - ruby-head
6
7
  - 2.2
@@ -12,6 +13,7 @@ rvm:
12
13
  - jruby-19mode # JRuby in 1.9 mode
13
14
  env:
14
15
  global:
16
+ - CODECLIMATE_REPO_TOKEN=bff5110004e542a78ce5dd5fb7f154ac06cdc5a564d54957656a56e79bf1a031
15
17
  - JRUBY_OPTS='--dev -J-Xmx1024M'
16
18
  matrix:
17
19
  fast_finish: true
data/Gemfile CHANGED
@@ -6,3 +6,7 @@ gemspec
6
6
  if ENV['CODECLIMATE_REPO_TOKEN']
7
7
  gem "codeclimate-test-reporter", :group => :test, :require => nil
8
8
  end
9
+
10
+ group :tools do
11
+ gem 'rubocop'
12
+ end
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 TODO: Write your name
1
+ Copyright (c) 2013-2015 Neopoly GmbH
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  ## Description
26
26
 
27
- Rohbau provides a set of patterns used in Domain Driven Design.
27
+ _Rohbau_ provides a set of patterns used in Domain Driven Design.
28
28
 
29
29
  ## Installation
30
30
 
@@ -52,6 +52,8 @@ By this a place is made where for example memories for in-memory gateway backend
52
52
 
53
53
  Inject a user service to your application
54
54
 
55
+ `examples/my_application.rb`
56
+
55
57
  ```ruby
56
58
  require 'rohbau/runtime'
57
59
  require 'rohbau/runtime_loader'
@@ -67,6 +69,14 @@ module MyApplication
67
69
  end
68
70
  end
69
71
 
72
+ ```
73
+
74
+ `examples/user_service/runtime.rb`
75
+
76
+ ```ruby
77
+ require 'rohbau/runtime'
78
+ require 'rohbau/runtime_loader'
79
+
70
80
  module UserService
71
81
  class RuntimeLoader < Rohbau::RuntimeLoader
72
82
  def initialize
@@ -78,10 +88,23 @@ module UserService
78
88
  end
79
89
  end
80
90
 
91
+ ```
92
+
93
+ `examples/runtime.rb`
94
+
95
+ ```ruby
96
+ require 'my_application'
97
+ require 'user_service/runtime'
98
+
81
99
  # Register user service on my application runtime
82
100
  MyApplication::Runtime.register :user_service, UserService::RuntimeLoader
101
+
102
+ # My application runtime knowns about the registered plugins
83
103
  MyApplication::Runtime.plugins # => {:user_service=>UserService::RuntimeLoader}
84
104
 
105
+ # The registered plugin knows his registrar
106
+ UserService::RuntimeLoader.registrar # => MyApplication::Runtime
107
+
85
108
  # Runtimes are not initialized yet
86
109
  MyApplication::RuntimeLoader.instance # => nil
87
110
  MyApplication::Runtime.plugins[:user_service].instance # => nil
@@ -105,17 +128,6 @@ MyApplication::RuntimeLoader.running? # => false
105
128
 
106
129
  ```
107
130
 
108
- ##### Registrar
109
-
110
- Every injected `RuntimeLoader` knows about it's registrar.
111
- In the example above `UserService::RuntimeLoader` has been injected to `MyApplication::RuntimeLoader`.
112
- `UserService::RuntimeLoader.registrar` therefore returns `MyApplication::RuntimeLoader`.
113
-
114
- ##### List of plugins
115
-
116
- Accordingly to the sample above `MyApplication::RuntimeLoader` knows about it's registered plugins.
117
- `MyApplication::RuntimeLoader.plugins` therefore returns `{:user_service => UserService::RuntimeLoader}`.
118
-
119
131
  ### ServiceFactory
120
132
 
121
133
  The `ServiceFactory` is considered the authority for retrieval of service instances.
@@ -125,6 +137,8 @@ It follows partly the service locator / registry pattern.
125
137
 
126
138
  Register and unregister default service and override with specific service.
127
139
 
140
+ `examples/user_service/service_factory.rb`
141
+
128
142
  ```ruby
129
143
  require 'rohbau/service_factory'
130
144
 
@@ -152,33 +166,403 @@ registry.user_service # => NoMethodError: undefined method `user_service'
152
166
 
153
167
  Validate registered dependencies
154
168
 
169
+ `examples/user_service/service_factory_validation.rb`
170
+
155
171
  ```ruby
172
+ require 'rohbau/service_factory'
173
+
174
+ MyServiceFactory = Class.new(Rohbau::ServiceFactory)
175
+
156
176
  MyServiceFactory.external_dependencies :user_service
157
- MyServiceFactory.missing_dependencies # => [:user_service]
177
+ MyServiceFactory.missing_dependencies # => [:user_service]
158
178
  MyServiceFactory.external_dependencies_complied? # => false
159
179
 
160
- MyServiceFactory.register(:user_service) { Object.new } # => :user_service
161
- MyServiceFactory.external_dependencies_complied? # => true
162
- MyServiceFactory.missing_dependencies # => []
180
+ MyServiceFactory.register(:user_service) { Object.new } # => :user_service
181
+ MyServiceFactory.external_dependencies_complied? # => true
182
+ MyServiceFactory.missing_dependencies # => []
183
+
184
+ ```
185
+
186
+ ### Request
187
+
188
+ It ensures an initialized runtime and builds up a new the service factory instance.
189
+
190
+ `examples/user_service/request.rb`
191
+
192
+ ```ruby
193
+ require 'rohbau/request'
194
+ require 'user_service/service_factory'
195
+
196
+ module UserService
197
+ class Request < Rohbau::Request
198
+ def initialize(runtime = RuntimeLoader.instance)
199
+ super(runtime)
200
+ end
201
+
202
+ protected
203
+
204
+ def build_service_factory
205
+ ServiceFactory.new(@runtime)
206
+ end
207
+ end
208
+ end
209
+
210
+ ```
211
+
212
+ ### Entity
213
+
214
+ Entities are low level, logic-less, data structures.
215
+
216
+ `examples/user_entity.rb`
217
+
218
+ ```ruby
219
+ require 'rohbau/entity'
220
+
221
+ class User < Rohbau::Entity
222
+ attributes :uid, :nickname
223
+
224
+ def initialize(user_data = {})
225
+ self.nickname = user_data[:nickname]
226
+ super()
227
+ end
228
+ end
229
+
230
+ bob = User.new
231
+ bob.nickname = 'Bob'
232
+ bob.nickname # => 'Bob'
233
+
234
+ other_bob = User.new
235
+ other_bob.nickname = 'Bob'
236
+ other_bob.nickname # => 'Bob'
237
+
238
+ bob == other_bob # => true
239
+
240
+ ```
241
+
242
+ ### Gateway
243
+
244
+ Provides an interface to persist entities.
245
+
246
+ `examples/user_service/user_gateway.rb`
247
+
248
+ ```ruby
249
+ require 'user_service/event_tube'
250
+ require 'user_entity'
251
+ require 'rohbau/default_memory_gateway'
252
+
253
+ module UserService
254
+ class UserGateway < Rohbau::DefaultMemoryGateway
255
+ def create(user_data)
256
+ user = User.new(user_data)
257
+ add(user)
258
+ EventTube.publish :user_registered, UserRegisteredEvent.new(user)
259
+ end
260
+
261
+ class UserRegisteredEvent < Struct.new(:user)
262
+ end
263
+ end
264
+ end
265
+
266
+ ```
267
+
268
+ ### UseCase
269
+
270
+ `UseCases` define the interface for the end user who interacts with the system.
271
+
272
+ #### Examples
273
+
274
+ Define a class that inherits from `Rohbau::UseCase` which has a `#call` method:
275
+
276
+ `examples/user_service/create_user_use_case.rb`
277
+
278
+ ```ruby
279
+ require 'rohbau/use_case'
280
+
281
+ module UserService
282
+ class CreateUser < Rohbau::UseCase
283
+ def initialize(request, user_data)
284
+ super(request)
285
+ @user_data = user_data
286
+ end
287
+
288
+ def call
289
+ service(:user_service).create(@user_data)
290
+ end
291
+ end
292
+ end
293
+
294
+ ```
295
+
296
+ And call the CreateUser use case as follows:
297
+
298
+ `examples/use_case.rb`
299
+
300
+ ```ruby
301
+ require 'user_service/runtime'
302
+ require 'user_service/request'
303
+ require 'user_service/create_user_use_case'
304
+
305
+ # Boot up user service
306
+ UserService::RuntimeLoader.new
307
+
308
+ request = UserService::Request.new
309
+ UserService::CreateUser.new(request, {:nickname => 'Bob'}).call # => 'Created user Bob'
310
+
311
+ ```
312
+
313
+ Alternately, use cases can be called using `Interface`, which is detailed in the next section.
314
+
315
+ ### Interface
316
+
317
+ `Interface` allows for simpler and more semantic use case calling, with the additional benefit of fine grain control of return values and spy-like access in a test context.
318
+
319
+ **Please note**: `Interface` requires an `Input` class in your use case, as well as a `Success` class if you are using the stub features.
320
+
321
+ In a nutshell, `Interface` allows your use case calls to go from this:
322
+
323
+ ```ruby
324
+ require 'user_service/runtime'
325
+ require 'user_service/request'
326
+ require 'user_service/create_user_use_case'
327
+
328
+ # Boot up user service
329
+ UserService::RuntimeLoader.new
330
+
331
+ request = UserService::Request.new
332
+ input = {
333
+ :user_data => {
334
+ :nickname => 'Bob'
335
+ }
336
+ }
337
+ UserService::CreateUser.new(request, input).call
338
+
339
+ ```
340
+
341
+ To this:
342
+
343
+ ```ruby
344
+ require 'rohbau/interface'
345
+ require 'user_service/create_user_use_case'
346
+
347
+ interface = Rohbau::Interface.new
348
+ interface.user_service :create_user, :user_data => {
349
+ :nickname => 'Bob'
350
+ }
351
+ ```
352
+
353
+ This increased simplicity is very helpful, but the majority of `Interface`'s usefulness becomes accessible while testing. Assume the following use case:
354
+
355
+ ```ruby
356
+ module UserService
357
+ module UseCases
358
+ class CreateUser
359
+ Input = Bound.required :user_data
360
+ Success = Bound.required :user_uid
361
+ Error = Bound.required :message
362
+
363
+ def initialize(request, input)
364
+ @request = request
365
+ @user_data = input.user_data
366
+ end
367
+
368
+ def call
369
+ result = service(:user_service).create(@user_data)
370
+
371
+ if result.nil?
372
+ Error.new :message => "Something went wrong"
373
+ else
374
+ Success.new :user_uid => "uid_for_#{user_data.nickname}"
375
+ end
376
+ end
377
+ end
378
+ end
379
+ end
380
+ ```
381
+ #### Stubbing use case return values
382
+
383
+ Let's assume this use case will be called, along with many other use cases, by the frontend framework of your choosing.
384
+
385
+ Frontend tests should be implemented with the actual objects they would use in production, but since test isolation is an important concept in DDD, the `UserService` domain, which is outside of the scope of the frontend, should never actually be called.
386
+
387
+ Beyond this, we will also want to stub return values to create the various test cases we may have - when there is no user present, for example.
388
+
389
+ These requirements can be realized by passing the following keys to your use case:
390
+
391
+ * `:stub_result`
392
+ When the `stub_result` key is present, its value will be passed to the called use case and returned in subsequent calls to that same use case as a `Success` object.
393
+ * `:stub_type`
394
+ Much like the `stub_result` key, the `stub_type` key allows the type of return value to be overwritten, provided, of course, that it is a type which is defined by your use case.
395
+
396
+ ```ruby
397
+ require 'rohbau/interface'
398
+
399
+ describe 'stubbing use case return values' do
400
+ let(:interface) { Rohbau::Interface.new }
401
+
402
+ it 'returns subsequent calls to the same use case as stubs' do
403
+ interface.user_service :create_user, :stub_result => {
404
+ :user_data => { :user_uid => "definitely NOT bob's uid" }
405
+ }
406
+
407
+ result = interface.user_service :create_user, :user_data => {
408
+ :nickname => 'bob'
409
+ }
410
+
411
+ assert_kind_of UserService::UseCases::CreateUser::Success, result
412
+ assert_equal "definitely NOT bob's uid", result.user_uid
413
+ end
414
+
415
+ it 'can also return other result types' do
416
+ interface.user_service :create_user,
417
+ :stub_type => :Error,
418
+ :stub_result => {
419
+ :message => "error"
420
+ }
421
+
422
+ result = interface.user_service :create_user
423
+
424
+ assert_kind_of UserService::UseCases::CreateUser::Error, result
425
+ assert_equal 'error', result.message
426
+ end
427
+ end
428
+ ```
429
+ #### Test spying
430
+
431
+ Sometimes it's helpful to look into the use case and see some details about how it has been called. There are two methods to this end, each of which returns a hash with keys corresponding to each use case which has been called:
432
+
433
+ * `interface.calls` is further keyed by argument and returns the value passed to the given argument.
434
+ * `interface.call_count` returns the number of times a given use case has been called.
435
+
436
+ ```ruby
437
+ require 'rohbau/interface'
438
+
439
+ describe 'spying on tests' do
440
+ let(:interface) { Rohbau::Interface.new }
441
+
442
+ it 'records passed arguments by use_case' do
443
+ interface.user_service :create_user, :user_data => {
444
+ :nickname => 'bob'
445
+ }
446
+
447
+ result = interface.calls[:create_user][:user_uid]
448
+
449
+ assert_equal "23", result
450
+ end
451
+
452
+ it 'records number of unstubbed calls to each use_case' do
453
+ interface.user_service :create_user, :stub_result => {
454
+ :user_data => { :user_uid => 'something else' }
455
+ }
456
+
457
+ interface.user_service :create_user, :user_data => {
458
+ :nickname => 'bob'
459
+ }
460
+
461
+ interface.user_service :create_user, :user_data => {
462
+ :nickname => 'bob'
463
+ }
464
+
465
+ assert_equal 2, interface.call_count[:create_user]
466
+ end
467
+ end
468
+ ```
469
+
470
+ #### Cleaning up
471
+
472
+ `Interface` provides the following two convenience methods for cleaning up your test environment:
473
+
474
+ * `interface.clear_stubs` does what it says on the tin - All recorded arguments, call counts, stubbed results and stubbed types are cleared.
475
+ * `interface.clear_cached_requests` empties the request cache.
476
+
477
+ ```ruby
478
+ require 'rohbau/interface'
479
+
480
+ let(:interface) { Rohbau::Interface.new }
481
+ it 'can clear all stubbed results' do
482
+ interface.user_service :create_user, :stub_result => {
483
+ :user_data => { :user_uid => 'something else' }
484
+ }
485
+
486
+ result = interface.user_service :create_user, :user_data => {
487
+ :nickname => 'bob'
488
+ }
489
+
490
+ assert_equal 'something else', result.user_uid
491
+
492
+ interface.clear_stubs
493
+
494
+ result = interface.user_service :create_user, :user_data => {
495
+ :nickname => 'bob'
496
+ }
497
+
498
+ refute_equal 'something else', result.user_uid
499
+ assert_equal 'uid_for_bob', result.user_uid
500
+ end
501
+ ```
502
+
503
+ ### EventTube
504
+
505
+ The `EventTube` implements the `Publish-subscribe` pattern. You can subscribe to events and publish them.
506
+
507
+ #### Examples
508
+
509
+ `examples/email_service/email_service.rb`
510
+
511
+ ```ruby
512
+ class EmailService
513
+ def self.send_user_registration_email_to(user)
514
+ print "Send out email to #{user.nickname}"
515
+ end
516
+ end
517
+
518
+ ```
519
+
520
+ `examples/user_service/event_tube.rb`
521
+
522
+ ```ruby
523
+ require 'rohbau/event_tube'
524
+
525
+ module UserService
526
+ class EventTube < Rohbau::EventTube
527
+ end
528
+ end
529
+
163
530
  ```
164
531
 
165
532
  ## Build README
166
533
 
167
- Make changes to README.md.template, not to README.md
534
+ Make changes to `README.md.template`, not to `README.md`
168
535
 
169
536
  Include examples with
170
537
 
171
538
  ```bash
172
- include_example example_file_name
539
+ include_example 'example_file_name'
173
540
  ```
174
541
 
175
542
  Build README.md with
176
543
 
177
544
  ```bash
178
- ./bin/build_readme
545
+ rake build_readme
546
+ ```
547
+
548
+ Always commit `README.md.template` and `README.md` together.
549
+
550
+ ## Examples
551
+
552
+ Run all examples via
553
+
554
+ ```bash
555
+ rake examples
556
+ ```
557
+
558
+ To verify all examples run:
559
+
560
+ ```bash
561
+ rake examples:verify
179
562
  ```
180
563
 
181
- Always commit README.md.template and README.md together.
564
+ Note: Examples will be verified during CI run.
565
+
182
566
 
183
567
  ## Contributing
184
568