process_handler 0.1.1 → 0.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: 3025924d4d9d6aabf01d20bb1fe3517f3d20ef1d
4
- data.tar.gz: 55fb26289828ad292043ae2eb2ca1657d59286b4
3
+ metadata.gz: 5b3e99c6bdab6b69d12a5a474666c512174360e0
4
+ data.tar.gz: 3424ff7f14a8c0e219fd4dded8bde82e480f04ff
5
5
  SHA512:
6
- metadata.gz: 9907397cf94e2c1e5c92881645bb6efd826bf0ccbcd831f59c5b964ac6f723dbfaf39c4e59d27c8b6f3b42287a737eead053f9924b710983db1c31321f5eb6c0
7
- data.tar.gz: 745805dfd41786a6d69b5c1aaa667fa13e99bb1a76676b0abd53905bc5b32848e02b7d6d58b0f626259af24582fa1adaca528f89e77a05b42ce8475c302acce2
6
+ metadata.gz: dac9142e911cb9a56f1b877b6a8ec888521effd2201240bbffa58054ac8813a4280d1b8726924dd0a85cb3ecf4aa5653a9408f8f15903f3dccee38057e1af0fb
7
+ data.tar.gz: 07cd4ae7c681df57c7da57fc428e731404e05ada1666cf7a279ca7e26a8918d74c95b655ea77accdba97255094b9adab2ff59570b75e04445c702533a93b1da9
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- process_handler (0.1.0)
4
+ process_handler (0.1.3)
5
5
  airbrake
6
6
  sucker_punch (~> 1.1)
7
7
 
@@ -32,19 +32,79 @@ module Salemove
32
32
  def spawn(service, blocking: true)
33
33
  @process_monitor.start
34
34
 
35
- @service_thread = ServiceSpawner.spawn(service, @messenger, @exception_notifier)
35
+ @service_threads = spawn_queue_threads(service).concat(spawn_tap_threads(service))
36
36
  blocking ? wait_for_monitor : Thread.new { wait_for_monitor }
37
37
  end
38
38
 
39
+ def spawn_queue_threads(service)
40
+ if service.class.const_defined?(:QUEUE)
41
+ [ServiceSpawner.spawn(service, @messenger, @exception_notifier)]
42
+ else
43
+ []
44
+ end
45
+ end
46
+
47
+ def spawn_tap_threads(service)
48
+ if service.class.const_defined?(:TAPPED_QUEUES)
49
+ service.class::TAPPED_QUEUES.map do |queue|
50
+ spawner = TapServiceSpawner.new(service, @messenger, @exception_notifier)
51
+ spawner.spawn(queue)
52
+ end
53
+ else
54
+ []
55
+ end
56
+ end
57
+
39
58
  private
40
59
 
60
+ def self.benchmark(input, &block)
61
+ type = input[:type] if input.is_a?(Hash)
62
+ result = nil
63
+
64
+ bm = Benchmark.measure { result = block.call }
65
+ if defined?(Logasm) && PivotProcess.logger.is_a?(Logasm)
66
+ PivotProcess.logger.debug "Execution time",
67
+ type: type, real: bm.real, user: bm.utime, system: bm.stime
68
+ end
69
+ result
70
+ end
71
+
41
72
  def wait_for_monitor
42
73
  sleep 1 while @process_monitor.running?
43
- @service_thread.shutdown
44
- @service_thread.join
74
+ @service_threads.each do |service_thread|
75
+ service_thread.shutdown
76
+ service_thread.join
77
+ end
45
78
  @process_monitor.shutdown
46
79
  end
47
80
 
81
+ class TapServiceSpawner
82
+ def initialize(service, messenger, exception_notifier)
83
+ @service = service
84
+ @messenger = messenger
85
+ @exception_notifier = exception_notifier
86
+ end
87
+
88
+ def spawn(queue)
89
+ @messenger.tap_into(queue) do |input|
90
+ delegate_to_service(input.merge(type: queue))
91
+ end
92
+ end
93
+
94
+ def delegate_to_service(input)
95
+ PivotProcess.benchmark(input) { @service.call(input) }
96
+ rescue => exception
97
+ handle_exception(exception, input)
98
+ end
99
+
100
+ def handle_exception(e, input)
101
+ PivotProcess.logger.error(e.inspect + "\n" + e.backtrace.join("\n"))
102
+ if @exception_notifier
103
+ @exception_notifier.notify_or_ignore(e, cgi_data: ENV.to_hash, parameters: input)
104
+ end
105
+ end
106
+ end
107
+
48
108
  class ServiceSpawner
49
109
  def self.spawn(service, messenger, exception_notifier)
50
110
  new(service, messenger, exception_notifier).spawn
@@ -100,7 +160,7 @@ module Salemove
100
160
  end
101
161
 
102
162
  def delegate_to_service(input)
103
- result = benchmark(input) { @service.call(input) }
163
+ result = PivotProcess.benchmark(input) { @service.call(input) }
104
164
  PivotProcess.logger.info "Result: #{result.inspect}"
105
165
  result
106
166
  end
@@ -112,19 +172,6 @@ module Salemove
112
172
  end
113
173
  { success: false, error: e.message }
114
174
  end
115
-
116
- def benchmark(input, &block)
117
- type = input[:type] if input.is_a?(Hash)
118
- result = nil
119
-
120
- bm = Benchmark.measure { result = block.call }
121
- if defined?(Logasm) && PivotProcess.logger.is_a?(Logasm)
122
- PivotProcess.logger.debug "Execution time",
123
- type: type, real: bm.real, user: bm.utime, system: bm.stime
124
- end
125
-
126
- result
127
- end
128
175
  end
129
176
  end
130
177
  end
@@ -1,5 +1,5 @@
1
1
  module Salemove
2
2
  module ProcessHandler
3
- VERSION = '0.1.1'
3
+ VERSION = '0.1.3'
4
4
  end
5
5
  end
@@ -2,27 +2,15 @@ require 'logasm'
2
2
  require 'spec_helper'
3
3
  require 'salemove/process_handler/pivot_process'
4
4
 
5
- class ResultService
6
- QUEUE = 'Dummy'
7
- end
8
-
9
5
  describe ProcessHandler::PivotProcess do
10
6
  let(:monitor) { double('Monitor') }
11
7
  let(:messenger) { double('Messenger') }
12
8
  let(:handler) { double('Handler') }
13
9
  let(:thread) { double('Thread') }
14
-
15
- subject { process.spawn(service) }
16
- let(:service) { ResultService.new }
17
-
18
10
  let(:process) { ProcessHandler::PivotProcess.new(messenger, process_params) }
19
11
  let(:process_params) {{ process_monitor: monitor , notifier_factory: notifier_factory, env: 'test' }}
20
12
  let(:notifier_factory) { double('NotifierFactory') }
21
13
  let(:responder) { double(shutdown: true, join: true) }
22
-
23
- let(:input) {{}}
24
- let(:result) { {success: true, result: 'RESULT'} }
25
-
26
14
  let(:logger) { Logasm.new([]) }
27
15
 
28
16
  def expect_monitor_to_behave
@@ -31,155 +19,244 @@ describe ProcessHandler::PivotProcess do
31
19
  expect(monitor).to receive(:shutdown)
32
20
  end
33
21
 
34
- def expect_message
35
- expect(messenger).to receive(:respond_to) {|destination, &callback|
36
- callback.call(input, handler)
37
- }.and_return(responder)
38
- end
39
-
40
- def expect_handler_thread_to_behave
41
- allow(handler).to receive(:success) { thread }
42
- allow(handler).to receive(:error) { thread }
43
- expect(responder).to receive(:shutdown)
44
- expect(responder).to receive(:join)
45
- end
46
-
47
22
  before do
48
23
  ProcessHandler::PivotProcess.logger = logger
49
24
  allow(notifier_factory).to receive(:get_notifier) { nil }
50
25
  expect_monitor_to_behave
51
- expect_message
52
- expect_handler_thread_to_behave
53
- allow(service).to receive(:call).with(input) { result }
54
26
  end
55
27
 
56
- describe 'when service responds correctly' do
57
28
 
58
- it 'can be executed with logger' do
59
- expect(handler).to receive(:success).with(result)
60
- expect(service).to receive(:call).with(input)
61
- subject()
29
+ describe 'responding services' do
30
+ class ResultService
31
+ QUEUE = 'Dummy'
62
32
  end
63
33
 
64
- end
34
+ subject { process.spawn(service) }
35
+ let(:service) { ResultService.new }
65
36
 
66
- describe 'when service responds with an error' do
67
- let(:result) { { success: false, error: 'hey' } }
37
+ let(:input) {{}}
38
+ let(:result) { {success: true, result: 'RESULT'} }
68
39
 
69
- before do
70
- expect(service).to receive(:call).with(input) { result }
40
+ def expect_handler_thread_to_behave
41
+ allow(handler).to receive(:success) { thread }
42
+ allow(handler).to receive(:error) { thread }
43
+ expect(responder).to receive(:shutdown)
44
+ expect(responder).to receive(:join)
71
45
  end
72
46
 
73
- it 'acks the message properly' do
74
- expect(handler).to receive(:error).with(result)
75
- subject()
76
- end
77
- end
78
47
 
79
- shared_examples 'an error_handler' do
48
+ def expect_message
49
+ expect(messenger).to receive(:respond_to) {|destination, &callback|
50
+ callback.call(input, handler)
51
+ }.and_return(responder)
52
+ end
80
53
 
81
- it 'logs error' do
82
- expect(logger).to receive(:error)
83
- subject()
54
+ before do
55
+ expect_message
56
+ expect_handler_thread_to_behave
57
+ allow(service).to receive(:call).with(input) { result }
84
58
  end
85
59
 
86
- describe 'with exception_notifier' do
60
+ describe 'when service responds correctly' do
87
61
 
88
- let(:exception_notifier) { double('Airbrake') }
62
+ it 'can be executed with logger' do
63
+ expect(handler).to receive(:success).with(result)
64
+ expect(service).to receive(:call).with(input)
65
+ subject()
66
+ end
67
+
68
+ end
69
+
70
+ describe 'when service responds with an error' do
71
+ let(:result) { { success: false, error: 'hey' } }
89
72
 
90
73
  before do
91
- allow(notifier_factory).to receive(:get_notifier) { exception_notifier }
74
+ expect(service).to receive(:call).with(input) { result }
92
75
  end
93
76
 
94
- it 'triggers exception_notifier' do
95
- expect(exception_notifier).to receive(:notify_or_ignore)
77
+ it 'acks the message properly' do
78
+ expect(handler).to receive(:error).with(result)
96
79
  subject()
97
80
  end
98
81
  end
99
82
 
100
- end
83
+ shared_examples 'an error_handler' do
101
84
 
102
- describe 'when service raises exception' do
85
+ it 'logs error' do
86
+ expect(logger).to receive(:error)
87
+ subject()
88
+ end
103
89
 
104
- let(:result) { { success: false, error: exception } }
105
- let(:exception) { "what an unexpected exception!" }
90
+ describe 'with exception_notifier' do
106
91
 
107
- before do
108
- expect(service).to receive(:call).with(input) { raise exception }
109
- end
92
+ let(:exception_notifier) { double('Airbrake') }
110
93
 
111
- it 'acks the message properly' do
112
- expect(handler).to receive(:error).with(result)
113
- subject()
114
- end
94
+ before do
95
+ allow(notifier_factory).to receive(:get_notifier) { exception_notifier }
96
+ end
115
97
 
116
- it_behaves_like 'an error_handler'
98
+ it 'triggers exception_notifier' do
99
+ expect(exception_notifier).to receive(:notify_or_ignore)
100
+ subject()
101
+ end
102
+ end
117
103
 
118
- end
104
+ end
119
105
 
120
- describe 'when exception raises after service call' do
106
+ describe 'when service raises exception' do
121
107
 
122
- let(:result) { { success: false, output: exception } }
123
- let(:exception) { "no no no ... no inspect for you!" }
108
+ let(:result) { { success: false, error: exception } }
109
+ let(:exception) { "what an unexpected exception!" }
124
110
 
125
- before do
126
- expect(result).to receive(:inspect) { raise exception }
127
- end
111
+ before do
112
+ expect(service).to receive(:call).with(input) { raise exception }
113
+ end
128
114
 
129
- it 'still acks the message properly' do
130
- subject()
131
- end
115
+ it 'acks the message properly' do
116
+ expect(handler).to receive(:error).with(result)
117
+ subject()
118
+ end
132
119
 
133
- it_behaves_like 'an error_handler'
120
+ it_behaves_like 'an error_handler'
134
121
 
135
- end
122
+ end
136
123
 
137
- describe 'when result is fulfillable' do
138
- let(:result) { double }
124
+ describe 'when exception raises after service call' do
139
125
 
140
- context 'and its already fulfilled' do
141
- let(:value) { { success: true, output: { result: 'R'} } }
126
+ let(:result) { { success: false, output: exception } }
127
+ let(:exception) { "no no no ... no inspect for you!" }
142
128
 
143
129
  before do
144
- allow(result).to receive(:fulfilled?) { true }
145
- allow(result).to receive(:value) { value }
130
+ expect(result).to receive(:inspect) { raise exception }
146
131
  end
147
132
 
148
- it 'responds immediately' do
149
- expect(handler).to receive(:success).with(value)
133
+ it 'still acks the message properly' do
150
134
  subject()
151
135
  end
136
+
137
+ it_behaves_like 'an error_handler'
138
+
152
139
  end
153
140
 
154
- context 'and its fulfilled later' do
155
- let(:value) { { success: true, output: { result: 'R'} } }
141
+ describe 'when result is fulfillable' do
142
+ let(:result) { double }
156
143
 
157
- before do
158
- allow(result).to receive(:fulfilled?) { false }
159
- Thread.new do
160
- sleep 0.005
144
+ context 'and its already fulfilled' do
145
+ let(:value) { { success: true, output: { result: 'R'} } }
146
+
147
+ before do
161
148
  allow(result).to receive(:fulfilled?) { true }
162
149
  allow(result).to receive(:value) { value }
163
150
  end
151
+
152
+ it 'responds immediately' do
153
+ expect(handler).to receive(:success).with(value)
154
+ subject()
155
+ end
156
+ end
157
+
158
+ context 'and its fulfilled later' do
159
+ let(:value) { { success: true, output: { result: 'R'} } }
160
+
161
+ before do
162
+ allow(result).to receive(:fulfilled?) { false }
163
+ Thread.new do
164
+ sleep 0.005
165
+ allow(result).to receive(:fulfilled?) { true }
166
+ allow(result).to receive(:value) { value }
167
+ end
168
+ end
169
+
170
+ it 'responds when fulfilled' do
171
+ expect(handler).to receive(:success).with(value)
172
+ subject()
173
+ end
174
+ end
175
+
176
+ context 'and its never fulfilled' do
177
+ before do
178
+ allow(result).to receive(:fulfilled?) { false }
179
+ allow(result).to receive(:timeout) { 0.001 }
180
+ end
181
+
182
+ it 'responds with timeout error' do
183
+ expect(handler).to receive(:error).with(success: false, error: "Fulfillable response was not fulfilled")
184
+ subject
185
+ end
164
186
  end
187
+ end
188
+ end
165
189
 
166
- it 'responds when fulfilled' do
167
- expect(handler).to receive(:success).with(value)
190
+ describe 'tapping services' do
191
+ class TappingService
192
+ TAPPED_QUEUES = [
193
+ 'one',
194
+ 'two'
195
+ ]
196
+ end
197
+
198
+ subject { process.spawn(service) }
199
+ let(:service) { TappingService.new }
200
+ let(:tap_count) { TappingService::TAPPED_QUEUES.count }
201
+
202
+ let(:input) {{}}
203
+
204
+ def expect_tap_into
205
+ expect(messenger).to receive(:tap_into) do |destination, &callback|
206
+ callback.call(input)
207
+ end
208
+ .exactly(tap_count).times
209
+ .and_return(responder)
210
+ end
211
+
212
+ before do
213
+ expect_tap_into
214
+ expect(responder).to receive(:shutdown)
215
+ expect(responder).to receive(:join)
216
+ allow(service).to receive(:call).with(input)
217
+ end
218
+
219
+
220
+ describe 'when service handles the input correctly' do
221
+ it 'can be executed' do
222
+ expect(service).to receive(:call).with(input.merge(type: 'one'))
223
+ expect(service).to receive(:call).with(input.merge(type: 'two'))
168
224
  subject()
169
225
  end
170
226
  end
171
227
 
172
- context 'and its never fulfilled' do
173
- before do
174
- allow(result).to receive(:fulfilled?) { false }
175
- allow(result).to receive(:timeout) { 0.001 }
228
+ shared_examples 'an error_handler' do
229
+ it 'logs error' do
230
+ expect(logger).to receive(:error)
231
+ subject()
176
232
  end
177
233
 
178
- it 'responds with timeout error' do
179
- expect(handler).to receive(:error).with(success: false, error: "Fulfillable response was not fulfilled")
180
- subject
234
+ describe 'with exception_notifier' do
235
+
236
+ let(:exception_notifier) { double('Airbrake') }
237
+
238
+ before do
239
+ allow(notifier_factory).to receive(:get_notifier) { exception_notifier }
240
+ end
241
+
242
+ it 'triggers exception_notifier' do
243
+ expect(exception_notifier).to receive(:notify_or_ignore)
244
+ subject()
245
+ end
181
246
  end
247
+
182
248
  end
183
- end
184
249
 
250
+ describe 'when service raises exception' do
251
+ let(:exception) { "what an unexpected exception!" }
252
+
253
+ before do
254
+ expect(service).to receive(:call).with(input.merge(type: 'one')) {}
255
+ expect(service).to receive(:call).with(input.merge(type: 'two')) { raise exception }
256
+ end
257
+
258
+ it_behaves_like 'an error_handler'
259
+ end
260
+
261
+ end
185
262
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: process_handler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Indrek Juhkam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-12 00:00:00.000000000 Z
11
+ date: 2015-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: airbrake