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.
@@ -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