u-service 0.6.0 → 0.7.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/.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
|