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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0a881f3a6544fe98f2384ee40522a44e1fcc2011f60fd1e312d1fbb4afee79f
4
- data.tar.gz: 425f39a6977091d5e9303b9fc86f1c261115737bffc693f929f8cb0dafce43bb
3
+ metadata.gz: a21e14f14ad8d4b658660c94ed2e3687c4b589531a61f11fee63c9fcc0810615
4
+ data.tar.gz: e504198f717114b9fd6794b795381ebfced5a6e29bda56ad83cc1f055296efc6
5
5
  SHA512:
6
- metadata.gz: cf65966cfd4ef161f07ff5e97e787afd2afe810420223941a574e1eeb0a236b98f0d83a4c391b374ac53391afaef7f2e23a4abbecf7a0b01adf89b125a3ea08a
7
- data.tar.gz: 2c88814614909b674a64ec4c727c5ecaeb2c42e83fae514ee044c085aa0753862529774d872eace7e2106461366d46a9521c94348f8931ce47fa10ab0b6cca4b
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- u-service (0.6.0)
4
+ u-service (0.7.0)
5
5
  u-attributes (~> 1.1)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -6,9 +6,25 @@
6
6
  μ-service (Micro::Service)
7
7
  ==========================
8
8
 
9
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/micro/service`. To experiment with that code, run `bin/console` for an interactive prompt.
9
+ Create simple and powerful service objects.
10
10
 
11
- TODO: Delete this and the text above, and describe your gem
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
- TODO: Write usage instructions here
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
- class Pipeline
6
- INVALID_COLLECTION =
7
- 'argument must be a collection of `Micro::Service::Base` classes'.freeze
5
+ module Pipeline
6
+ class Reducer
7
+ def initialize(services)
8
+ @services = services
9
+ end
8
10
 
9
- def self.[](*services)
10
- new(services)
11
- end
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
- def initialize(services)
14
- @services = validate!(services)
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
- def call(arg={})
18
- @services.reduce(initial_result(arg)) do |result, service|
19
- break result if result.failure?
20
- service.call(result.value)
21
- end
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
- private
35
+ def self.new(args)
36
+ services = Array(args)
25
37
 
26
- def validate!(services)
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
- def initial_result(arg)
35
- return arg if arg.is_a?(Micro::Service::Result)
36
- Micro::Service::Result::Success(value: arg)
37
- end
40
+ Reducer.new(services)
41
+ end
38
42
  end
39
43
  end
40
44
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Service
5
- VERSION = '0.6.0'.freeze
5
+ VERSION = '0.7.0'.freeze
6
6
  end
7
7
  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.6.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