simple_service 2.1.0 → 2.1.3

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
  SHA1:
3
- metadata.gz: 0e3f3509cf01d669899e4b014dfd78117a99f0e0
4
- data.tar.gz: 728f0043ddfc2e9d9c8d031416f900b2c4bba888
3
+ metadata.gz: 80b6b2d5cf8a166fb7c3666d0b71347526a0b9c7
4
+ data.tar.gz: 1e52375eca8157659afe17c69cf5c053bddde9df
5
5
  SHA512:
6
- metadata.gz: 83f503070b874dd3f590e1ef8bcec3c806615a415b3f31a3f1c4c5735a5dc594bff649dc7bd20a031451a8a1b981b2e08aac7e7fc0141e5b9fcd248d1bdea9ef
7
- data.tar.gz: d0b1f1da0d55f45d8ef5192bc3beb5d9fc924e8a2f03f51ab33141d6973019bf596e341fd5ef4ea06e9ef561e2ac443a4240937a7129e8063fbb17133fe85a0b
6
+ metadata.gz: '03186be2699de59c7d3f88d7a1df830fed42ec735c21bfb44f9e0c56678ab8b567097be327f0544adb653cc641db25bcff78f13ee716c6f8b49d2d39710372c4'
7
+ data.tar.gz: f8437b785fe9419de4b88c74bb40437184ef92a31532f8e23a105fd2be464587aac5f194e12ac542b2151807eedc85f7c9ee6ecfb9a6c341603d8e06093af3d6
@@ -1,7 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - jruby-19mode
4
- - 2.1.0
5
4
  - 2.3.0
6
5
  - 2.3.3
7
6
  - 2.4.2
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
- command :add_stuff
97
-
98
- def add_stuff(one:, two:)
99
- success(three: one + two)
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
- command :add_more_stuff
107
-
108
- def add_more_stuff(three:)
109
- binding.pry
110
- success(seven: three + 4)
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 `#call`
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(kwargs)
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
@@ -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
- module InstanceMethods
44
- def result
45
- @result ||= Result.new
46
- end
32
+ def call(kwargs={})
33
+ service = self.new
47
34
 
48
- def commands
49
- self.class.instance_variable_get('@commands')
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
- def call(kwargs)
53
- result.value = kwargs.is_a?(Result) ? kwargs.value : kwargs
38
+ get_commands(service).each do |cmnd|
39
+ service = execute_command(cmnd, service)
40
+ break if service.result.failure?
41
+ end
54
42
 
55
- commands.each do |command|
56
- @current_command = command
43
+ service.result
44
+ end
57
45
 
58
- command_output = if command.is_a?(Class)
59
- command.new.call(result.value)
60
- elsif command.is_a?(Symbol)
61
- method(command).call(result.value)
62
- end
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
- if command_output.is_a?(Result)
65
- result.append_result(command_output)
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
- break if result.failure?
70
+ if command_output.is_a?(Result)
71
+ service.result.append_result(command_output)
69
72
  end
70
73
 
71
- result
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, @current_command, result_value)
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, @current_command, result_value)
100
+ result.failure!(self.class, current_command, result_value)
80
101
  end
81
102
  end
82
103
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleService
2
- VERSION = '2.1.0'
2
+ VERSION = '2.1.3'
3
3
  end
@@ -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: 'asdf',
10
- bar: 'qwer',
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: :command_one,
95
+ command_name: :upcase_foo,
89
96
  success: true,
90
97
  },
91
98
  {
92
99
  class_name: 'BasicService',
93
- command_name: :command_two,
100
+ command_name: :upcase_bar,
94
101
  success: true,
95
102
  },
96
103
  {
97
- class_name: 'CommandOne',
98
- command_name: :modify_foo_bar,
104
+ class_name: 'ModifyFooBar',
105
+ command_name: :call,
99
106
  success: true,
100
107
  },
101
108
  {
102
- class_name: 'CommandTwo',
103
- command_name: :combine_foo_bar,
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: :command_one,
126
+ command_name: :upcase_foo,
120
127
  received_args: {
121
- foo: 'asdf',
122
- bar: 'qwer',
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: 'qwer',
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: :command_two,
142
+ command_name: :upcase_bar,
136
143
  received_args: {
137
144
  foo: 'FOO',
138
- bar: 'qwer',
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: 'CommandOne',
151
- command_name: :modify_foo_bar,
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: 'CommandTwo',
166
- command_name: :combine_foo_bar,
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 'command_one'
2
- require_relative 'command_two'
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
- command :command_one
8
- command :command_two
9
- commands CommandOne, CommandTwo
7
+ commands :upcase_foo,
8
+ :upcase_bar,
9
+ ModifyFooBar,
10
+ CombineFooBar
10
11
 
11
- def command_one(**kwargs)
12
- success(kwargs.merge(foo: 'FOO'))
12
+ def upcase_foo(**kwargs)
13
+ success(kwargs.merge(foo: kwargs[:foo].upcase))
13
14
  end
14
15
 
15
- def command_two(**kwargs)
16
- success(kwargs.merge(bar: '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 CommandTwo
1
+ class CombineFooBar
2
2
  include SimpleService
3
3
 
4
- command :combine_foo_bar
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
@@ -0,0 +1,4 @@
1
+ class EmptyService
2
+ include SimpleService
3
+ end
4
+
@@ -3,7 +3,7 @@ class LoopingService
3
3
 
4
4
  commands :add_one
5
5
 
6
- def call(kwargs)
6
+ def self.call(kwargs)
7
7
  count = kwargs
8
8
 
9
9
  3.times do
@@ -1,9 +1,7 @@
1
- class CommandOne
1
+ class ModifyFooBar
2
2
  include SimpleService
3
3
 
4
- command :modify_foo_bar
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.0
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-10-24 00:00:00.000000000 Z
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/command_one.rb
122
- - spec/support/command_two.rb
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/command_one.rb
154
- - spec/support/command_two.rb
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