sea_food 0.1.1 → 0.2.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/.rubocop.yml +8 -3
- data/README.md +191 -0
- data/lib/sea_food/service.rb +15 -3
- data/lib/sea_food/version.rb +1 -1
- data/sea_food.gemspec +2 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09caa9ace45be8850e8639249d6838094845be06718b0c70e384873a35612020'
|
4
|
+
data.tar.gz: 4b77831dc3d9decd91d67f735baea6bd3cfb27c0519d581d1e6988f6d8ff0194
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 710db87b3d97c5cf54c3ad05599efd80a499a6d3610212a8c7289fa67d678eaf6213cf02f49801bf3fe86fb8713d2fed2d73d3a9b4b9eb6c8baa6d39b521c6b4
|
7
|
+
data.tar.gz: 49c68b7d88ef0526fa7f9762e0b5f264dd135a174287993b73e31e412dfc8ee225623c1c11bd4c966f50bb446459f83a604f31c46839c76368df51bff7b7ff83
|
data/.rubocop.yml
CHANGED
@@ -42,7 +42,7 @@ Metrics/BlockLength:
|
|
42
42
|
- 'sea_food.gemspec'
|
43
43
|
- config/**/*
|
44
44
|
- spec/**/*
|
45
|
-
|
45
|
+
AllowedMethods:
|
46
46
|
- class_methods
|
47
47
|
|
48
48
|
Metrics/BlockNesting:
|
@@ -97,9 +97,14 @@ Style/MissingRespondToMissing:
|
|
97
97
|
Exclude:
|
98
98
|
- 'lib/sea_food/service.rb'
|
99
99
|
|
100
|
-
|
100
|
+
Lint/MissingSuper:
|
101
101
|
Exclude:
|
102
102
|
- 'lib/sea_food/service.rb'
|
103
|
+
- spec/**/*
|
103
104
|
|
104
105
|
Naming/PredicateName:
|
105
|
-
Enabled: false
|
106
|
+
Enabled: false
|
107
|
+
|
108
|
+
Lint/ConstantDefinitionInBlock:
|
109
|
+
Exclude:
|
110
|
+
- 'spec/**/*'
|
data/README.md
CHANGED
@@ -53,6 +53,197 @@ end
|
|
53
53
|
```
|
54
54
|
It will raise an `ArgumentError`.
|
55
55
|
|
56
|
+
|
57
|
+
### Handling Success and Failure
|
58
|
+
#### Using success and fail
|
59
|
+
`success(data):` Marks the service result as successful and optionally provides data.
|
60
|
+
`fail(data):` Marks the service result as a failure but continues executing the call method.
|
61
|
+
`fail!(data):` Marks the service result as a failure and immediately exits the call method.
|
62
|
+
#### Example: Using fail followed by success
|
63
|
+
```ruby
|
64
|
+
class TestFailService < SeaFood::Service
|
65
|
+
def initialize(email:)
|
66
|
+
@email = email
|
67
|
+
end
|
68
|
+
|
69
|
+
def call
|
70
|
+
fail(email: 'hi@example.com')
|
71
|
+
success(email: @email)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
result = TestFailService.call(email: 'service@example.com')
|
76
|
+
|
77
|
+
puts result.success? # => true
|
78
|
+
puts result.email # => 'service@example.com'
|
79
|
+
```
|
80
|
+
In this example:
|
81
|
+
|
82
|
+
The fail method sets the result to failure but allows the method to continue.
|
83
|
+
The subsequent success call overrides the failure, resulting in a successful outcome.
|
84
|
+
|
85
|
+
#### Example: Using fail! to Exit Early
|
86
|
+
```ruby
|
87
|
+
class TestFailBangService < SeaFood::Service
|
88
|
+
def initialize(email:)
|
89
|
+
@email = email
|
90
|
+
end
|
91
|
+
|
92
|
+
def call
|
93
|
+
fail!(email: 'hi@example.com')
|
94
|
+
success(email: @email) # This line is not executed
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
result = TestFailBangService.call(email: 'service@example.com')
|
99
|
+
|
100
|
+
puts result.success? # => false
|
101
|
+
puts result.email # => 'hi@example.com'
|
102
|
+
```
|
103
|
+
|
104
|
+
The `fail!` method immediately exits the call method.
|
105
|
+
The service result is a failure, and subsequent code in call is not executed.
|
106
|
+
Nested Services
|
107
|
+
You can call other services within a service. The behavior depends on whether you use `call` or `call!`.
|
108
|
+
#### Summary of call vs. call!
|
109
|
+
`call`: Executes the service and returns the result. Does not raise an exception if the service fails.
|
110
|
+
|
111
|
+
`call!`: Executes the service and raises an exception if the service fails. Useful for propagating failures in nested services.
|
112
|
+
|
113
|
+
#### Using call (Non-Bang Method)
|
114
|
+
Failures in nested services do not automatically propagate to the parent service.
|
115
|
+
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
class InnerService < SeaFood::Service
|
119
|
+
def initialize(email:)
|
120
|
+
@email = email
|
121
|
+
end
|
122
|
+
|
123
|
+
def call
|
124
|
+
fail!(email: @email)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class OuterService < SeaFood::Service
|
129
|
+
def initialize(email:)
|
130
|
+
@email = email
|
131
|
+
end
|
132
|
+
|
133
|
+
def call
|
134
|
+
InnerService.call(email: 'inner@example.com')
|
135
|
+
success(email: @email)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
result = OuterService.call(email: 'outer@example.com')
|
140
|
+
|
141
|
+
puts result.success? # => true
|
142
|
+
puts result.email # => 'outer@example.com'
|
143
|
+
```
|
144
|
+
*Explanation:*
|
145
|
+
|
146
|
+
`InnerService` fails using `fail!`.
|
147
|
+
`OuterService` calls `InnerService` using `call`.
|
148
|
+
Since `call` does not raise an exception on failure, `OuterService` continues and succeeds.
|
149
|
+
|
150
|
+
#### Using `call!` (Bang Method)
|
151
|
+
Failures in nested services propagate to the parent service when using `call!`.
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
class InnerService < SeaFood::Service
|
155
|
+
def initialize(email:)
|
156
|
+
@email = email
|
157
|
+
end
|
158
|
+
|
159
|
+
def call
|
160
|
+
fail!(email: @email)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class OuterService < SeaFood::Service
|
165
|
+
def initialize(email:)
|
166
|
+
@email = email
|
167
|
+
end
|
168
|
+
|
169
|
+
def call
|
170
|
+
InnerService.call!(email: 'inner@example.com')
|
171
|
+
success(email: @email) # This line is not executed
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
result = OuterService.call(email: 'outer@example.com')
|
176
|
+
|
177
|
+
puts result.success? # => false
|
178
|
+
puts result.email # => 'inner@example.com'
|
179
|
+
```
|
180
|
+
*Explanation:*
|
181
|
+
|
182
|
+
`InnerService` fails using `fail!`.
|
183
|
+
`OuterService` calls `InnerService` using call!.
|
184
|
+
The failure from `InnerService` propagates, causing `OuterService` to fail.
|
185
|
+
#### Handling Failures in Nested Services
|
186
|
+
You can handle failures from nested services by checking their result.
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
class InnerService < SeaFood::Service
|
190
|
+
def initialize(value:)
|
191
|
+
@value = value
|
192
|
+
end
|
193
|
+
|
194
|
+
def call
|
195
|
+
if @value < 0
|
196
|
+
fail!(error: 'Negative value')
|
197
|
+
else
|
198
|
+
success(value: @value)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class OuterService < SeaFood::Service
|
204
|
+
def initialize(value:)
|
205
|
+
@value = value
|
206
|
+
end
|
207
|
+
|
208
|
+
def call
|
209
|
+
result = InnerService.call(value: @value)
|
210
|
+
|
211
|
+
if result.fail?
|
212
|
+
fail!(error: result.error)
|
213
|
+
else
|
214
|
+
success(value: result.value * 2)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
result = OuterService.call(value: -1)
|
220
|
+
|
221
|
+
puts result.success? # => false
|
222
|
+
puts result.error # => 'Negative value'
|
223
|
+
```
|
224
|
+
*Explanation:*
|
225
|
+
|
226
|
+
`OuterService` checks the result of `InnerService`.
|
227
|
+
If `InnerService` fails, `OuterService` handles it accordingly.
|
228
|
+
|
229
|
+
#### Accessing Result Data
|
230
|
+
The result object allows you to access data provided in success or fail calls using method syntax.
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
class ExampleService < SeaFood::Service
|
234
|
+
def call
|
235
|
+
success(message: 'Operation successful', value: 42)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
result = ExampleService.call
|
240
|
+
|
241
|
+
puts result.success? # => true
|
242
|
+
puts result.message # => 'Operation successful'
|
243
|
+
puts result.value # => 42
|
244
|
+
```
|
245
|
+
|
246
|
+
|
56
247
|
## Development
|
57
248
|
|
58
249
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/sea_food/service.rb
CHANGED
@@ -21,12 +21,18 @@ module SeaFood
|
|
21
21
|
# @param args [Hash] Arguments to pass to the service.
|
22
22
|
# @return [ServiceResult] The result of the service call.
|
23
23
|
def call(params = {})
|
24
|
-
# debugger
|
25
24
|
service = new(**params)
|
26
25
|
service.call
|
27
26
|
service.result || ServiceResult.new
|
28
27
|
rescue ServiceError => e
|
29
|
-
service.result
|
28
|
+
service.result || e.try(:result)
|
29
|
+
end
|
30
|
+
|
31
|
+
def call!(params = {})
|
32
|
+
result = call(params)
|
33
|
+
return result || ServiceResult.new unless result.fail?
|
34
|
+
|
35
|
+
raise ServiceError, result
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
@@ -113,7 +119,13 @@ module SeaFood
|
|
113
119
|
end
|
114
120
|
end
|
115
121
|
|
116
|
-
class ServiceError < StandardError
|
122
|
+
class ServiceError < StandardError
|
123
|
+
attr_reader :result
|
124
|
+
|
125
|
+
def initialize(result)
|
126
|
+
@result = result
|
127
|
+
end
|
128
|
+
end
|
117
129
|
|
118
130
|
private
|
119
131
|
|
data/lib/sea_food/version.rb
CHANGED
data/sea_food.gemspec
CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.summary = 'A Ruby gem for seamlessly integrating form and service object patterns.'
|
13
13
|
spec.homepage = 'https://github.com/eagerworks/sea_food'
|
14
14
|
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
|
15
16
|
|
16
17
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
18
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
@@ -43,6 +44,6 @@ Gem::Specification.new do |spec|
|
|
43
44
|
spec.add_development_dependency 'debug'
|
44
45
|
spec.add_development_dependency 'rake'
|
45
46
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
46
|
-
spec.add_development_dependency 'rubocop', '~>
|
47
|
+
spec.add_development_dependency 'rubocop', '~> 1.0'
|
47
48
|
spec.add_development_dependency 'sqlite3', '~> 1.5.0'
|
48
49
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sea_food
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Federico Aldunate
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: '1.0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: '1.0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: sqlite3
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
192
|
requirements:
|
193
193
|
- - ">="
|
194
194
|
- !ruby/object:Gem::Version
|
195
|
-
version:
|
195
|
+
version: 2.7.0
|
196
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
197
|
requirements:
|
198
198
|
- - ">="
|