u-service 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.tool-versions +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +215 -3
- data/lib/micro/service/pipeline.rb +29 -25
- data/lib/micro/service/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a21e14f14ad8d4b658660c94ed2e3687c4b589531a61f11fee63c9fcc0810615
|
4
|
+
data.tar.gz: e504198f717114b9fd6794b795381ebfced5a6e29bda56ad83cc1f055296efc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74d66f07259c0d31c1469d70bd9a89190fafce614bdc4adced7e2e8a4dba07859483f020d90a87850804485f44e468835d481263c027fe86e4bbb9eee8af15ff
|
7
|
+
data.tar.gz: c18b4b93cac117a7a5ea48d5bca5ef17811d62eedd18419a75d40ba6559b2518e5d7cb0c745dc38621034c0c8f802f247c602e4e6acb8ea1dee4b1365b5a25bc
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 2.6.3
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -6,9 +6,25 @@
|
|
6
6
|
μ-service (Micro::Service)
|
7
7
|
==========================
|
8
8
|
|
9
|
-
|
9
|
+
Create simple and powerful service objects.
|
10
10
|
|
11
|
-
|
11
|
+
- [μ-service (Micro::Service)](#%ce%bc-service-microservice)
|
12
|
+
- [Required Ruby version](#required-ruby-version)
|
13
|
+
- [Installation](#installation)
|
14
|
+
- [Usage](#usage)
|
15
|
+
- [How to create a basic Service Object?](#how-to-create-a-basic-service-object)
|
16
|
+
- [How to use the Service Object result hooks?](#how-to-use-the-service-object-result-hooks)
|
17
|
+
- [How to create a pipeline of Service Objects?](#how-to-create-a-pipeline-of-service-objects)
|
18
|
+
- [What is a strict Service Object?](#what-is-a-strict-service-object)
|
19
|
+
- [How to validate Service Object attributes?](#how-to-validate-service-object-attributes)
|
20
|
+
- [Development](#development)
|
21
|
+
- [Contributing](#contributing)
|
22
|
+
- [License](#license)
|
23
|
+
- [Code of Conduct](#code-of-conduct)
|
24
|
+
|
25
|
+
## Required Ruby version
|
26
|
+
|
27
|
+
> \>= 2.2.0
|
12
28
|
|
13
29
|
## Installation
|
14
30
|
|
@@ -28,7 +44,203 @@ Or install it yourself as:
|
|
28
44
|
|
29
45
|
## Usage
|
30
46
|
|
31
|
-
|
47
|
+
### How to create a basic Service Object?
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
class Multiply < Micro::Service::Base
|
51
|
+
attributes :a, :b
|
52
|
+
|
53
|
+
def call!
|
54
|
+
if a.is_a?(Numeric) && b.is_a?(Numeric)
|
55
|
+
Success(a * b)
|
56
|
+
else
|
57
|
+
Failure(:invalid_data)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#====================#
|
63
|
+
# Calling a service #
|
64
|
+
#====================#
|
65
|
+
|
66
|
+
result = Multiply.call(a: 2, b: 2)
|
67
|
+
p result.success? # true
|
68
|
+
p result.value # 4
|
69
|
+
|
70
|
+
#----------------------------#
|
71
|
+
# Calling a service instance #
|
72
|
+
#----------------------------#
|
73
|
+
|
74
|
+
result = Multiply.new(a: 2, b: 3).call
|
75
|
+
p result.success? # true
|
76
|
+
p result.value # 6
|
77
|
+
|
78
|
+
#===========================#
|
79
|
+
# Verify the result failure #
|
80
|
+
#===========================#
|
81
|
+
|
82
|
+
result = Multiply.call(a: '2', b: 2)
|
83
|
+
p result.success? # false
|
84
|
+
p result.failure? # true
|
85
|
+
p result.value # :invalid_data
|
86
|
+
```
|
87
|
+
|
88
|
+
### How to use the Service Object result hooks?
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
class Double < Micro::Service::Base
|
92
|
+
attributes :number
|
93
|
+
|
94
|
+
def call!
|
95
|
+
return Failure(:invalid) { 'the number must be a numeric value' } unless number.is_a?(Numeric)
|
96
|
+
return Failure(:lte_zero) { 'the number must be greater than 0' } if number <= 0
|
97
|
+
|
98
|
+
Success(number * number)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
#================================#
|
103
|
+
# Printing the output if success #
|
104
|
+
#================================#
|
105
|
+
|
106
|
+
Double
|
107
|
+
.call(number: 3)
|
108
|
+
.on_success { |number| p number }
|
109
|
+
.on_failure(:invalid) { |msg| raise TypeError, msg }
|
110
|
+
.on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
|
111
|
+
|
112
|
+
# The output when is a success:
|
113
|
+
# 9
|
114
|
+
|
115
|
+
#=============================#
|
116
|
+
# Raising an error if failure #
|
117
|
+
#=============================#
|
118
|
+
|
119
|
+
Double
|
120
|
+
.call(number: -1)
|
121
|
+
.on_success { |number| p number }
|
122
|
+
.on_failure(:invalid) { |msg| raise TypeError, msg }
|
123
|
+
.on_failure(:lte_zero) { |msg| raise ArgumentError, msg }
|
124
|
+
|
125
|
+
# The output (raised an error) when is a failure:
|
126
|
+
# ArgumentError (the number must be greater than 0)
|
127
|
+
```
|
128
|
+
|
129
|
+
### How to create a pipeline of Service Objects?
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
module Steps
|
133
|
+
class ConvertToNumbers < Micro::Service::Base
|
134
|
+
attribute :relation
|
135
|
+
|
136
|
+
def call!
|
137
|
+
if relation.all? { |value| String(value) =~ /\d+/ }
|
138
|
+
Success(numbers: relation.map(&:to_i))
|
139
|
+
else
|
140
|
+
Failure('relation must contain only numbers')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Add2 < Micro::Service::Strict
|
146
|
+
attribute :numbers
|
147
|
+
|
148
|
+
def call!
|
149
|
+
Success(numbers.map { |number| number + 2 })
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Double < Micro::Service::Strict
|
154
|
+
attribute :numbers
|
155
|
+
|
156
|
+
def call!
|
157
|
+
Success(numbers.map { |number| number * number })
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
Add2ToAllNumbers = Micro::Service::Pipeline[
|
163
|
+
Steps::ConvertToNumbers,
|
164
|
+
Steps::Add2
|
165
|
+
]
|
166
|
+
|
167
|
+
DoubleAllNumbers = Micro::Service::Pipeline[
|
168
|
+
Steps::ConvertToNumbers,
|
169
|
+
Steps::Double
|
170
|
+
]
|
171
|
+
|
172
|
+
result = Add2ToAllNumbers.call(relation: %w[1 1 2 2 3 4])
|
173
|
+
|
174
|
+
p result.success? # true
|
175
|
+
p result.value # [3, 3, 4, 4, 5, 6]
|
176
|
+
|
177
|
+
DoubleAllNumbers
|
178
|
+
.call(relation: %w[1 1 b 2 3 4])
|
179
|
+
.on_failure { |message| p message } # "relation must contain only numbers"
|
180
|
+
```
|
181
|
+
|
182
|
+
### What is a strict Service Object?
|
183
|
+
|
184
|
+
A: Is a service object which will require all keywords (attributes) on its initialization.
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
class Double < Micro::Service::Strict
|
188
|
+
attribute :numbers
|
189
|
+
|
190
|
+
def call!
|
191
|
+
Success(numbers.map { |number| number * number })
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
Double.call({})
|
196
|
+
|
197
|
+
# The output (raised an error):
|
198
|
+
# ArgumentError (missing keyword: :numbers)
|
199
|
+
```
|
200
|
+
|
201
|
+
### How to validate Service Object attributes?
|
202
|
+
|
203
|
+
Note: To do this your application must have the (activemodel >= 3.2)(https://rubygems.org/gems/activemodel) as a dependency.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
#
|
207
|
+
# By default, if your project has the activemodel
|
208
|
+
# any kind of service attribute can be validated.
|
209
|
+
#
|
210
|
+
class Multiply < Micro::Service::Base
|
211
|
+
attribute :a
|
212
|
+
attribute :b
|
213
|
+
validates :a, :b, presence: true, numericality: true
|
214
|
+
|
215
|
+
def call!
|
216
|
+
return Success(number: a * b) if valid?
|
217
|
+
|
218
|
+
Failure(errors: self.errors)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
#
|
223
|
+
# But if do you want an automatic way to fail
|
224
|
+
# your services if there is some invalid data.
|
225
|
+
# You can use:
|
226
|
+
require 'micro/service/with_validation'
|
227
|
+
|
228
|
+
# Using this approach, you can rewrite the previous sample with fewer lines of code.
|
229
|
+
|
230
|
+
class Multiply < Micro::Service::WithValidation
|
231
|
+
attribute :a
|
232
|
+
attribute :b
|
233
|
+
validates :a, :b, presence: true, numericality: true
|
234
|
+
|
235
|
+
def call!
|
236
|
+
Success(number: a * b)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Note:
|
241
|
+
# There is a strict variation for Micro::Service::WithValidation
|
242
|
+
# Use Micro::Service::Strict::Validation if do you want this behavior.
|
243
|
+
```
|
32
244
|
|
33
245
|
## Development
|
34
246
|
|
@@ -2,39 +2,43 @@
|
|
2
2
|
|
3
3
|
module Micro
|
4
4
|
module Service
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
module Pipeline
|
6
|
+
class Reducer
|
7
|
+
def initialize(services)
|
8
|
+
@services = services
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
def call(arg={})
|
12
|
+
@services.reduce(initial_result(arg)) do |result, service|
|
13
|
+
break result if result.failure?
|
14
|
+
service.call(result.value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
12
19
|
|
13
|
-
|
14
|
-
|
20
|
+
def initial_result(arg)
|
21
|
+
return arg if arg.is_a?(Micro::Service::Result)
|
22
|
+
Micro::Service::Result::Success(value: arg)
|
23
|
+
end
|
15
24
|
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
26
|
+
private_constant :Reducer
|
27
|
+
|
28
|
+
INVALID_SERVICES =
|
29
|
+
'argument must be a collection of `Micro::Service::Base` classes'.freeze
|
30
|
+
|
31
|
+
def self.[](*args)
|
32
|
+
self.new(args)
|
22
33
|
end
|
23
34
|
|
24
|
-
|
35
|
+
def self.new(args)
|
36
|
+
services = Array(args)
|
25
37
|
|
26
|
-
|
27
|
-
Array(services).tap do |collection|
|
28
|
-
if collection.any? { |klass| !(klass < ::Micro::Service::Base) }
|
29
|
-
raise ArgumentError, INVALID_COLLECTION
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
38
|
+
raise ArgumentError, INVALID_SERVICES if services.any? { |klass| !(klass < ::Micro::Service::Base) }
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
Micro::Service::Result::Success(value: arg)
|
37
|
-
end
|
40
|
+
Reducer.new(services)
|
41
|
+
end
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: u-service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Serradura
|
@@ -74,6 +74,7 @@ extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
76
|
- ".gitignore"
|
77
|
+
- ".tool-versions"
|
77
78
|
- ".travis.sh"
|
78
79
|
- ".travis.yml"
|
79
80
|
- CODE_OF_CONDUCT.md
|