simple_service 1.4.1 → 2.1.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/.travis.yml +1 -2
- data/README.md +111 -123
- data/lib/simple_service.rb +78 -8
- data/lib/simple_service/configuration.rb +9 -0
- data/lib/simple_service/result.rb +56 -0
- data/lib/simple_service/version.rb +1 -1
- data/spec/simple_service_spec.rb +176 -1
- data/spec/support/basic_service.rb +19 -0
- data/spec/support/command_one.rb +21 -0
- data/spec/support/command_two.rb +15 -0
- data/spec/support/looping_service.rb +20 -0
- metadata +12 -28
- data/example/hello_world.rb +0 -29
- data/example/nested_organizer.rb +0 -29
- data/example/nested_services.rb +0 -29
- data/example/override_organizer_call_method.rb +0 -39
- data/lib/simple_service/command.rb +0 -28
- data/lib/simple_service/commands/validates_commands_not_empty.rb +0 -16
- data/lib/simple_service/commands/validates_commands_properly_inherit.rb +0 -24
- data/lib/simple_service/commands/validates_expected_keys.rb +0 -19
- data/lib/simple_service/context.rb +0 -32
- data/lib/simple_service/ensure_organizer_is_valid.rb +0 -22
- data/lib/simple_service/exceptions.rb +0 -10
- data/lib/simple_service/organizer.rb +0 -113
- data/lib/simple_service/service_base.rb +0 -169
- data/spec/lib/command_spec.rb +0 -111
- data/spec/lib/commands/validates_commands_not_empty_spec.rb +0 -28
- data/spec/lib/commands/validates_commands_properly_inherit_spec.rb +0 -34
- data/spec/lib/commands/validates_expected_keys_spec.rb +0 -60
- data/spec/lib/ensure_organizer_is_valid_spec.rb +0 -17
- data/spec/lib/organizer_spec.rb +0 -216
@@ -1,28 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
class Command
|
3
|
-
|
4
|
-
include ServiceBase::InstanceMethods
|
5
|
-
extend ServiceBase::ClassMethods
|
6
|
-
|
7
|
-
attr_accessor :context
|
8
|
-
|
9
|
-
def initialize(context={})
|
10
|
-
@context = context
|
11
|
-
setup_call_chain
|
12
|
-
define_getters_and_setters
|
13
|
-
|
14
|
-
unless skip_validation
|
15
|
-
ValidatesExpectedKeys.new(provided_keys: context.keys).call
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# call is where the command's behavior is defined
|
20
|
-
# call should be overriden by whatever class inherits from
|
21
|
-
# this class
|
22
|
-
def call
|
23
|
-
error_msg = "#{self.class} - does not define a call method"
|
24
|
-
raise SimpleService::CallNotDefinedError , error_msg
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
class ValidatesCommandsNotEmpty < Command
|
3
|
-
|
4
|
-
expects :provided_commands
|
5
|
-
|
6
|
-
skip_validation true # prevent infinite loop
|
7
|
-
|
8
|
-
def call
|
9
|
-
if provided_commands.nil? || provided_commands.empty?
|
10
|
-
error_msg = 'This Organizer class does not contain any command definitions'
|
11
|
-
raise SimpleService::OrganizerCommandsNotDefinedError, error_msg
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
class ValidatesCommandsProperlyInherit < Command
|
3
|
-
|
4
|
-
expects :provided_commands
|
5
|
-
|
6
|
-
skip_validation true # prevent infinite loop
|
7
|
-
|
8
|
-
def call
|
9
|
-
# valid commands inherit from Command and do not inherit from service
|
10
|
-
# reject all valid commands and anything left over is invalid
|
11
|
-
invalid_commands = provided_commands.to_a.reject do |command|
|
12
|
-
command.ancestors.include?(SimpleService::Command) ||
|
13
|
-
command.ancestors.include?(SimpleService::Organizer)
|
14
|
-
end
|
15
|
-
|
16
|
-
if invalid_commands.any?
|
17
|
-
error_msg = invalid_commands.join(', ') +
|
18
|
-
' - must inherit from SimpleService::Command'
|
19
|
-
raise SimpleService::CommandParentClassInvalidError, error_msg
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
class ValidatesExpectedKeys < Command
|
3
|
-
|
4
|
-
expects :expected_keys, :provided_keys
|
5
|
-
|
6
|
-
skip_validation true # prevent infinite loop
|
7
|
-
|
8
|
-
def call
|
9
|
-
arguments_not_included = expected_keys.to_a - provided_keys.to_a
|
10
|
-
|
11
|
-
if arguments_not_included.any?
|
12
|
-
error_msg = 'keys required by the organizer but not found in the context: ' +
|
13
|
-
arguments_not_included.join(', ')
|
14
|
-
raise ExpectedKeyError, error_msg
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
|
3
|
-
# TODO: move context logic here?
|
4
|
-
module Context
|
5
|
-
|
6
|
-
def success?
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
def success!
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def failure?
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
def failure!
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def update_getters_and_setters
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def prune!(*keys)
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
class EnsureOrganizerIsValid < Organizer
|
3
|
-
|
4
|
-
expects :expected_keys, :provided_keys, :provided_commands
|
5
|
-
|
6
|
-
# makes sure validtion is only done once, prevents an infinite loop
|
7
|
-
skip_validation true
|
8
|
-
|
9
|
-
commands ValidatesExpectedKeys,
|
10
|
-
ValidatesCommandsNotEmpty,
|
11
|
-
ValidatesCommandsProperlyInherit
|
12
|
-
|
13
|
-
def call
|
14
|
-
super
|
15
|
-
|
16
|
-
# dont return the keys within this internal service
|
17
|
-
# so we dont pollute external service objects
|
18
|
-
context.select { |k,v| !expects.include?(k) }
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
|
3
|
-
class OrganizerCommandsNotDefinedError < StandardError; end;
|
4
|
-
class CommandParentClassInvalidError < StandardError; end;
|
5
|
-
class ExpectedKeyError < StandardError; end;
|
6
|
-
class CallNotDefinedError < StandardError; end;
|
7
|
-
class ReturnKeyError < StandardError; end;
|
8
|
-
class InvalidArgumentError < StandardError; end;
|
9
|
-
|
10
|
-
end
|
@@ -1,113 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
class Organizer
|
3
|
-
|
4
|
-
include ServiceBase::InstanceMethods
|
5
|
-
extend ServiceBase::ClassMethods
|
6
|
-
|
7
|
-
attr_accessor :context
|
8
|
-
|
9
|
-
def initialize(_context = {})
|
10
|
-
@context = validate_context(_context)
|
11
|
-
|
12
|
-
symbolize_context_keys
|
13
|
-
setup_call_chain
|
14
|
-
define_getters_and_setters
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.commands(*args)
|
18
|
-
@commands = args
|
19
|
-
end
|
20
|
-
|
21
|
-
def commands
|
22
|
-
self.class.instance_variable_get('@commands')
|
23
|
-
end
|
24
|
-
|
25
|
-
def call
|
26
|
-
# underscores used to disambiguate local vars from methods with the same name
|
27
|
-
with_validation do |_commands|
|
28
|
-
_commands.each do |command|
|
29
|
-
|
30
|
-
# halt further command calls if success has been set to false
|
31
|
-
# in a previously called command or halt is set
|
32
|
-
break if context[:success] == false || context[:halt] == true
|
33
|
-
|
34
|
-
# if command class defines "expects" then only feed the command
|
35
|
-
# those keys, otherwise just give it the entire context
|
36
|
-
_context = if command.get_expects.any?
|
37
|
-
{}.tap do |c|
|
38
|
-
command.get_expects.each {|key| c[key] = context[key] }
|
39
|
-
end
|
40
|
-
else
|
41
|
-
context
|
42
|
-
end
|
43
|
-
|
44
|
-
# also merge any optional keys
|
45
|
-
command.get_optional.each do |key|
|
46
|
-
_context[key] = context[key]
|
47
|
-
end
|
48
|
-
|
49
|
-
# instantiate and call the command
|
50
|
-
resulting_context = command.new(_context).call
|
51
|
-
|
52
|
-
# update the master context with the results of the command
|
53
|
-
@context.merge!(resulting_context)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# allow execution of the service from the class level for those
|
59
|
-
# that prefer that style
|
60
|
-
def self.call(context = {})
|
61
|
-
self.new(context).call
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
def validate_context(_context)
|
67
|
-
unless _context.class == Hash
|
68
|
-
raise InvalidArgumentError,
|
69
|
-
"Hash required as argument, but was given a #{_context.class}"
|
70
|
-
end
|
71
|
-
|
72
|
-
_context
|
73
|
-
end
|
74
|
-
|
75
|
-
def with_validation
|
76
|
-
# don't mess with the context if we are doing internal validation
|
77
|
-
add_validation_keys_to_context unless skip_validation
|
78
|
-
|
79
|
-
# ensure that the organizer and commands are setup correctly
|
80
|
-
# by injecting an internal service organizer into the stack of
|
81
|
-
# commands to be executed. Only include this if skip_validation is
|
82
|
-
# not set. Since both user defined and internal services use this code
|
83
|
-
# the skip_validation avoids an infinite loop
|
84
|
-
_commands = skip_validation ? commands : [EnsureOrganizerIsValid] + commands
|
85
|
-
|
86
|
-
yield(_commands)
|
87
|
-
|
88
|
-
# cleanup context keys that are used by the validation so the final
|
89
|
-
# return is clean even if the user does not define "returns" in their
|
90
|
-
# organizer
|
91
|
-
remove_validation_keys_from_context unless skip_validation
|
92
|
-
end
|
93
|
-
|
94
|
-
def add_validation_keys_to_context
|
95
|
-
context.merge!(validation_hash)
|
96
|
-
end
|
97
|
-
|
98
|
-
def remove_validation_keys_from_context
|
99
|
-
validation_hash.keys.each do |key|
|
100
|
-
context.delete(key)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def validation_hash
|
105
|
-
@validation_hash ||= {
|
106
|
-
provided_keys: context.keys,
|
107
|
-
expected_keys: expects,
|
108
|
-
provided_commands: commands
|
109
|
-
}
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
@@ -1,169 +0,0 @@
|
|
1
|
-
module SimpleService
|
2
|
-
module ServiceBase
|
3
|
-
|
4
|
-
module ClassMethods
|
5
|
-
def expects(*args)
|
6
|
-
@expects = args
|
7
|
-
end
|
8
|
-
|
9
|
-
def get_expects
|
10
|
-
@expects || []
|
11
|
-
end
|
12
|
-
|
13
|
-
def optional(*args)
|
14
|
-
@optional = args
|
15
|
-
end
|
16
|
-
|
17
|
-
def get_optional
|
18
|
-
@optional || []
|
19
|
-
end
|
20
|
-
|
21
|
-
def returns(*args)
|
22
|
-
@returns = args
|
23
|
-
end
|
24
|
-
|
25
|
-
def get_returns
|
26
|
-
@returns || []
|
27
|
-
end
|
28
|
-
|
29
|
-
def skip_validation(skip=true)
|
30
|
-
@skip_validation = skip
|
31
|
-
end
|
32
|
-
|
33
|
-
# allow execution of the service or commands from the
|
34
|
-
# class level for those that prefer that style
|
35
|
-
def call(context = {})
|
36
|
-
new(context).call
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
module InstanceMethods
|
42
|
-
|
43
|
-
def failed?
|
44
|
-
!successful?
|
45
|
-
end
|
46
|
-
|
47
|
-
def halted?
|
48
|
-
context.has_key?(:halt) || context[:halt] == true
|
49
|
-
end
|
50
|
-
|
51
|
-
def successful?
|
52
|
-
!context.has_key?(:success) || context[:success] == true
|
53
|
-
end
|
54
|
-
|
55
|
-
# sets up an "after" filter for #call
|
56
|
-
#
|
57
|
-
# allows user to implement #call in their individual
|
58
|
-
# command and organizer # classes without having to
|
59
|
-
# rely on super or executing another other method
|
60
|
-
# to do post #call housekeeping such as returning
|
61
|
-
# only specific context keys
|
62
|
-
def setup_call_chain
|
63
|
-
self.class.class_eval do
|
64
|
-
|
65
|
-
# grab the method object and hold onto it here
|
66
|
-
call_method = instance_method(:call)
|
67
|
-
|
68
|
-
# redefine the call method, execute the existing call method object,
|
69
|
-
# and then run return key checking...
|
70
|
-
define_method :call do
|
71
|
-
call_method.bind(self).call
|
72
|
-
return_context_with_success_status
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def symbolize_context_keys
|
78
|
-
context.keys.each do |key|
|
79
|
-
context[key.to_sym] = context.delete(key)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def return_context_with_success_status
|
84
|
-
_context = find_specified_return_keys
|
85
|
-
|
86
|
-
# only automatically set context[:success] on Organizers and only if its not already set
|
87
|
-
# by a command calling #failure!
|
88
|
-
if !_context.has_key?(:success) && organizer?
|
89
|
-
_context[:success] = true
|
90
|
-
end
|
91
|
-
|
92
|
-
_context
|
93
|
-
end
|
94
|
-
|
95
|
-
def find_specified_return_keys
|
96
|
-
if returns.nil? || returns.empty? || failed? || halted?
|
97
|
-
context
|
98
|
-
else
|
99
|
-
returns.inject({}) do |to_return, return_param|
|
100
|
-
if context.has_key?(return_param)
|
101
|
-
to_return[return_param] = context[return_param]
|
102
|
-
else
|
103
|
-
error_msg = "#{self.class} tried to return #{return_param}, but it did not exist in the context: #{context.inspect}"
|
104
|
-
raise ReturnKeyError, error_msg
|
105
|
-
end
|
106
|
-
|
107
|
-
to_return
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def expects
|
113
|
-
self.class.get_expects
|
114
|
-
end
|
115
|
-
|
116
|
-
def optional
|
117
|
-
self.class.get_optional
|
118
|
-
end
|
119
|
-
|
120
|
-
def returns
|
121
|
-
self.class.get_returns
|
122
|
-
end
|
123
|
-
|
124
|
-
def skip_validation
|
125
|
-
self.class.instance_variable_get('@skip_validation')
|
126
|
-
end
|
127
|
-
|
128
|
-
def all_context_keys
|
129
|
-
(expects + optional + returns + ['message', 'success']).uniq
|
130
|
-
end
|
131
|
-
|
132
|
-
def organizer?
|
133
|
-
self.class.ancestors.include?(SimpleService::Organizer)
|
134
|
-
end
|
135
|
-
|
136
|
-
def failure!(message = nil)
|
137
|
-
context[:success] = false
|
138
|
-
context[:halt] = true
|
139
|
-
context[:message] = message || 'There was a problem'
|
140
|
-
end
|
141
|
-
|
142
|
-
def success!(message = nil)
|
143
|
-
context[:success] = true
|
144
|
-
context[:halt] = true
|
145
|
-
context[:message] = message || 'Success! Returned early'
|
146
|
-
end
|
147
|
-
|
148
|
-
def define_getters_and_setters
|
149
|
-
all_context_keys.each do |key|
|
150
|
-
self.class.class_eval do
|
151
|
-
|
152
|
-
# getter
|
153
|
-
define_method key do
|
154
|
-
self.context[key]
|
155
|
-
end
|
156
|
-
|
157
|
-
# setter
|
158
|
-
define_method "#{key}=" do |val|
|
159
|
-
self.context[key] = val
|
160
|
-
end
|
161
|
-
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
end
|
169
|
-
end
|