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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '03877b6389a946be9418d0d4f41d8efe5a67811920bcdaacbb7467cc31d86275'
4
- data.tar.gz: d560985874aa56cfb429fcca22520c37ff6acc5ae74a5a1b8f6aa682e84c2084
3
+ metadata.gz: b9aa9c4a076ce6c0f78c14a4084200eef94d7e04feda739ae80a715d83b17d29
4
+ data.tar.gz: 4d52dc751a9c0da7dfbca46bae74b00286422835c201e1ee27dbfb7b467fa010
5
5
  SHA512:
6
- metadata.gz: 40681884ea919c82a08cf7e454582f5f0135567efd2d80f63bff464b7d11aab9ae523a25432dd557433d8b58c6a4e9f3493e47e8d1f791d0849976060b3cc438
7
- data.tar.gz: 79a7ed2f656c80a39d554a09777c2affba64e3897b606cf666e5a607912113464bd95d3b94cc516c450f03f1bf02868363a4f9b4f9332127b9b329b8b4995dc2
6
+ metadata.gz: 207f7141d5e7dcc5063d0ddbad53f51bb3c6991e287b2dac25de21c77e8c2386b6200b62f0eea819a22dd42b3fd86e0f7f37c62f8cf99e93080f9223451c566f
7
+ data.tar.gz: e8a887fd59c3663aeea410b8ca840aa1d507a3ff239edb0ac8909932c7776e68a13e705ab9b8262b84a5c812fdf0e1e97c016c30ae19e47f1e009476a41ca6ae
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- u-service (0.9.0)
4
+ u-service (0.10.0)
5
5
  u-attributes (~> 1.1)
6
6
 
7
7
  GEM
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 Service Object result hooks?](#how-to-use-the-service-object-result-hooks)
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 Service Object result hooks?
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 :relation
142
+ attribute :numbers
139
143
 
140
144
  def call!
141
- if relation.all? { |value| String(value) =~ /\d+/ }
142
- Success(numbers: relation.map(&:to_i))
145
+ if numbers.all? { |value| String(value) =~ /\d+/ }
146
+ Success(numbers: numbers.map(&:to_i))
143
147
  else
144
- Failure('relation must contain only numbers')
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
- # An alternative way to declare pipelines within classes.
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
- result = Add2ToAllNumbers.call(relation: %w[1 1 2 2 3 4])
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
- p result.success? # true
182
- p result.value # [3, 3, 4, 4, 5, 6]
206
+ #=================================================================#
207
+ # Another way to create a pipeline using the composition operator #
208
+ #=================================================================#
183
209
 
184
- DoubleAllNumbers
185
- .call(relation: %w[1 1 b 2 3 4])
186
- .on_failure { |message| p message } # "relation must contain only numbers"
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.
@@ -8,6 +8,10 @@ module Micro
8
8
 
9
9
  INVALID_RESULT = '#call! must return a Micro::Service::Result instance'.freeze
10
10
 
11
+ def self.>>(service)
12
+ Micro::Service::Pipeline[self, service]
13
+ end
14
+
11
15
  def self.call(options = {})
12
16
  new(options).call
13
17
  end
@@ -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
- private_constant :Reducer
38
-
39
- module Macros
40
- def pipeline(*args)
41
- @pipeline = Reducer.build(args)
50
+ module ClassMethods
51
+ def __pipeline__
52
+ @__pipeline
42
53
  end
43
54
 
44
- def pipeline_call(options)
45
- @pipeline.call(options)
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 :Macros
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(Macros)
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.pipeline_call(@options)
76
+ self.class.__pipeline__.call(@options)
66
77
  end
67
78
  end
68
79
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Service
5
- VERSION = '0.9.0'.freeze
5
+ VERSION = '0.10.0'.freeze
6
6
  end
7
7
  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.9.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-09 00:00:00.000000000 Z
11
+ date: 2019-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: u-attributes