services 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/services/modules/call_logger.rb +11 -5
- data/lib/services/modules/uniqueness_checker.rb +56 -11
- data/lib/services/version.rb +1 -1
- data/spec/services/base_spec.rb +0 -166
- data/spec/services/modules/call_logger_spec.rb +55 -0
- data/spec/services/modules/exception_wrapper_spec.rb +60 -0
- data/spec/services/modules/uniqueness_checker_spec.rb +96 -0
- data/spec/spec_helper.rb +13 -6
- data/spec/support/call_proxy.rb +11 -0
- data/spec/support/test_services.rb +11 -11
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6a69a25c27cff265025953ddd3dfaa6f04a4f7e
|
4
|
+
data.tar.gz: fd175797287181892a619c61dd195d1062919ad4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bb62070e8e679707ab12e04590a80a90e61895f02f2aca643248040f57d60257de7e29bc34dc306f89382f2fbf46c5e918a4e0055a52948f4e8a517c0bfe68c
|
7
|
+
data.tar.gz: 772463884aa0a6ddd64bd5ab83fa311c9881bd4e8156a5fb61253a4ed2c3d0529d5570d842ea99327a92d7adbc42e3f23239d6b05707fe200b8fdccad6a4de87
|
@@ -3,7 +3,7 @@ module Services
|
|
3
3
|
module CallLogger
|
4
4
|
def call(*args)
|
5
5
|
log "START with args #{args}"
|
6
|
-
log "CALLED BY #{caller}"
|
6
|
+
log "CALLED BY #{caller || '(not found)'}"
|
7
7
|
start = Time.now
|
8
8
|
begin
|
9
9
|
result = super
|
@@ -38,10 +38,16 @@ module Services
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def caller
|
41
|
-
caller_location = caller_locations(
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
caller_location = caller_locations(1, 10).detect do |location|
|
42
|
+
location.path !~ /\A#{Regexp.escape File.expand_path('../..', __FILE__)}/
|
43
|
+
end
|
44
|
+
if caller_location.nil?
|
45
|
+
nil
|
46
|
+
else
|
47
|
+
caller_path = caller_location.path
|
48
|
+
caller_path = caller_path.sub(%r(\A#{Regexp.escape Rails.root.to_s}/), '') if defined?(Rails)
|
49
|
+
[caller_path, caller_location.lineno].join(':')
|
50
|
+
end
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|
@@ -6,35 +6,66 @@ module Services
|
|
6
6
|
uniqueness
|
7
7
|
).join(':')
|
8
8
|
|
9
|
+
ON_ERROR = %i(
|
10
|
+
fail
|
11
|
+
ignore
|
12
|
+
reschedule
|
13
|
+
)
|
14
|
+
|
15
|
+
MAX_RETRIES = 10
|
16
|
+
ONE_HOUR = 60 * 60
|
17
|
+
|
9
18
|
def self.prepended(mod)
|
10
19
|
mod.const_set :NotUniqueError, Class.new(mod::Error)
|
11
20
|
end
|
12
21
|
|
13
|
-
def check_uniqueness!(*args)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
if similar_service_id = Services.configuration.redis.get(new_uniqueness_key)
|
20
|
-
|
22
|
+
def check_uniqueness!(*args, on_error: :fail)
|
23
|
+
raise "on_error must be one of #{ON_ERROR.join(', ')}, but was #{on_error}" unless ON_ERROR.include?(on_error.to_sym)
|
24
|
+
raise 'Service args not found.' if @service_args.nil?
|
25
|
+
@uniqueness_args = args.empty? ? @service_args : args
|
26
|
+
new_uniqueness_key = uniqueness_key(@uniqueness_args)
|
27
|
+
raise "A uniqueness key with args #{@uniqueness_args.inspect} already exists." if @uniqueness_keys && @uniqueness_keys.include?(new_uniqueness_key)
|
28
|
+
if @similar_service_id = Services.configuration.redis.get(new_uniqueness_key)
|
29
|
+
case on_error.to_sym
|
30
|
+
when :ignore
|
31
|
+
false
|
32
|
+
when :fail
|
33
|
+
raise_non_unique_error
|
34
|
+
when :reschedule
|
35
|
+
error_count = (Services.configuration.redis.get(error_count_key) || 0).to_i
|
36
|
+
if error_count >= MAX_RETRIES
|
37
|
+
raise_non_unique_error
|
38
|
+
else
|
39
|
+
error_count += 1
|
40
|
+
self.class.perform_in retry_delay(error_count), @service_args
|
41
|
+
Services.configuration.redis.setex error_count_key, retry_delay(error_count) + ONE_HOUR, error_count
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
21
45
|
else
|
22
46
|
@uniqueness_keys ||= []
|
23
|
-
raise "A uniqueness key with args #{args.inspect} already exists." if @uniqueness_keys.include?(new_uniqueness_key)
|
24
47
|
@uniqueness_keys << new_uniqueness_key
|
25
|
-
Services.configuration.redis.setex new_uniqueness_key,
|
48
|
+
Services.configuration.redis.setex new_uniqueness_key, ONE_HOUR, @id
|
49
|
+
true
|
26
50
|
end
|
27
51
|
end
|
28
52
|
|
29
53
|
def call(*args)
|
30
|
-
@
|
54
|
+
@service_args = args
|
31
55
|
super
|
32
56
|
ensure
|
33
57
|
Services.configuration.redis.del @uniqueness_keys unless @uniqueness_keys.nil? || @uniqueness_keys.empty?
|
58
|
+
Services.configuration.redis.del error_count_key
|
34
59
|
end
|
35
60
|
|
36
61
|
private
|
37
62
|
|
63
|
+
def raise_non_unique_error(retried = false)
|
64
|
+
message = "Service #{self.class} #{@id} with uniqueness args #{@uniqueness_args} is not unique, a similar service is already running: #{@similar_service_id}."
|
65
|
+
message << " The service has been retried #{MAX_RETRIES} times."
|
66
|
+
raise self.class::NotUniqueError, message
|
67
|
+
end
|
68
|
+
|
38
69
|
def uniqueness_key(args)
|
39
70
|
[
|
40
71
|
KEY_PREFIX,
|
@@ -43,6 +74,20 @@ module Services
|
|
43
74
|
key << Digest::MD5.hexdigest(args.to_s) unless args.empty?
|
44
75
|
end.join(':')
|
45
76
|
end
|
77
|
+
|
78
|
+
def error_count_key
|
79
|
+
[
|
80
|
+
KEY_PREFIX,
|
81
|
+
'errors',
|
82
|
+
self.class.to_s
|
83
|
+
].tap do |key|
|
84
|
+
key << Digest::MD5.hexdigest(@service_args.to_s) unless @service_args.empty?
|
85
|
+
end.join(':')
|
86
|
+
end
|
87
|
+
|
88
|
+
def retry_delay(error_count)
|
89
|
+
(error_count ** 3) + 5
|
90
|
+
end
|
46
91
|
end
|
47
92
|
end
|
48
93
|
end
|
data/lib/services/version.rb
CHANGED
data/spec/services/base_spec.rb
CHANGED
@@ -58,172 +58,6 @@ describe Services::Base do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
if StandardError.new.respond_to?(:cause)
|
62
|
-
context 'wrapping exceptions' do
|
63
|
-
it 'does not wrap service errors or subclasses' do
|
64
|
-
expect do
|
65
|
-
ErrorService.call
|
66
|
-
end.to raise_error do |error|
|
67
|
-
expect(error).to be_a(ErrorService::Error)
|
68
|
-
expect(error.message).to eq('I am a service error.')
|
69
|
-
expect(error.cause).to be_nil
|
70
|
-
end
|
71
|
-
|
72
|
-
class ServiceWithCustomError < Services::Base
|
73
|
-
CustomError = Class.new(self::Error)
|
74
|
-
def call
|
75
|
-
raise CustomError.new('I am a custom error.')
|
76
|
-
end
|
77
|
-
end
|
78
|
-
expect do
|
79
|
-
ServiceWithCustomError.call
|
80
|
-
end.to raise_error do |error|
|
81
|
-
expect(error).to be_a(ServiceWithCustomError::CustomError)
|
82
|
-
expect(error.message).to eq('I am a custom error.')
|
83
|
-
expect(error.cause).to be_nil
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'wraps all other exceptions' do
|
88
|
-
class ServiceWithStandardError < Services::Base
|
89
|
-
def call
|
90
|
-
raise 'I am a StandardError.'
|
91
|
-
end
|
92
|
-
end
|
93
|
-
expect do
|
94
|
-
ServiceWithStandardError.call
|
95
|
-
end.to raise_error do |error|
|
96
|
-
expect(error).to be_a(ServiceWithStandardError::Error)
|
97
|
-
expect(error.message).to eq('I am a StandardError.')
|
98
|
-
expect(error.cause).to be_a(StandardError)
|
99
|
-
expect(error.cause.message).to eq('I am a StandardError.')
|
100
|
-
end
|
101
|
-
|
102
|
-
class ServiceWithCustomStandardError < Services::Base
|
103
|
-
CustomStandardError = Class.new(StandardError)
|
104
|
-
def call
|
105
|
-
raise CustomStandardError, 'I am a custom StandardError.'
|
106
|
-
end
|
107
|
-
end
|
108
|
-
expect do
|
109
|
-
ServiceWithCustomStandardError.call
|
110
|
-
end.to raise_error do |error|
|
111
|
-
expect(error).to be_a(ServiceWithCustomStandardError::Error)
|
112
|
-
expect(error.message).to eq('I am a custom StandardError.')
|
113
|
-
expect(error.cause).to be_a(ServiceWithCustomStandardError::CustomStandardError)
|
114
|
-
expect(error.cause.message).to eq('I am a custom StandardError.')
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
context 'checking for uniqueness' do
|
121
|
-
context 'when the service checks for uniqueness with the default args' do
|
122
|
-
it 'raises an error when the same job is executed multiple times' do
|
123
|
-
wait_for_job_to_run UniqueService do
|
124
|
-
3.times do
|
125
|
-
expect { UniqueService.call }.to raise_error(UniqueService::NotUniqueError)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
context 'when the service checks for uniqueness with custom args' do
|
132
|
-
it 'raises an error when a job with the same custom args is executed multiple times' do
|
133
|
-
wait_for_job_to_run UniqueWithCustomArgsService, 'foo', 'bar', 'baz' do
|
134
|
-
3.times do
|
135
|
-
expect { UniqueWithCustomArgsService.call('foo', 'bar', 'pelle') }.to raise_error(UniqueWithCustomArgsService::NotUniqueError)
|
136
|
-
end
|
137
|
-
expect { UniqueWithCustomArgsService.call('foo', 'baz', 'pelle') }.to_not raise_error
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
context 'when the service checks for uniqueness multiple times' do
|
143
|
-
let(:args) { %w(foo bar baz) }
|
144
|
-
|
145
|
-
it 'raises an error when one of the checks fails' do
|
146
|
-
wait_for_job_to_run UniqueMultipleService, *args do
|
147
|
-
args.each do |arg|
|
148
|
-
3.times do
|
149
|
-
expect { UniqueMultipleService.call(arg) }.to raise_error(UniqueMultipleService::NotUniqueError)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
expect { UniqueMultipleService.call('pelle') }.to_not raise_error
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'does not leave any Redis keys behind' do
|
157
|
-
expect do
|
158
|
-
wait_for_job_to_run_and_finish UniqueMultipleService, *args do
|
159
|
-
args.each do |arg|
|
160
|
-
UniqueMultipleService.call(arg) rescue nil
|
161
|
-
end
|
162
|
-
UniqueMultipleService.call('pelle')
|
163
|
-
end
|
164
|
-
end.to_not change {
|
165
|
-
Services.configuration.redis.keys("*#{Services::Base::UniquenessChecker::KEY_PREFIX}*").count
|
166
|
-
}
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
context 'when the service was not set to check for uniqueness' do
|
171
|
-
it 'does not raise an error when the same job is executed twice' do
|
172
|
-
wait_for_job_to_run NonUniqueService do
|
173
|
-
expect { NonUniqueService.call }.to_not raise_error
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
context 'logging' do
|
180
|
-
it 'logs start with args and end with duration' do
|
181
|
-
service = EmptyService.new
|
182
|
-
logs = []
|
183
|
-
allow(service).to receive(:log) do |message, *|
|
184
|
-
logs << message
|
185
|
-
end
|
186
|
-
service.call 'foo', 'bar'
|
187
|
-
expect(logs.first).to eq('START with args ["foo", "bar"]')
|
188
|
-
expect(logs.last).to eq('END after 0.0 seconds')
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'logs the caller' do
|
192
|
-
service_calling_service, called_service = ServiceCallingService.new, EmptyService.new
|
193
|
-
logs = []
|
194
|
-
allow(called_service).to receive(:log) do |message, *|
|
195
|
-
logs << message
|
196
|
-
end
|
197
|
-
|
198
|
-
# When Rails is not defined, the complete caller path should be logged
|
199
|
-
service_calling_service.call called_service
|
200
|
-
expect(logs).to include(/\ACALLED BY #{Regexp.escape(PROJECT_ROOT.join(SERVICES_PATH).to_s)}:\d+/)
|
201
|
-
|
202
|
-
# When Rails is defined, only the caller path relative to Rails.root is logged
|
203
|
-
class Rails
|
204
|
-
def self.root; PROJECT_ROOT; end
|
205
|
-
end
|
206
|
-
logs = []
|
207
|
-
service_calling_service.call called_service
|
208
|
-
expect(logs).to include(/\ACALLED BY #{Regexp.escape(SERVICES_PATH)}:\d+/)
|
209
|
-
Object.send :remove_const, :Rails
|
210
|
-
end
|
211
|
-
|
212
|
-
if RUBY_VERSION > '2.1'
|
213
|
-
it 'logs exceptions and exception causes' do
|
214
|
-
service = NestedExceptionService.new
|
215
|
-
logs = []
|
216
|
-
allow(service).to receive(:log) do |message, *|
|
217
|
-
logs << message
|
218
|
-
end
|
219
|
-
expect { service.call }.to raise_error(NestedExceptionService::Error)
|
220
|
-
%w(NestedError1 NestedError2).each do |error|
|
221
|
-
expect(logs).to include(/\Acaused by: NestedExceptionService::#{error}/)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
61
|
context 'when executed asynchronously' do
|
228
62
|
it 'finds its own worker' do
|
229
63
|
3.times { OwnWorkerService.perform_async }
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Services::Base::CallLogger do
|
4
|
+
it 'logs start with args and end with duration' do
|
5
|
+
service = EmptyService.new
|
6
|
+
logs = []
|
7
|
+
allow(service).to receive(:log) do |message, *|
|
8
|
+
logs << message
|
9
|
+
end
|
10
|
+
service.call 'foo', 'bar'
|
11
|
+
expect(logs.first).to eq('START with args ["foo", "bar"]')
|
12
|
+
expect(logs.last).to eq('END after 0.0 seconds')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'logs the caller' do
|
16
|
+
service_calling_service, called_service = ServiceCallingService.new, EmptyService.new
|
17
|
+
logs = []
|
18
|
+
allow(called_service).to receive(:log) do |message, *|
|
19
|
+
logs << message
|
20
|
+
end
|
21
|
+
|
22
|
+
# When Rails is not defined, the complete caller path should be logged
|
23
|
+
service_calling_service.call called_service
|
24
|
+
expect(logs).to include(/\ACALLED BY #{Regexp.escape PROJECT_ROOT.join(TEST_SERVICES_PATH).to_s}:\d+/)
|
25
|
+
|
26
|
+
# When Rails is defined, only the caller path relative to Rails.root is logged
|
27
|
+
class Rails
|
28
|
+
def self.root; PROJECT_ROOT; end
|
29
|
+
end
|
30
|
+
logs = []
|
31
|
+
service_calling_service.call called_service
|
32
|
+
expect(logs).to include(/\ACALLED BY #{Regexp.escape TEST_SERVICES_PATH.to_s}:\d+/)
|
33
|
+
Object.send :remove_const, :Rails
|
34
|
+
|
35
|
+
# Caller paths from services lib folder should be filtered
|
36
|
+
require 'services/call_proxy'
|
37
|
+
logs = []
|
38
|
+
Services::CallProxy.call(called_service, :call)
|
39
|
+
expect(logs).to include(/\ACALLED BY #{Regexp.escape __FILE__}:\d+/)
|
40
|
+
end
|
41
|
+
|
42
|
+
if RUBY_VERSION > '2.1'
|
43
|
+
it 'logs exceptions and exception causes' do
|
44
|
+
service = NestedExceptionService.new
|
45
|
+
logs = []
|
46
|
+
allow(service).to receive(:log) do |message, *|
|
47
|
+
logs << message
|
48
|
+
end
|
49
|
+
expect { service.call }.to raise_error(service.class::Error)
|
50
|
+
%w(NestedError1 NestedError2).each do |error|
|
51
|
+
expect(logs).to include(/\Acaused by: #{service.class}::#{error}/)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Services::Base::ExceptionWrapper do
|
4
|
+
if StandardError.new.respond_to?(:cause)
|
5
|
+
it 'does not wrap service errors or subclasses' do
|
6
|
+
expect do
|
7
|
+
ErrorService.call
|
8
|
+
end.to raise_error do |error|
|
9
|
+
expect(error).to be_a(ErrorService::Error)
|
10
|
+
expect(error.message).to eq('I am a service error.')
|
11
|
+
expect(error.cause).to be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
class ServiceWithCustomError < Services::Base
|
15
|
+
CustomError = Class.new(self::Error)
|
16
|
+
def call
|
17
|
+
raise CustomError.new('I am a custom error.')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
expect do
|
21
|
+
ServiceWithCustomError.call
|
22
|
+
end.to raise_error do |error|
|
23
|
+
expect(error).to be_a(ServiceWithCustomError::CustomError)
|
24
|
+
expect(error.message).to eq('I am a custom error.')
|
25
|
+
expect(error.cause).to be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'wraps all other exceptions' do
|
30
|
+
class ServiceWithStandardError < Services::Base
|
31
|
+
def call
|
32
|
+
raise 'I am a StandardError.'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
expect do
|
36
|
+
ServiceWithStandardError.call
|
37
|
+
end.to raise_error do |error|
|
38
|
+
expect(error).to be_a(ServiceWithStandardError::Error)
|
39
|
+
expect(error.message).to eq('I am a StandardError.')
|
40
|
+
expect(error.cause).to be_a(StandardError)
|
41
|
+
expect(error.cause.message).to eq('I am a StandardError.')
|
42
|
+
end
|
43
|
+
|
44
|
+
class ServiceWithCustomStandardError < Services::Base
|
45
|
+
CustomStandardError = Class.new(StandardError)
|
46
|
+
def call
|
47
|
+
raise CustomStandardError, 'I am a custom StandardError.'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
expect do
|
51
|
+
ServiceWithCustomStandardError.call
|
52
|
+
end.to raise_error do |error|
|
53
|
+
expect(error).to be_a(ServiceWithCustomStandardError::Error)
|
54
|
+
expect(error.message).to eq('I am a custom StandardError.')
|
55
|
+
expect(error.cause).to be_a(ServiceWithCustomStandardError::CustomStandardError)
|
56
|
+
expect(error.cause.message).to eq('I am a custom StandardError.')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples 'checking the uniqueness properly' do
|
4
|
+
it 'notices when the same job is executed multiple times' do
|
5
|
+
# Check that error is raised when on_error is "fail"
|
6
|
+
wait_for_job_to_run_and_finish service, *args, 'fail', true do
|
7
|
+
if defined?(fail_args)
|
8
|
+
3.times do
|
9
|
+
fail_args.each do |fail_arg_group|
|
10
|
+
expect { service.call(*fail_arg_group, 'fail', false) }.to raise_error(service::NotUniqueError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
if defined?(pass_args)
|
15
|
+
3.times do
|
16
|
+
pass_args.each do |pass_arg_group|
|
17
|
+
expect { service.call(*pass_arg_group, 'fail', false) }.to_not raise_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check that no error is raised when on_error is "ignore"
|
23
|
+
if defined?(fail_args)
|
24
|
+
3.times do
|
25
|
+
fail_args.each do |fail_arg_group|
|
26
|
+
expect { service.call(*fail_arg_group, 'ignore', false) }.to_not raise_error
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
if defined?(pass_args)
|
31
|
+
3.times do
|
32
|
+
pass_args.each do |pass_arg_group|
|
33
|
+
expect { service.call(*pass_arg_group, 'ignore', false) }.to_not raise_error
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Check that service is rescheduled when on_error is "reschedule"
|
39
|
+
if defined?(fail_args)
|
40
|
+
3.times do
|
41
|
+
fail_args.each do |fail_arg_group|
|
42
|
+
expect(service).to receive(:perform_in).with(an_instance_of(Fixnum), fail_arg_group + (['reschedule', false]))
|
43
|
+
service.call(*fail_arg_group, 'reschedule', false)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
if defined?(pass_args)
|
48
|
+
3.times do
|
49
|
+
pass_args.each do |pass_arg_group|
|
50
|
+
expect { service.call(*pass_arg_group, 'reschedule', false) }.to_not raise_error
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe Services::Base::UniquenessChecker do
|
59
|
+
context 'when the service checks for uniqueness with the default args' do
|
60
|
+
it_behaves_like 'checking the uniqueness properly' do
|
61
|
+
let(:service) { UniqueService }
|
62
|
+
let(:args) { [] }
|
63
|
+
let(:fail_args) { [] }
|
64
|
+
let(:keys_after_start) { 1 }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when the service checks for uniqueness with custom args' do
|
69
|
+
it_behaves_like 'checking the uniqueness properly' do
|
70
|
+
let(:service) { UniqueWithCustomArgsService }
|
71
|
+
let(:args) { %w(foo bar baz) }
|
72
|
+
let(:fail_args) { [%w(foo bar pelle)] }
|
73
|
+
let(:pass_args) { [%w(foo baz pelle)] }
|
74
|
+
let(:keys_after_start) { 1 }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when the service checks for uniqueness multiple times' do
|
79
|
+
it_behaves_like 'checking the uniqueness properly' do
|
80
|
+
let(:service) { UniqueMultipleService }
|
81
|
+
let(:args) { %w(foo bar baz) }
|
82
|
+
let(:fail_args) { args.map { |arg| [arg] } }
|
83
|
+
let(:pass_args) { [%w(pelle)] }
|
84
|
+
let(:keys_after_start) { 3 }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when the service does not check for uniqueness' do
|
89
|
+
it_behaves_like 'checking the uniqueness properly' do
|
90
|
+
let(:service) { NonUniqueService }
|
91
|
+
let(:args) { [] }
|
92
|
+
let(:pass_args) { [] }
|
93
|
+
let(:keys_after_start) { 0 }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,15 +4,16 @@ require 'redis'
|
|
4
4
|
require 'sidekiq'
|
5
5
|
|
6
6
|
require_relative '../lib/services'
|
7
|
-
require_relative 'support/helpers'
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
PROJECT_ROOT = Pathname.new(File.expand_path('../..', __FILE__))
|
9
|
+
TEST_SERVICES_PATH = Pathname.new(File.join('spec', 'support', 'test_services.rb'))
|
10
|
+
support_dir = Pathname.new(File.expand_path('../support', __FILE__))
|
11
|
+
log_dir = support_dir.join('log')
|
11
12
|
|
12
|
-
|
13
|
+
CALL_PROXY_SOURCE = support_dir.join('call_proxy.rb')
|
14
|
+
CALL_PROXY_DESTINATION = PROJECT_ROOT.join('lib', 'services', 'call_proxy.rb')
|
13
15
|
|
14
|
-
support_dir
|
15
|
-
log_dir = support_dir.join('log')
|
16
|
+
Dir[support_dir.join('**', '*.rb')].each { |f| require f }
|
16
17
|
|
17
18
|
redis_port = 6379
|
18
19
|
redis_pidfile = support_dir.join('redis.pid')
|
@@ -65,9 +66,15 @@ RSpec.configure do |config|
|
|
65
66
|
pidfile: sidekiq_pidfile
|
66
67
|
}
|
67
68
|
system "bundle exec sidekiq #{options_hash_to_string(sidekiq_options)}"
|
69
|
+
|
70
|
+
# Copy call proxy
|
71
|
+
FileUtils.cp CALL_PROXY_SOURCE, CALL_PROXY_DESTINATION
|
68
72
|
end
|
69
73
|
|
70
74
|
config.after :suite do
|
75
|
+
# Delete call proxy
|
76
|
+
FileUtils.rm CALL_PROXY_DESTINATION
|
77
|
+
|
71
78
|
# Stop Sidekiq
|
72
79
|
system "bundle exec sidekiqctl stop #{sidekiq_pidfile} #{sidekiq_timeout}"
|
73
80
|
while File.exist?(sidekiq_pidfile)
|
@@ -65,31 +65,31 @@ class ServiceCallingService < Services::Base
|
|
65
65
|
end
|
66
66
|
|
67
67
|
class UniqueService < Services::Base
|
68
|
-
def call
|
69
|
-
check_uniqueness!
|
70
|
-
sleep 0.5
|
68
|
+
def call(on_error, sleep)
|
69
|
+
check_uniqueness! on_error: on_error
|
70
|
+
sleep 0.5 if sleep
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
74
|
class UniqueWithCustomArgsService < Services::Base
|
75
|
-
def call(uniqueness_arg1, uniqueness_arg2, ignore_arg)
|
76
|
-
check_uniqueness! uniqueness_arg1, uniqueness_arg2
|
77
|
-
sleep 0.5
|
75
|
+
def call(uniqueness_arg1, uniqueness_arg2, ignore_arg, on_error, sleep)
|
76
|
+
check_uniqueness! uniqueness_arg1, uniqueness_arg2, on_error: on_error
|
77
|
+
sleep 0.5 if sleep
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
81
|
class UniqueMultipleService < Services::Base
|
82
|
-
def call(*args)
|
82
|
+
def call(*args, on_error, sleep)
|
83
83
|
args.each do |arg|
|
84
|
-
check_uniqueness! arg
|
84
|
+
check_uniqueness! arg, on_error: on_error
|
85
85
|
end
|
86
|
-
sleep 0.5
|
86
|
+
sleep 0.5 if sleep
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
90
|
class NonUniqueService < Services::Base
|
91
|
-
def call
|
92
|
-
sleep 0.5
|
91
|
+
def call(on_error, sleep)
|
92
|
+
sleep 0.5 if sleep
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: services
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manuel Meurer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -146,7 +146,11 @@ files:
|
|
146
146
|
- lib/services/version.rb
|
147
147
|
- services.gemspec
|
148
148
|
- spec/services/base_spec.rb
|
149
|
+
- spec/services/modules/call_logger_spec.rb
|
150
|
+
- spec/services/modules/exception_wrapper_spec.rb
|
151
|
+
- spec/services/modules/uniqueness_checker_spec.rb
|
149
152
|
- spec/spec_helper.rb
|
153
|
+
- spec/support/call_proxy.rb
|
150
154
|
- spec/support/helpers.rb
|
151
155
|
- spec/support/log/.gitkeep
|
152
156
|
- spec/support/redis-cli
|
@@ -178,7 +182,11 @@ specification_version: 4
|
|
178
182
|
summary: ''
|
179
183
|
test_files:
|
180
184
|
- spec/services/base_spec.rb
|
185
|
+
- spec/services/modules/call_logger_spec.rb
|
186
|
+
- spec/services/modules/exception_wrapper_spec.rb
|
187
|
+
- spec/services/modules/uniqueness_checker_spec.rb
|
181
188
|
- spec/spec_helper.rb
|
189
|
+
- spec/support/call_proxy.rb
|
182
190
|
- spec/support/helpers.rb
|
183
191
|
- spec/support/log/.gitkeep
|
184
192
|
- spec/support/redis-cli
|