rohbau 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +149 -0
- data/.travis.yml +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +1 -1
- data/README.md +404 -20
- data/README.md.template +104 -20
- data/Rakefile +1 -1
- data/bin/build_readme +3 -1
- data/examples/email_service/email_service.rb +5 -0
- data/examples/event_tube.rb +9 -0
- data/examples/my_application.rb +13 -0
- data/examples/runtime.rb +7 -24
- data/examples/service_factory_validation.rb +4 -4
- data/examples/use_case.rb +10 -0
- data/examples/user_entity.rb +20 -0
- data/examples/user_service/create_user_use_case.rb +14 -0
- data/examples/user_service/event_tube.rb +6 -0
- data/examples/user_service/request.rb +16 -0
- data/examples/user_service/runtime.rb +13 -0
- data/examples/user_service/service_factory.rb +10 -0
- data/examples/user_service/user_gateway.rb +16 -0
- data/examples/user_service.rb +1 -0
- data/examples/verify/examples.txt +31 -0
- data/examples/verify/examples_spec.rb +25 -0
- data/lib/rohbau/application.rb +5 -8
- data/lib/rohbau/default_memory_gateway.rb +0 -2
- data/lib/rohbau/entity.rb +1 -3
- data/lib/rohbau/event_tube.rb +2 -1
- data/lib/rohbau/index.rb +4 -5
- data/lib/rohbau/interface.rb +130 -0
- data/lib/rohbau/it_behaves_like.rb +0 -2
- data/lib/rohbau/minitest/exclude.rb +0 -6
- data/lib/rohbau/registry.rb +4 -6
- data/lib/rohbau/request.rb +2 -4
- data/lib/rohbau/request_cache.rb +19 -0
- data/lib/rohbau/require.rb +0 -1
- data/lib/rohbau/runtime.rb +0 -2
- data/lib/rohbau/service_factory.rb +1 -2
- data/lib/rohbau/shared_spec.rb +0 -2
- data/lib/rohbau/shared_specs/default_gateway.rb +2 -6
- data/lib/rohbau/use_case.rb +0 -3
- data/lib/rohbau/version.rb +1 -1
- data/lib/rohbau.rb +19 -1
- data/rakelib/build_readme.rake +23 -0
- data/rakelib/ci.rake +5 -0
- data/rakelib/examples.rake +16 -0
- data/rakelib/rubocop.rake +18 -0
- data/spec/event_tube_spec.rb +2 -3
- data/spec/interface_spec.rb +111 -0
- data/spec/runtime_loader_spec.rb +1 -2
- data/spec/service_factory_spec.rb +6 -9
- data/spec/shared_spec_spec.rb +0 -2
- metadata +43 -20
- data/etc/build_readme.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aba07a5627f76c6095247bcbfbd5ef8f4cc3e221
|
4
|
+
data.tar.gz: 79c36065afca542bc02b79a77eca590e4b9bdde4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -24,7 +24,7 @@
|
|
24
24
|
|
25
25
|
## Description
|
26
26
|
|
27
|
-
|
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
|
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
|
-
|
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
|
-
|
564
|
+
Note: Examples will be verified during CI run.
|
565
|
+
|
182
566
|
|
183
567
|
## Contributing
|
184
568
|
|