process_handler 0.1.1 → 0.1.3

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