rohbau 0.1.0 → 0.2.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 +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
|
|