simple_ruby_service 1.0.2 → 1.0.3
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/CHANGELOG.md +1 -1
- data/README.md +67 -35
- data/lib/simple_ruby_service/version.rb +1 -1
- data/simple_ruby_service.gemspec +2 -2
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e42ccf4f74e452f3da1dddc21eafd66c9c20a84386bfd2e0434b19d97393db46
|
4
|
+
data.tar.gz: 92f5a6025b72f92144896272378d308e223474103dab3abfee7304eb11432fa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a983c65b09b6219e4ce3636e53837affde6667c3f5ffa888bc66742c7c7533590ba702515bb8340e326d5d9558114c22aada7d0d925645b8a8d616b7bb377e9
|
7
|
+
data.tar.gz: 18da46473f59e5c8ff97a36dea127fafd48cb71686d9d3a2f29deee80dc1843304d29b8c5ec10d6d09dce3d5fa6df3142f90bd472860f77579dccb0f4d150008
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -3,13 +3,13 @@
|
|
3
3
|
[](https://travis-ci.com/amazing-jay/simple_ruby_service)
|
4
4
|
[](https://codecov.io/gh/amazing-jay/simple_ruby_service)
|
5
5
|
|
6
|
-
Simple Ruby Service is a lightweight framework for
|
6
|
+
Simple Ruby Service is a lightweight framework for creating Services and Service Objects (SOs) in Ruby.
|
7
7
|
|
8
8
|
The framework provides a simple DSL that:
|
9
9
|
|
10
10
|
1. Incorporates ActiveModel validations and error handling
|
11
11
|
2. Encourages a succinct, idiomatic coding style
|
12
|
-
3.
|
12
|
+
3. Allows Service Objects to ducktype as Procs
|
13
13
|
|
14
14
|
## Requirements
|
15
15
|
|
@@ -37,10 +37,12 @@ Source code can be downloaded on GitHub
|
|
37
37
|
[github.com/amazing-jay/simple_ruby_service/tree/master](https://github.com/amazing-jay/simple_ruby_service/tree/master)
|
38
38
|
|
39
39
|
|
40
|
-
|
40
|
+
## Quick Start
|
41
41
|
|
42
42
|
See [Usage](https://github.com/amazing-jay/simple_ruby_service#usage) & [Creating Simple Ruby Services](https://github.com/amazing-jay/simple_ruby_service#creating-simple-ruby-services) for more information.
|
43
43
|
|
44
|
+
### How to refactor complex business logic with Simple Ruby Service
|
45
|
+
|
44
46
|
#### ::Before:: Vanilla Rails with a fat controller (a contrived example)
|
45
47
|
```ruby
|
46
48
|
# in app/controllers/some_controller.rb
|
@@ -51,6 +53,7 @@ class SomeController < ApplicationController
|
|
51
53
|
authorize! resource
|
52
54
|
resource.do_something
|
53
55
|
value = resource.do_something_related
|
56
|
+
raise unless resource.errors
|
54
57
|
render value
|
55
58
|
end
|
56
59
|
end
|
@@ -61,12 +64,39 @@ end
|
|
61
64
|
# in app/controllers/some_controller.rb
|
62
65
|
class SomeController < ApplicationController
|
63
66
|
def show
|
64
|
-
# NOTE:
|
65
|
-
render DoSomething.call(params)
|
67
|
+
# NOTE: Just one, readable line of code
|
68
|
+
render DoSomething.call!(params)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
#### ::Alternate After:: Refactored using a Service
|
74
|
+
```ruby
|
75
|
+
# in app/controllers/some_controller.rb
|
76
|
+
class SomeController < ApplicationController
|
77
|
+
def show
|
78
|
+
# NOTE: Simple Ruby Service methods can be chained together
|
79
|
+
render SomeService.new(params)
|
80
|
+
.do_something
|
81
|
+
.do_something_related
|
82
|
+
.value
|
66
83
|
end
|
67
84
|
end
|
85
|
+
```
|
68
86
|
|
87
|
+
### Taking a peek under the hood
|
69
88
|
|
89
|
+
Similar to `ActiveRecord::Base#save!`, `DoSomething.call!(params)`:
|
90
|
+
- creates an instance of `DoSomething`
|
91
|
+
- initializes `instance.attributes` with `params`
|
92
|
+
- raises `SimpleRubyService::Invalid` if `instance.invalid?`
|
93
|
+
- sends `instance.call`
|
94
|
+
- raises `SimpleRubyService::Failed` if `instance.failed?`
|
95
|
+
- returns `instance.value` directly to the caller
|
96
|
+
|
97
|
+
|
98
|
+
### Anatomy of a Simple Ruby Service Object
|
99
|
+
```ruby
|
70
100
|
# in app/service_objects/do_something.rb
|
71
101
|
class DoSomething
|
72
102
|
include SimpleRubyService::ServiceObject
|
@@ -74,33 +104,26 @@ class DoSomething
|
|
74
104
|
attribute :id
|
75
105
|
attr_accessor :resource
|
76
106
|
|
77
|
-
#
|
107
|
+
# Validations are executed prior to the business logic encapsulated in `perform`
|
78
108
|
validate do
|
79
109
|
@resource ||= SomeModel.find(id)
|
80
110
|
authorize! resource
|
81
111
|
end
|
82
112
|
|
83
|
-
#
|
113
|
+
# The result of `perform` is automatically stored as the SO's `value`
|
84
114
|
def perform
|
85
|
-
resource.do_something
|
86
|
-
resource.do_something_related
|
115
|
+
resource.do_something
|
116
|
+
result = resource.do_something_related
|
117
|
+
|
118
|
+
# Adding any kind of error indicates failure
|
119
|
+
add_errors_from_object resource
|
120
|
+
result
|
87
121
|
end
|
88
122
|
end
|
89
123
|
```
|
90
124
|
|
91
|
-
|
125
|
+
### Anatomy of a Simple Ruby Service
|
92
126
|
```ruby
|
93
|
-
# in app/controllers/some_controller.rb
|
94
|
-
class SomeController < ApplicationController
|
95
|
-
def show
|
96
|
-
# NOTE: Simple Ruby Service methods can be chained together
|
97
|
-
render SomeService.new(params)
|
98
|
-
.do_something
|
99
|
-
.do_something_related
|
100
|
-
.value
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
127
|
# in app/services/do_something.rb
|
105
128
|
class SomeService
|
106
129
|
include SimpleRubyService::Service
|
@@ -108,25 +131,38 @@ class SomeService
|
|
108
131
|
attribute :id
|
109
132
|
attr_accessor :resource
|
110
133
|
|
111
|
-
#
|
134
|
+
# Similar to SOs, validations are executed prior to the first service method called
|
112
135
|
validate do
|
113
136
|
@resource ||= SomeModel.find(id)
|
114
137
|
authorize! @resource
|
115
138
|
end
|
116
139
|
|
140
|
+
# Unlike SOs, Services can define an arbitrary number of service methods with arbitrary names
|
117
141
|
service_methods do
|
118
142
|
def do_something
|
119
|
-
resource.
|
143
|
+
resource.do_something
|
120
144
|
end
|
121
145
|
|
122
|
-
#
|
146
|
+
# Unlike SOs, `value` must be explicitely set for Service methods
|
123
147
|
def do_something_related
|
124
148
|
self.value ||= resource.tap &:do_something_related
|
149
|
+
add_errors_from_object resource
|
125
150
|
end
|
126
151
|
end
|
127
152
|
end
|
128
153
|
```
|
129
154
|
|
155
|
+
## A special note about Simple Ruby Service Objects, Procs, and Ducktyping
|
156
|
+
|
157
|
+
Simple Ruby Service Objects respond to (`#call`) so they can stand in for Procs, i.e.:
|
158
|
+
```ruby
|
159
|
+
# in app/models/some_model.rb
|
160
|
+
class SomeModel < ApplicationRecord
|
161
|
+
validates :some_attribute, if: SomeServiceObject
|
162
|
+
[...]
|
163
|
+
```
|
164
|
+
_See [To bang!, or not to bang](https://github.com/amazing-jay/simple_ruby_service/tree/master#to-bang-or-not-to-bang) to learn about `.call!` vs. `.call`._
|
165
|
+
|
130
166
|
## Usage
|
131
167
|
|
132
168
|
### Service Objects
|
@@ -137,8 +173,6 @@ Service Object names should begin with a verb and should not include the words `
|
|
137
173
|
|
138
174
|
Also, only one operation should be made public, it should always be named `call`, and it should not accept arguments (except for an optional block).
|
139
175
|
|
140
|
-
_See [To bang!, or not to bang](https://github.com/amazing-jay/simple_ruby_service/tree/master#to-bang-or-not-to-bang) to learn about `.call!` vs. `.call`._
|
141
|
-
|
142
176
|
#### Short form (_recommended_)
|
143
177
|
|
144
178
|
```ruby
|
@@ -197,9 +231,6 @@ Unlike Service Objects, Service class names should begin with a noun (and may in
|
|
197
231
|
|
198
232
|
Also, any number of operations may be made public, any of these operations may be named `call`, and any of these operations may accept arguments.
|
199
233
|
|
200
|
-
_See [To bang!, or not to bang](https://github.com/amazing-jay/simple_ruby_service/tree/master#to-bang-or-not-to-bang) to learn about `.service_method_name!` vs. `.service_method_name`._
|
201
|
-
|
202
|
-
|
203
234
|
#### Short form
|
204
235
|
|
205
236
|
_not available for Services_
|
@@ -254,7 +285,7 @@ end
|
|
254
285
|
## Creating Simple Ruby Services
|
255
286
|
|
256
287
|
### Service Objects
|
257
|
-
To implement
|
288
|
+
To implement a Simple Ruby Service Object:
|
258
289
|
|
259
290
|
1. include `SimpleRubyService::ServiceObject`
|
260
291
|
2. declare attributes with the `attribute` keyword (class level DSL)
|
@@ -282,7 +313,7 @@ end
|
|
282
313
|
```
|
283
314
|
|
284
315
|
### Services
|
285
|
-
To implement
|
316
|
+
To implement a Simple Ruby Service:
|
286
317
|
|
287
318
|
1. include `SimpleRubyService::Service`
|
288
319
|
2. declare attributes with the `attribute` keyword (class level DSL)
|
@@ -323,9 +354,9 @@ end
|
|
323
354
|
|
324
355
|
### Why should I use Services & SOs?
|
325
356
|
|
326
|
-
[
|
357
|
+
[LMGTFY](https://www.google.com/search?q=service+object+pattern+rails&rlz=1C5CHFA_enUS893US893&oq=service+object+pattern+rails) to learn more about the Services & SO design pattern.
|
327
358
|
|
328
|
-
**TLDR
|
359
|
+
**TLDR** - Fat models and fat controllers are bad! Services and Service Objects help you DRY things up.
|
329
360
|
|
330
361
|
### How is a Service different from an SO?
|
331
362
|
|
@@ -416,6 +447,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
416
447
|
|
417
448
|
## DEVELOPMENT ROADMAP
|
418
449
|
|
419
|
-
1. Create a
|
420
|
-
2.
|
450
|
+
1. Create a class level DSL to stop before each Service method unless errors.empty?
|
451
|
+
2. Create a helper to dynamically generate default SOs for ActiveRecord models (`create`, `update`, and `destroy`) _(when used in a project that includes [ActiveRecord](https://github.com/rails/rails/tree/main/activerecord))_.
|
452
|
+
3. Consider isolating validation errors from execution errors (so that invalid? is not always true when failed? is true)
|
421
453
|
|
data/simple_ruby_service.gemspec
CHANGED
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ["Jay Crouch"]
|
11
11
|
spec.email = ["i.jaycrouch@gmail.com"]
|
12
12
|
|
13
|
-
spec.summary = 'Simple Ruby Service is a lightweight framework for
|
14
|
-
spec.description = 'Simple Ruby Service is a lightweight framework for
|
13
|
+
spec.summary = 'Simple Ruby Service is a lightweight framework for creating Services and Service Objects (SOs) in Ruby.'
|
14
|
+
spec.description = 'Simple Ruby Service is a lightweight framework for creating Services and Service Objects (SOs) in Ruby. The framework provides a simple DSL that:\n a) incorporates ActiveModel validations and error handling;\n b) encourages a succinct, idiomatic coding style;\n c) allows Service Objects to ducktype as Procs.'
|
15
15
|
spec.homepage = 'https://github.com/amazing-jay/simple_ruby_service'
|
16
16
|
spec.license = "MIT"
|
17
17
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_ruby_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jay Crouch
|
@@ -304,10 +304,10 @@ dependencies:
|
|
304
304
|
- - "~>"
|
305
305
|
- !ruby/object:Gem::Version
|
306
306
|
version: '3.13'
|
307
|
-
description:
|
308
|
-
|
309
|
-
|
310
|
-
|
307
|
+
description: Simple Ruby Service is a lightweight framework for creating Services
|
308
|
+
and Service Objects (SOs) in Ruby. The framework provides a simple DSL that:\n a)
|
309
|
+
incorporates ActiveModel validations and error handling;\n b) encourages a succinct,
|
310
|
+
idiomatic coding style;\n c) allows Service Objects to ducktype as Procs.
|
311
311
|
email:
|
312
312
|
- i.jaycrouch@gmail.com
|
313
313
|
executables: []
|
@@ -358,6 +358,6 @@ requirements: []
|
|
358
358
|
rubygems_version: 3.0.9
|
359
359
|
signing_key:
|
360
360
|
specification_version: 4
|
361
|
-
summary: Simple Ruby Service is a lightweight framework for
|
362
|
-
|
361
|
+
summary: Simple Ruby Service is a lightweight framework for creating Services and
|
362
|
+
Service Objects (SOs) in Ruby.
|
363
363
|
test_files: []
|