simple_service 2.1.0 → 2.1.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/.travis.yml +0 -1
- data/README.md +42 -12
- data/lib/simple_service.rb +54 -33
- data/lib/simple_service/version.rb +1 -1
- data/spec/simple_service_spec.rb +25 -18
- data/spec/support/basic_service.rb +10 -9
- data/spec/support/{command_two.rb → combine_foo_bar.rb} +2 -4
- data/spec/support/empty_service.rb +4 -0
- data/spec/support/looping_service.rb +1 -1
- data/spec/support/{command_one.rb → modify_foo_bar.rb} +2 -4
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80b6b2d5cf8a166fb7c3666d0b71347526a0b9c7
|
4
|
+
data.tar.gz: 1e52375eca8157659afe17c69cf5c053bddde9df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '03186be2699de59c7d3f88d7a1df830fed42ec735c21bfb44f9e0c56678ab8b567097be327f0544adb653cc641db25bcff78f13ee716c6f8b49d2d39710372c4'
|
7
|
+
data.tar.gz: f8437b785fe9419de4b88c74bb40437184ef92a31532f8e23a105fd2be464587aac5f194e12ac542b2151807eedc85f7c9ee6ecfb9a6c341603d8e06093af3d6
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -12,6 +12,18 @@ and composable units of business logic. The core concept of SimpleService is the
|
|
12
12
|
When properly designed, these command objects can be composited together or even nested to create
|
13
13
|
complex flows.
|
14
14
|
|
15
|
+
# 2.0.0 update notes
|
16
|
+
|
17
|
+
This update is a major refactor from previous 1.x.x versions. After revisiting this codebase I decided that
|
18
|
+
I needed to make SimpleService actually simple in both use and implementation. The gem has now been paired down
|
19
|
+
to about 150 lines of code.
|
20
|
+
|
21
|
+
* All functionality is added to your service class via module inclusion instead of inheritance
|
22
|
+
* The concept of an Organizer has been removed
|
23
|
+
* The DSL for defining interfaces has been removed in favor of simple keyword arguments
|
24
|
+
* `#success` or `#failure` must be called within each command or call method
|
25
|
+
* Services are always invoked via the class method `.call`. Previously you could use either `#call` or `.call`.
|
26
|
+
|
15
27
|
## Installation
|
16
28
|
|
17
29
|
Add this line to your application's Gemfile:
|
@@ -84,7 +96,9 @@ result.failure? #=> true
|
|
84
96
|
result.value #=> {:message=>"hey Bob, things went wrong."}
|
85
97
|
```
|
86
98
|
|
87
|
-
You can also use ClassNames as commands and to organize them into other files
|
99
|
+
You can also use ClassNames as commands and to organize them into other files. If `#call` is
|
100
|
+
defined and no other commands are defined via `command` or `commands` then SimpleService will
|
101
|
+
automatically use `#call` as the default command
|
88
102
|
|
89
103
|
```ruby
|
90
104
|
require 'rubygems'
|
@@ -93,21 +107,24 @@ require 'simple_service'
|
|
93
107
|
class CommandOne
|
94
108
|
include SimpleService
|
95
109
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
110
|
+
def call(one:, two:)
|
111
|
+
if one == 1 && two == 2
|
112
|
+
success(three: one + two)
|
113
|
+
else
|
114
|
+
failure(something: 'went wrong')
|
115
|
+
end
|
100
116
|
end
|
101
117
|
end
|
102
118
|
|
103
119
|
class CommandTwo
|
104
120
|
include SimpleService
|
105
121
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
122
|
+
def call(three:)
|
123
|
+
if three == 3
|
124
|
+
success(seven: three + 4)
|
125
|
+
else
|
126
|
+
failure(another_thing: 'went wrong')
|
127
|
+
end
|
111
128
|
end
|
112
129
|
end
|
113
130
|
|
@@ -120,9 +137,13 @@ end
|
|
120
137
|
result = DoNestedStuff.call(one: 1, two: 2)
|
121
138
|
result.success? #=> true
|
122
139
|
result.value #=> {:seven=>7}
|
140
|
+
|
141
|
+
result = DoNestedStuff.call(one: 2, two: 1)
|
142
|
+
result.success? #=> false
|
143
|
+
result.value #=> {something: 'went wrong'}
|
123
144
|
```
|
124
145
|
|
125
|
-
If you would like your service to process an enumerable you can override
|
146
|
+
If you would like your service to process an enumerable you can override `.call`
|
126
147
|
on your service object. Invoking `#super` in your definition and passing along
|
127
148
|
the appropriate arguments will allow your command chain to proceed as normal, but
|
128
149
|
called multiple times via a loop. The Result object returned from each call to `#super`
|
@@ -138,9 +159,14 @@ class LoopingService
|
|
138
159
|
|
139
160
|
commands :add_one
|
140
161
|
|
141
|
-
def call(
|
162
|
+
def self.call(count:)
|
142
163
|
count = kwargs
|
143
164
|
|
165
|
+
# In this example the result object from super overwrites
|
166
|
+
# the previous result/initial args. You could also capture
|
167
|
+
# results in an array or hash for further manipulation.
|
168
|
+
# If you do not need to do anything with the result object
|
169
|
+
# then there is no need to assign it back to anything.
|
144
170
|
3.times do
|
145
171
|
count = super(count)
|
146
172
|
end
|
@@ -152,6 +178,10 @@ class LoopingService
|
|
152
178
|
success(count: count + 1)
|
153
179
|
end
|
154
180
|
end
|
181
|
+
|
182
|
+
result = LoopingService.call(count: 0)
|
183
|
+
result.is_a?(SimpleService::Result) #=> true
|
184
|
+
result.value #=> {count: 3}
|
155
185
|
```
|
156
186
|
|
157
187
|
If you are using this with a Rails app, placing top level services in
|
data/lib/simple_service.rb
CHANGED
@@ -19,16 +19,6 @@ module SimpleService
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module ClassMethods
|
22
|
-
def call(**kwargs)
|
23
|
-
service = self.new
|
24
|
-
|
25
|
-
if service.method(:call).arity.zero?
|
26
|
-
service.call
|
27
|
-
else
|
28
|
-
service.call(kwargs)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
22
|
def command(command_name)
|
33
23
|
@commands ||= []
|
34
24
|
@commands << command_name
|
@@ -38,45 +28,76 @@ module SimpleService
|
|
38
28
|
@commands ||= []
|
39
29
|
@commands += args
|
40
30
|
end
|
41
|
-
end
|
42
31
|
|
43
|
-
|
44
|
-
|
45
|
-
@result ||= Result.new
|
46
|
-
end
|
32
|
+
def call(kwargs={})
|
33
|
+
service = self.new
|
47
34
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
35
|
+
# if kwargs is a result obj then pull its hash out via #value
|
36
|
+
service.result.value = kwargs.is_a?(Result) ? kwargs.value : kwargs
|
51
37
|
|
52
|
-
|
53
|
-
|
38
|
+
get_commands(service).each do |cmnd|
|
39
|
+
service = execute_command(cmnd, service)
|
40
|
+
break if service.result.failure?
|
41
|
+
end
|
54
42
|
|
55
|
-
|
56
|
-
|
43
|
+
service.result
|
44
|
+
end
|
57
45
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
46
|
+
def get_commands(service)
|
47
|
+
if !@commands.nil? && @commands.any?
|
48
|
+
@commands
|
49
|
+
elsif service.respond_to?(:call)
|
50
|
+
[:call]
|
51
|
+
else
|
52
|
+
raise "No commands defined for #{self.to_s}, define at least one " \
|
53
|
+
'command or implement the #call method'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def execute_command(cmnd, service)
|
58
|
+
service.current_command = cmnd
|
63
59
|
|
64
|
-
|
65
|
-
|
60
|
+
command_output = if cmnd.is_a?(Class)
|
61
|
+
cmnd.call(service.result.value)
|
62
|
+
elsif cmnd.is_a?(Symbol)
|
63
|
+
if service.method(cmnd).arity.zero?
|
64
|
+
service.public_send(cmnd)
|
65
|
+
else
|
66
|
+
service.public_send(cmnd, service.result.value)
|
66
67
|
end
|
68
|
+
end
|
67
69
|
|
68
|
-
|
70
|
+
if command_output.is_a?(Result)
|
71
|
+
service.result.append_result(command_output)
|
69
72
|
end
|
70
73
|
|
71
|
-
|
74
|
+
service
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module InstanceMethods
|
79
|
+
def result
|
80
|
+
@result ||= Result.new
|
81
|
+
end
|
82
|
+
|
83
|
+
def current_command
|
84
|
+
@current_command
|
85
|
+
end
|
86
|
+
|
87
|
+
def current_command=(cmnd)
|
88
|
+
@current_command = cmnd
|
89
|
+
end
|
90
|
+
|
91
|
+
def commands
|
92
|
+
self.class.instance_variable_get('@commands') || []
|
72
93
|
end
|
73
94
|
|
74
95
|
def success(result_value)
|
75
|
-
result.success!(self.class,
|
96
|
+
result.success!(self.class, current_command, result_value)
|
76
97
|
end
|
77
98
|
|
78
99
|
def failure(result_value)
|
79
|
-
result.failure!(self.class,
|
100
|
+
result.failure!(self.class, current_command, result_value)
|
80
101
|
end
|
81
102
|
end
|
82
103
|
end
|
data/spec/simple_service_spec.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require_relative 'support/basic_service'
|
3
3
|
require_relative 'support/looping_service'
|
4
|
+
require_relative 'support/empty_service'
|
4
5
|
|
5
6
|
RSpec.describe SimpleService do
|
6
7
|
|
7
8
|
let(:params) do
|
8
9
|
{
|
9
|
-
foo: '
|
10
|
-
bar: '
|
10
|
+
foo: 'foo',
|
11
|
+
bar: 'bar',
|
11
12
|
command_one_success: true,
|
12
13
|
command_two_success: true,
|
13
14
|
}
|
@@ -79,28 +80,34 @@ RSpec.describe SimpleService do
|
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
83
|
+
context 'improperly configured service class' do
|
84
|
+
it 'raises error when no commands or call defined' do
|
85
|
+
expect { EmptyService.call }.to raise_error(/No commands defined for EmptyService/)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
82
89
|
context 'record commands' do
|
83
90
|
it 'records normal command execution' do
|
84
91
|
expect(result.recorded_commands).to eq(
|
85
92
|
[
|
86
93
|
{
|
87
94
|
class_name: 'BasicService',
|
88
|
-
command_name: :
|
95
|
+
command_name: :upcase_foo,
|
89
96
|
success: true,
|
90
97
|
},
|
91
98
|
{
|
92
99
|
class_name: 'BasicService',
|
93
|
-
command_name: :
|
100
|
+
command_name: :upcase_bar,
|
94
101
|
success: true,
|
95
102
|
},
|
96
103
|
{
|
97
|
-
class_name: '
|
98
|
-
command_name: :
|
104
|
+
class_name: 'ModifyFooBar',
|
105
|
+
command_name: :call,
|
99
106
|
success: true,
|
100
107
|
},
|
101
108
|
{
|
102
|
-
class_name: '
|
103
|
-
command_name: :
|
109
|
+
class_name: 'CombineFooBar',
|
110
|
+
command_name: :call,
|
104
111
|
success: true,
|
105
112
|
},
|
106
113
|
]
|
@@ -116,26 +123,26 @@ RSpec.describe SimpleService do
|
|
116
123
|
[
|
117
124
|
{
|
118
125
|
class_name: 'BasicService',
|
119
|
-
command_name: :
|
126
|
+
command_name: :upcase_foo,
|
120
127
|
received_args: {
|
121
|
-
foo: '
|
122
|
-
bar: '
|
128
|
+
foo: 'foo',
|
129
|
+
bar: 'bar',
|
123
130
|
command_one_success: true,
|
124
131
|
command_two_success: true
|
125
132
|
},
|
126
133
|
success: {
|
127
134
|
foo: 'FOO',
|
128
|
-
bar: '
|
135
|
+
bar: 'bar',
|
129
136
|
command_one_success: true,
|
130
137
|
command_two_success: true,
|
131
138
|
},
|
132
139
|
},
|
133
140
|
{
|
134
141
|
class_name: 'BasicService',
|
135
|
-
command_name: :
|
142
|
+
command_name: :upcase_bar,
|
136
143
|
received_args: {
|
137
144
|
foo: 'FOO',
|
138
|
-
bar: '
|
145
|
+
bar: 'bar',
|
139
146
|
command_one_success: true,
|
140
147
|
command_two_success: true
|
141
148
|
},
|
@@ -147,8 +154,8 @@ RSpec.describe SimpleService do
|
|
147
154
|
},
|
148
155
|
},
|
149
156
|
{
|
150
|
-
class_name: '
|
151
|
-
command_name: :
|
157
|
+
class_name: 'ModifyFooBar',
|
158
|
+
command_name: :call,
|
152
159
|
received_args: {
|
153
160
|
foo: 'FOO',
|
154
161
|
bar: 'BAR',
|
@@ -162,8 +169,8 @@ RSpec.describe SimpleService do
|
|
162
169
|
},
|
163
170
|
},
|
164
171
|
{
|
165
|
-
class_name: '
|
166
|
-
command_name: :
|
172
|
+
class_name: 'CombineFooBar',
|
173
|
+
command_name: :call,
|
167
174
|
received_args: {
|
168
175
|
modified_foo: 'modified FOO',
|
169
176
|
modified_bar: 'modified BAR',
|
@@ -1,19 +1,20 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative '
|
1
|
+
require_relative 'modify_foo_bar'
|
2
|
+
require_relative 'combine_foo_bar'
|
3
3
|
|
4
4
|
class BasicService
|
5
5
|
include SimpleService
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
commands :upcase_foo,
|
8
|
+
:upcase_bar,
|
9
|
+
ModifyFooBar,
|
10
|
+
CombineFooBar
|
10
11
|
|
11
|
-
def
|
12
|
-
success(kwargs.merge(foo:
|
12
|
+
def upcase_foo(**kwargs)
|
13
|
+
success(kwargs.merge(foo: kwargs[:foo].upcase))
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
success(kwargs.merge(bar:
|
16
|
+
def upcase_bar(**kwargs)
|
17
|
+
success(kwargs.merge(bar: kwargs[:bar].upcase))
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -1,9 +1,7 @@
|
|
1
|
-
class
|
1
|
+
class CombineFooBar
|
2
2
|
include SimpleService
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
def combine_foo_bar(modified_foo:, modified_bar:, command_two_success:)
|
4
|
+
def call(modified_foo:, modified_bar:, command_two_success:)
|
7
5
|
combined_foo_bar = "combined #{modified_foo} #{modified_bar}"
|
8
6
|
|
9
7
|
if command_two_success
|
@@ -1,9 +1,7 @@
|
|
1
|
-
class
|
1
|
+
class ModifyFooBar
|
2
2
|
include SimpleService
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
def modify_foo_bar(foo:, bar:, command_one_success:, command_two_success:)
|
4
|
+
def call(foo:, bar:, command_one_success:, command_two_success:)
|
7
5
|
modified_foo = "modified #{foo}"
|
8
6
|
modified_bar = "modified #{bar}"
|
9
7
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jarrod Spillers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -118,9 +118,10 @@ files:
|
|
118
118
|
- spec/simple_service_spec.rb
|
119
119
|
- spec/spec_helper.rb
|
120
120
|
- spec/support/basic_service.rb
|
121
|
-
- spec/support/
|
122
|
-
- spec/support/
|
121
|
+
- spec/support/combine_foo_bar.rb
|
122
|
+
- spec/support/empty_service.rb
|
123
123
|
- spec/support/looping_service.rb
|
124
|
+
- spec/support/modify_foo_bar.rb
|
124
125
|
homepage: https://github.com/jspillers/simple_service
|
125
126
|
licenses:
|
126
127
|
- MIT
|
@@ -150,6 +151,7 @@ test_files:
|
|
150
151
|
- spec/simple_service_spec.rb
|
151
152
|
- spec/spec_helper.rb
|
152
153
|
- spec/support/basic_service.rb
|
153
|
-
- spec/support/
|
154
|
-
- spec/support/
|
154
|
+
- spec/support/combine_foo_bar.rb
|
155
|
+
- spec/support/empty_service.rb
|
155
156
|
- spec/support/looping_service.rb
|
157
|
+
- spec/support/modify_foo_bar.rb
|