u-service 0.9.0 → 0.10.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/Gemfile.lock +1 -1
- data/README.md +106 -15
- data/lib/micro/service/base.rb +4 -0
- data/lib/micro/service/pipeline.rb +22 -11
- data/lib/micro/service/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9aa9c4a076ce6c0f78c14a4084200eef94d7e04feda739ae80a715d83b17d29
|
4
|
+
data.tar.gz: 4d52dc751a9c0da7dfbca46bae74b00286422835c201e1ee27dbfb7b467fa010
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 207f7141d5e7dcc5063d0ddbad53f51bb3c6991e287b2dac25de21c77e8c2386b6200b62f0eea819a22dd42b3fd86e0f7f37c62f8cf99e93080f9223451c566f
|
7
|
+
data.tar.gz: e8a887fd59c3663aeea410b8ca840aa1d507a3ff239edb0ac8909932c7776e68a13e705ab9b8262b84a5c812fdf0e1e97c016c30ae19e47f1e009476a41ca6ae
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -13,10 +13,11 @@ Create simple and powerful service objects.
|
|
13
13
|
- [Installation](#installation)
|
14
14
|
- [Usage](#usage)
|
15
15
|
- [How to create a Service Object?](#how-to-create-a-service-object)
|
16
|
-
- [How to use the
|
16
|
+
- [How to use the result hooks?](#how-to-use-the-result-hooks)
|
17
17
|
- [How to create a pipeline of Service Objects?](#how-to-create-a-pipeline-of-service-objects)
|
18
18
|
- [What is a strict Service Object?](#what-is-a-strict-service-object)
|
19
19
|
- [How to validate Service Object attributes?](#how-to-validate-service-object-attributes)
|
20
|
+
- [It's possible to compose pipelines with other pipelines?](#its-possible-to-compose-pipelines-with-other-pipelines)
|
20
21
|
- [Development](#development)
|
21
22
|
- [Contributing](#contributing)
|
22
23
|
- [License](#license)
|
@@ -64,6 +65,7 @@ end
|
|
64
65
|
#====================#
|
65
66
|
|
66
67
|
result = Multiply.call(a: 2, b: 2)
|
68
|
+
|
67
69
|
p result.success? # true
|
68
70
|
p result.value # 4
|
69
71
|
|
@@ -76,6 +78,7 @@ p result.value # 4
|
|
76
78
|
#----------------------------#
|
77
79
|
|
78
80
|
result = Multiply.new(a: 2, b: 3).call
|
81
|
+
|
79
82
|
p result.success? # true
|
80
83
|
p result.value # 6
|
81
84
|
|
@@ -84,12 +87,13 @@ p result.value # 6
|
|
84
87
|
#===========================#
|
85
88
|
|
86
89
|
result = Multiply.call(a: '2', b: 2)
|
90
|
+
|
87
91
|
p result.success? # false
|
88
92
|
p result.failure? # true
|
89
93
|
p result.value # :invalid_data
|
90
94
|
```
|
91
95
|
|
92
|
-
### How to use the
|
96
|
+
### How to use the result hooks?
|
93
97
|
|
94
98
|
```ruby
|
95
99
|
class Double < Micro::Service::Base
|
@@ -135,13 +139,13 @@ Double
|
|
135
139
|
```ruby
|
136
140
|
module Steps
|
137
141
|
class ConvertToNumbers < Micro::Service::Base
|
138
|
-
attribute :
|
142
|
+
attribute :numbers
|
139
143
|
|
140
144
|
def call!
|
141
|
-
if
|
142
|
-
Success(numbers:
|
145
|
+
if numbers.all? { |value| String(value) =~ /\d+/ }
|
146
|
+
Success(numbers: numbers.map(&:to_i))
|
143
147
|
else
|
144
|
-
Failure('
|
148
|
+
Failure('numbers must contain only numeric types')
|
145
149
|
end
|
146
150
|
end
|
147
151
|
end
|
@@ -150,7 +154,7 @@ module Steps
|
|
150
154
|
attribute :numbers
|
151
155
|
|
152
156
|
def call!
|
153
|
-
Success(numbers.map { |number| number + 2 })
|
157
|
+
Success(numbers: numbers.map { |number| number + 2 })
|
154
158
|
end
|
155
159
|
end
|
156
160
|
|
@@ -158,17 +162,36 @@ module Steps
|
|
158
162
|
attribute :numbers
|
159
163
|
|
160
164
|
def call!
|
161
|
-
Success(numbers.map { |number| number * 2 })
|
165
|
+
Success(numbers: numbers.map { |number| number * 2 })
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Square < Micro::Service::Strict
|
170
|
+
attribute :numbers
|
171
|
+
|
172
|
+
def call!
|
173
|
+
Success(numbers: numbers.map { |number| number * number })
|
162
174
|
end
|
163
175
|
end
|
164
176
|
end
|
165
177
|
|
178
|
+
#=================================================#
|
179
|
+
# Creating a pipeline using the collection syntax #
|
180
|
+
#=================================================#
|
181
|
+
|
166
182
|
Add2ToAllNumbers = Micro::Service::Pipeline[
|
167
183
|
Steps::ConvertToNumbers,
|
168
184
|
Steps::Add2
|
169
185
|
]
|
170
186
|
|
171
|
-
|
187
|
+
result = Add2ToAllNumbers.call(numbers: %w[1 1 2 2 3 4])
|
188
|
+
|
189
|
+
p result.success? # true
|
190
|
+
p result.value # {:numbers => [3, 3, 4, 4, 5, 6]}
|
191
|
+
|
192
|
+
#=======================================================#
|
193
|
+
# An alternative way to create a pipeline using classes #
|
194
|
+
#=======================================================#
|
172
195
|
|
173
196
|
class DoubleAllNumbers
|
174
197
|
include Micro::Service::Pipeline
|
@@ -176,14 +199,20 @@ class DoubleAllNumbers
|
|
176
199
|
pipeline Steps::ConvertToNumbers, Steps::Double
|
177
200
|
end
|
178
201
|
|
179
|
-
|
202
|
+
DoubleAllNumbers
|
203
|
+
.call(numbers: %w[1 1 b 2 3 4])
|
204
|
+
.on_failure { |message| p message } # "numbers must contain only numeric types"
|
180
205
|
|
181
|
-
|
182
|
-
|
206
|
+
#=================================================================#
|
207
|
+
# Another way to create a pipeline using the composition operator #
|
208
|
+
#=================================================================#
|
183
209
|
|
184
|
-
|
185
|
-
|
186
|
-
|
210
|
+
SquareAllNumbers =
|
211
|
+
Steps::ConvertToNumbers >> Steps::Square
|
212
|
+
|
213
|
+
SquareAllNumbers
|
214
|
+
.call(numbers: %w[1 1 2 2 3 4])
|
215
|
+
.on_success { |value| p value[:numbers] } # [1, 1, 4, 4, 9, 16]
|
187
216
|
```
|
188
217
|
|
189
218
|
### What is a strict Service Object?
|
@@ -249,6 +278,68 @@ end
|
|
249
278
|
# Use Micro::Service::Strict::Validation if do you want this behavior.
|
250
279
|
```
|
251
280
|
|
281
|
+
### It's possible to compose pipelines with other pipelines?
|
282
|
+
|
283
|
+
Answer: Yes
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
module Steps
|
287
|
+
class ConvertToNumbers < Micro::Service::Base
|
288
|
+
attribute :numbers
|
289
|
+
|
290
|
+
def call!
|
291
|
+
if numbers.all? { |value| String(value) =~ /\d+/ }
|
292
|
+
Success(numbers: numbers.map(&:to_i))
|
293
|
+
else
|
294
|
+
Failure('numbers must contain only numeric types')
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class Add2 < Micro::Service::Strict
|
300
|
+
attribute :numbers
|
301
|
+
|
302
|
+
def call!
|
303
|
+
Success(numbers: numbers.map { |number| number + 2 })
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
class Double < Micro::Service::Strict
|
308
|
+
attribute :numbers
|
309
|
+
|
310
|
+
def call!
|
311
|
+
Success(numbers: numbers.map { |number| number * 2 })
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class Square < Micro::Service::Strict
|
316
|
+
attribute :numbers
|
317
|
+
|
318
|
+
def call!
|
319
|
+
Success(numbers: numbers.map { |number| number * number })
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
Add2ToAllNumbers = Steps::ConvertToNumbers >> Steps::Add2
|
325
|
+
DoubleAllNumbers = Steps::ConvertToNumbers >> Steps::Double
|
326
|
+
SquareAllNumbers = Steps::ConvertToNumbers >> Steps::Square
|
327
|
+
DoubleAllNumbersAndAdd2 = DoubleAllNumbers >> Steps::Add2
|
328
|
+
SquareAllNumbersAndAdd2 = SquareAllNumbers >> Steps::Add2
|
329
|
+
DoubleAllNumbersAndSquareThem = DoubleAllNumbers >> SquareAllNumbersAndAdd2
|
330
|
+
SquareAllNumbersAndDoubleThem = SquareAllNumbersAndAdd2 >> DoubleAllNumbers
|
331
|
+
|
332
|
+
DoubleAllNumbersAndSquareThem
|
333
|
+
.call(numbers: %w[1 1 2 2 3 4])
|
334
|
+
.on_success { |value| p value[:numbers] } # [6, 6, 18, 18, 38, 66]
|
335
|
+
|
336
|
+
SquareAllNumbersAndDoubleThem
|
337
|
+
.call(numbers: %w[1 1 2 2 3 4])
|
338
|
+
.on_success { |value| p value[:numbers] } # [6, 6, 12, 12, 22, 36]
|
339
|
+
```
|
340
|
+
|
341
|
+
Note: You can blend any of the [syntaxes/approaches to create the pipelines](#how-to-create-a-pipeline-of-service-objects)) - [examples](https://github.com/serradura/u-service/blob/master/test/micro/service/pipeline/blend_test.rb#L7-L34).
|
342
|
+
|
252
343
|
## Development
|
253
344
|
|
254
345
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/micro/service/base.rb
CHANGED
@@ -4,11 +4,19 @@ module Micro
|
|
4
4
|
module Service
|
5
5
|
module Pipeline
|
6
6
|
class Reducer
|
7
|
+
attr_reader :services
|
8
|
+
|
7
9
|
INVALID_SERVICES =
|
8
10
|
'argument must be a collection of `Micro::Service::Base` classes'.freeze
|
9
11
|
|
12
|
+
def self.map_services(arg)
|
13
|
+
return arg.services if arg.is_a?(Reducer)
|
14
|
+
return arg.__pipeline__.services if arg.is_a?(Class) && arg < Micro::Service::Pipeline
|
15
|
+
Array(arg)
|
16
|
+
end
|
17
|
+
|
10
18
|
def self.build(args)
|
11
|
-
services = Array(args)
|
19
|
+
services = Array(args).flat_map { |arg| map_services(arg) }
|
12
20
|
|
13
21
|
raise ArgumentError, INVALID_SERVICES if services.any? { |klass| !(klass < ::Micro::Service::Base) }
|
14
22
|
|
@@ -26,23 +34,26 @@ module Micro
|
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
37
|
+
def >>(arg)
|
38
|
+
Reducer.build(services + self.class.map_services(arg))
|
39
|
+
end
|
40
|
+
|
29
41
|
private
|
30
42
|
|
31
43
|
def initial_result(arg)
|
32
44
|
return arg if arg.is_a?(Micro::Service::Result)
|
45
|
+
|
33
46
|
Micro::Service::Result::Success[value: arg]
|
34
47
|
end
|
35
48
|
end
|
36
49
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def pipeline(*args)
|
41
|
-
@pipeline = Reducer.build(args)
|
50
|
+
module ClassMethods
|
51
|
+
def __pipeline__
|
52
|
+
@__pipeline
|
42
53
|
end
|
43
54
|
|
44
|
-
def
|
45
|
-
@
|
55
|
+
def pipeline(*args)
|
56
|
+
@__pipeline = Reducer.build(args)
|
46
57
|
end
|
47
58
|
|
48
59
|
def call(options={})
|
@@ -50,19 +61,19 @@ module Micro
|
|
50
61
|
end
|
51
62
|
end
|
52
63
|
|
53
|
-
private_constant :
|
64
|
+
private_constant :ClassMethods
|
54
65
|
|
55
66
|
def self.[](*args)
|
56
67
|
Reducer.build(args)
|
57
68
|
end
|
58
69
|
|
59
70
|
def self.included(base)
|
60
|
-
base.extend(
|
71
|
+
base.extend(ClassMethods)
|
61
72
|
base.class_eval('def initialize(options); @options = options; end')
|
62
73
|
end
|
63
74
|
|
64
75
|
def call
|
65
|
-
self.class.
|
76
|
+
self.class.__pipeline__.call(@options)
|
66
77
|
end
|
67
78
|
end
|
68
79
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: u-service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Serradura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: u-attributes
|