gilmour 0.3.4 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,274 @@
1
+ # encoding: utf-8
2
+ require 'rspec/given'
3
+ require 'securerandom'
4
+ require './testservice/test_service_base'
5
+
6
+ require_relative 'helpers/common'
7
+ require_relative 'helpers/connection'
8
+
9
+ def install_test_subscriber
10
+ waiter = Waiter.new
11
+ TestReplier.callback do |topic, data|
12
+ @topic = topic
13
+ @data = data
14
+ waiter.signal
15
+ end
16
+ waiter
17
+ end
18
+
19
+ describe 'TestReplier' do
20
+ opts = redis_connection_options
21
+ opts[:broadcast_errors] = true
22
+
23
+ test_subscriber_path = './testservice/subscribers/test_reply'
24
+ after(:all) do
25
+ EM.stop
26
+ end
27
+ Given(:subscriber) { TestServiceBase }
28
+ Given do
29
+ subscriber.load_subscriber(test_subscriber_path)
30
+ subscriber.subscribers.each do |topic, arr|
31
+ arr.each do |s|
32
+ s[:fork] = true
33
+ end
34
+ end
35
+ end
36
+ Then do
37
+ handlers = subscriber.subscribers(TestReplier::Topic)
38
+ module_present = handlers.find { |h| h[:subscriber] == TestReplier }
39
+ module_present.should be_truthy
40
+ end
41
+
42
+ context 'Running Service' do
43
+ before(:all) do
44
+ @service = TestServiceBase.new(opts, 'redis')
45
+ end
46
+ Given(:connection_opts) { opts }
47
+ before(:all) do
48
+ @service.registered_subscribers.each do |s|
49
+ s.backend = 'redis'
50
+ end
51
+ @service.start
52
+ end
53
+
54
+ context 'Handler Register' do
55
+ When { install_test_subscriber }
56
+ context 'Check registered handlers' do
57
+ When(:handlers) do
58
+ subscriber.subscribers(TestReplier::Simulation)
59
+ .map { |h| h[:handler] }
60
+ end
61
+ Then { handlers.each { |h| h.should be_kind_of Proc } }
62
+ Then do
63
+ arg1 = TestReplier::Simulation
64
+ handlers.each do |h|
65
+ arg2 = SecureRandom.hex
66
+ # simualate a handler call
67
+ h.call(arg1, arg2)
68
+ @topic.should be == arg1
69
+ @data.should be == arg2
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'Publisher side timeout' do
76
+ Given(:ping_opts) do
77
+ redis_ping_options
78
+ end
79
+
80
+ When(:sub) do
81
+ Gilmour::RedisBackend.new({})
82
+ end
83
+ When(:code) do
84
+ code = nil
85
+ waiter_code = Waiter.new
86
+
87
+ sub.request!(3, TestReplier::TimeoutTopic, {:timeout => 1}) do |d, c|
88
+ code = c
89
+ waiter_code.signal
90
+ end
91
+
92
+ waiter_code.wait(5)
93
+ code
94
+ end
95
+ Then do
96
+ code.should be == 499
97
+ end
98
+ end
99
+
100
+ context 'Handler sleeps longer than the Timeout' do
101
+ Given(:ping_opts) do
102
+ redis_ping_options
103
+ end
104
+
105
+ When(:sub) do
106
+ Gilmour::RedisBackend.new({})
107
+ end
108
+ When(:code) do
109
+ waiter_error = Waiter.new
110
+ waiter_code = Waiter.new
111
+ code = nil
112
+
113
+ backend = @service.get_backend("redis")
114
+ backend.broadcast_errors = true
115
+ sub.slot Gilmour::ErrorChannel do
116
+ waiter_error.signal
117
+ end
118
+
119
+ sub.request!(3, TestReplier::TimeoutTopic) do |d, c|
120
+ code = c
121
+ waiter_code.signal
122
+ end
123
+
124
+ waiter_code.wait(5)
125
+ waiter_error.wait(5)
126
+
127
+ backend.broadcast_errors = false
128
+ code
129
+ end
130
+ Then do
131
+ code.should be == 504
132
+ end
133
+ end
134
+
135
+ context 'Handler sleeps just enough to survive the timeout' do
136
+ Given(:ping_opts) do
137
+ redis_ping_options
138
+ end
139
+
140
+ When(:sub) do
141
+ Gilmour::RedisBackend.new({})
142
+ end
143
+ When(:code) do
144
+ waiter = Waiter.new
145
+ code = nil
146
+
147
+ sub.request!(1, TestReplier::TimeoutTopic) do |d, c|
148
+ code = c
149
+ waiter.signal
150
+ end
151
+
152
+ waiter.wait(5)
153
+ code
154
+ end
155
+ Then do
156
+ code.should be == 200
157
+ end
158
+ end
159
+
160
+
161
+ context 'Request! sans subscriber error' do
162
+ Given(:ping_opts) do
163
+ redis_ping_options
164
+ end
165
+
166
+ When(:sub) do
167
+ Gilmour::RedisBackend.new({})
168
+ end
169
+ When(:response) do
170
+ waiter = Waiter.new
171
+ data = code = nil
172
+ sub.request!(ping_opts[:message], "hello.world") do |d, c|
173
+ data = d
174
+ code = c
175
+ waiter.signal
176
+ end
177
+ waiter.wait(5)
178
+ [data, code]
179
+ end
180
+ Then do
181
+ data, code = response
182
+ data.should be == nil
183
+ code.should be == 404
184
+ end
185
+
186
+ end
187
+
188
+ context 'Send and receive a message' do
189
+ Given(:ping_opts) { redis_ping_options }
190
+ When(:sub) do
191
+ Gilmour::RedisBackend.new({})
192
+ end
193
+ When(:response) do
194
+ waiter = Waiter.new
195
+ data = code = nil
196
+ sub.request!(ping_opts[:message], TestReplier::Topic) do |d, c|
197
+ data = d
198
+ code = c
199
+ waiter.signal
200
+ end
201
+ waiter.wait(5)
202
+ [data, code]
203
+ end
204
+ Then do
205
+ data, code = response
206
+ data.should be == ping_opts[:response]
207
+ code.should be == 200
208
+ end
209
+ end
210
+
211
+ context 'Send once, Receive twice' do
212
+ Given(:ping_opts) { redis_ping_options }
213
+ When(:sub) do
214
+ Gilmour::RedisBackend.new({})
215
+ end
216
+ When (:response) do
217
+ waiter = Waiter.new
218
+
219
+ actual_ret = []
220
+
221
+ group_proc = sub.slot TestReplier::GroupReturn do
222
+ actual_ret.push(request.body)
223
+ waiter.signal if actual_ret.length == 4
224
+ end
225
+
226
+ sub.signal!(ping_opts[:message], TestReplier::GroupTopic)
227
+ waiter.wait(5)
228
+
229
+ sub.remove_slot TestReplier::GroupReturn, group_proc
230
+ actual_ret
231
+ end
232
+ Then do
233
+ response.select { |e| e == ping_opts[:message] }.size.should == 2
234
+ response.select { |e| e == "2" }.size.should == 2
235
+ end
236
+ end
237
+
238
+ context 'Handler to Test exits' do
239
+ Given(:ping_opts) do
240
+ redis_ping_options
241
+ end
242
+
243
+ When(:sub) do
244
+ Gilmour::RedisBackend.new({})
245
+ end
246
+ When(:code) do
247
+ waiter_error = Waiter.new
248
+ waiter_code = Waiter.new
249
+ code = nil
250
+
251
+ #backend = @service.get_backend("redis")
252
+ #backend.broadcast_errors = true
253
+ error_listener_proc = sub.slot Gilmour::ErrorChannel do |d, c|
254
+ waiter_error.signal
255
+ end
256
+
257
+ sub.request!(1, TestReplier::ExitTopic) do |d, c|
258
+ code = c
259
+ waiter_code.signal
260
+ end
261
+
262
+ waiter_code.wait(5)
263
+ waiter_error.wait(5)
264
+
265
+ sub.remove_listener Gilmour::ErrorChannel, error_listener_proc
266
+ #backend.broadcast_errors = false
267
+ code
268
+ end
269
+ Then do
270
+ code.should be == 500
271
+ end
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ require 'rspec/given'
3
+
4
+ require '../lib/gilmour/waiter'
5
+
6
+ describe 'TestAddGroup' do
7
+ context "Wait Group" do
8
+ it "Wait should return instantly" do
9
+ wg = Gilmour::Waiter.new
10
+
11
+ num = 2
12
+ arr = []
13
+
14
+ num.times do
15
+ wg.add 1
16
+ Thread.new {
17
+ arr << "done"
18
+ wg.done
19
+ }
20
+ end
21
+
22
+ wg.wait
23
+ expect(arr.length).to eq num
24
+ end
25
+
26
+ it "Wait should return after sleep" do
27
+ wg = Gilmour::Waiter.new
28
+
29
+ num = 2
30
+ arr = []
31
+
32
+ num.times do
33
+ wg.add 1
34
+ Thread.new {
35
+ sleep(1)
36
+ arr << true
37
+ wg.done
38
+ }
39
+ end
40
+
41
+ wg.wait
42
+ expect(arr.length).to eq num
43
+ end
44
+
45
+ it "Wait should perform a yield" do
46
+ wg = Gilmour::Waiter.new
47
+
48
+ num = 2
49
+ arr = []
50
+
51
+ num.times do
52
+ wg.add 1
53
+ Thread.new {
54
+ sleep(1)
55
+ arr << true
56
+ wg.done
57
+ }
58
+ end
59
+
60
+ wg.wait do
61
+ arr << true
62
+ end
63
+
64
+ expect(arr.length).to eq num+1
65
+ end
66
+
67
+ it "Wait should timeout" do
68
+ wg = Gilmour::Waiter.new
69
+
70
+ num = 2
71
+ arr = []
72
+
73
+ num.times do
74
+ wg.add 1
75
+ Thread.new {
76
+ sleep(2)
77
+ arr << true
78
+ wg.done
79
+ }
80
+ end
81
+
82
+ wg.wait(1) do
83
+ arr << true
84
+ end
85
+
86
+ expect(arr.length).to eq 1
87
+ end
88
+
89
+ it "Done and Wait only" do
90
+ wg = Gilmour::Waiter.new
91
+
92
+ arr = []
93
+
94
+ Thread.new {
95
+ sleep(2)
96
+ arr << true
97
+ wg.done
98
+ }
99
+
100
+ wg.wait do
101
+ arr << true
102
+ end
103
+
104
+ expect(arr.length).to eq 2
105
+ end
106
+
107
+ it "Signal should raise when using add" do
108
+ wg = Gilmour::Waiter.new
109
+ wg.add 1
110
+ expect{wg.signal}.to raise_error(RuntimeError)
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,69 @@
1
+ class TestReplier < TestServiceBase
2
+ TimeoutTopic = "test.reply.timeout"
3
+ Topic = 'test.reply.topic'
4
+ WildcardTopic = 'test.reply.wildcard.*'
5
+ Simulation = 'simulate.reply.topic'
6
+ Republish = 'test.reply.republish'
7
+ GroupReturn = "test.slot.group_return"
8
+ GroupTopic = "test.slot.group"
9
+ ExclusiveTopic = "test.reply.exclusive"
10
+ ExitTopic = "topic.reply.exit"
11
+ ReListenTopic = "topic.reply.relisten"
12
+
13
+
14
+ def self.get_callback
15
+ @callback
16
+ end
17
+
18
+ def self.callback
19
+ @callback = Proc.new
20
+ end
21
+
22
+
23
+ 2.times do
24
+ slot GroupTopic do
25
+ signal!(request.body, TestReplier::GroupReturn)
26
+ signal!("2", TestReplier::GroupReturn)
27
+ end
28
+ end
29
+
30
+ reply_to TimeoutTopic, {timeout: 2} do
31
+ data, _, _ = Gilmour::Protocol.parse_response(request.body)
32
+ logger.info "Will sleep for #{data} seconds now. But allowed timeout is 2."
33
+ sleep data
34
+ respond 'Pong!'
35
+ logger.info "Done with sleep"
36
+ end
37
+
38
+ reply_to ExitTopic do
39
+ logger.info "Sleeping for 2 seconds, and then will exit"
40
+ exit!
41
+ end
42
+
43
+ reply_to ReListenTopic do
44
+ # In forked environment this should not work.
45
+ reply_to "test.world", excl_group: 'relisten' do
46
+ respond "Pong!"
47
+ end
48
+ respond "Pong"
49
+ end
50
+
51
+ reply_to Topic do
52
+ if TestReplier.get_callback
53
+ TestReplier.get_callback.call(request.topic, request.body)
54
+ end
55
+ respond 'Pong!' if request.body == 'Ping!'
56
+ end
57
+
58
+ reply_to Simulation do |topic, data|
59
+ @callback.call(topic, data)
60
+ end
61
+
62
+ reply_to Republish do
63
+ resp = self
64
+ request!(request.body, Topic) do |data, code|
65
+ resp.respond data, 200, now: true
66
+ end
67
+ delay_response
68
+ end
69
+ end
@@ -99,5 +99,6 @@ class TestSubscriber < TestServiceBase
99
99
  publish(request.body, Topic) do |data, code|
100
100
  resp.respond data, 200, now: true
101
101
  end
102
+ delay_response
102
103
  end
103
104
  end
data/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Gilmour
2
- VERSION = '0.3.4'
2
+ VERSION = '0.4.1'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gilmour
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aditya Godbole
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-07-31 00:00:00.000000000 Z
12
+ date: 2015-08-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -109,24 +109,33 @@ files:
109
109
  - Gemfile
110
110
  - LICENSE
111
111
  - README.md
112
+ - examples/composition.rb
113
+ - examples/container.rb
112
114
  - examples/echoclient.rb
113
- - examples/fork_log_server.rb
114
- - examples/server.rb
115
- - examples/thread_example.rb
115
+ - examples/fibonacci.rb
116
+ - examples/forkechoclient.rb
117
+ - examples/signal_slot.rb
118
+ - examples/subscribers/echo.rb
116
119
  - gilmour.gemspec
117
120
  - lib/gilmour.rb
118
121
  - lib/gilmour/backends/backend.rb
119
122
  - lib/gilmour/backends/redis.rb
120
123
  - lib/gilmour/base.rb
124
+ - lib/gilmour/composers.rb
121
125
  - lib/gilmour/protocol.rb
122
126
  - lib/gilmour/responder.rb
123
127
  - lib/gilmour/waiter.rb
124
128
  - test/spec/helpers/common.rb
125
129
  - test/spec/helpers/connection.rb
126
130
  - test/spec/helpers/data.yml
131
+ - test/spec/test_pipelines.rb
127
132
  - test/spec/test_service_base.rb
128
133
  - test/spec/test_subscriber_redis.rb
129
134
  - test/spec/test_subscriber_redis_forked.rb
135
+ - test/spec/test_subscriber_redis_reply_slot.rb
136
+ - test/spec/test_subscriber_redis_reply_slot_fork.rb
137
+ - test/spec/test_waiter.rb
138
+ - test/testservice/subscribers/test_reply.rb
130
139
  - test/testservice/subscribers/test_subscriber.rb
131
140
  - test/testservice/test_service_base.rb
132
141
  - version.rb
@@ -157,8 +166,13 @@ test_files:
157
166
  - test/spec/helpers/common.rb
158
167
  - test/spec/helpers/connection.rb
159
168
  - test/spec/helpers/data.yml
169
+ - test/spec/test_pipelines.rb
160
170
  - test/spec/test_service_base.rb
161
171
  - test/spec/test_subscriber_redis.rb
162
172
  - test/spec/test_subscriber_redis_forked.rb
173
+ - test/spec/test_subscriber_redis_reply_slot.rb
174
+ - test/spec/test_subscriber_redis_reply_slot_fork.rb
175
+ - test/spec/test_waiter.rb
176
+ - test/testservice/subscribers/test_reply.rb
163
177
  - test/testservice/subscribers/test_subscriber.rb
164
178
  - test/testservice/test_service_base.rb
@@ -1,97 +0,0 @@
1
- # encoding: utf-8
2
- require_relative '../lib/gilmour'
3
-
4
- class EventServer
5
- include Gilmour::Base
6
-
7
- def initialize
8
- backend = 'redis'
9
- enable_backend(backend, { })
10
- registered_subscribers.each do |sub|
11
- sub.backend = backend
12
- end
13
- $stderr.puts "Starting server. To see messaging in action run clients."
14
- start(true)
15
- end
16
- end
17
-
18
- class EchoSubscriber < EventServer
19
- class << self
20
- def capture_output(pipe)
21
- streams = [$stdout, $stderr]
22
-
23
- # Save the streams to be reassigned later.
24
- # Actually it doesn't matter because the child process would be killed
25
- # anyway after the work is done.
26
- saved = streams.collect { |stream| stream.dup }
27
-
28
- begin
29
- streams.each_with_index do |stream, ix|
30
- # Probably I should not use IX, otherwise stdout and stderr can arrive
31
- # out of order, which they should?
32
- # If I reopen both of them on the same PIPE, they are guaranteed to
33
- # arrive in order.
34
- stream.reopen(pipe)
35
- #stream.sync = true
36
- end
37
- yield
38
- ensure
39
- # This is sort of meaningless, just makes sense aesthetically.
40
- # To return what was borrowed.
41
- streams.each_with_index do |stream, i|
42
- stream.reopen(saved[i])
43
- end
44
- pipe.close unless pipe.closed?
45
- end
46
- end
47
-
48
- def ds_respond(topic, opts={}, &blk)
49
- options = { exclusive: true, fork: true }.merge(opts)
50
- listen_to topic, options do
51
- logger.error "Captuting output before execution"
52
-
53
- waiter = Gilmour::Waiter.new
54
- waiter.add 1
55
- read_pipe, write_pipe = IO.pipe
56
-
57
- th = Thread.new {
58
- loop {
59
- begin
60
- result = read_pipe.readline.chomp
61
- logger.debug result
62
- rescue EOFError
63
- waiter.done
64
- rescue Exception => e
65
- logger.error "Error: #{e.message}"
66
- logger.error "Traceback: #{e.backtrace}"
67
- end
68
- }
69
- }
70
-
71
- EchoSubscriber::capture_output(write_pipe) do
72
- instance_eval(&blk)
73
- end
74
-
75
- waiter.wait do
76
- th.kill
77
- end
78
-
79
- end
80
- end
81
- end
82
-
83
- # Passing second parameter as true makes only one instance of this handler handle a request
84
- EchoSubscriber::ds_respond 'echo.*' do
85
- if request.body == 'Palmolive'
86
- respond nil
87
- else
88
- logger.error "logger: #{request.body}"
89
- $stderr.puts "stderr.puts: #{request.body}"
90
- puts "stdout.puts #{request.body}"
91
- respond "#{request.topic}"
92
- end
93
- end
94
-
95
- end
96
-
97
- EventServer.new
data/examples/server.rb DELETED
@@ -1,47 +0,0 @@
1
- # encoding: utf-8
2
- require 'gilmour'
3
-
4
- class EventServer
5
- include Gilmour::Base
6
-
7
- def initialize
8
- backend = 'redis'
9
- enable_backend(backend, { })
10
- registered_subscribers.each do |sub|
11
- sub.backend = backend
12
- end
13
- $stderr.puts "Starting server. To see messaging in action run clients."
14
- start(true)
15
- end
16
- end
17
-
18
- class EchoSubscriber < EventServer
19
- # Passing second parameter as true makes only one instance of this handler handle a request
20
- listen_to 'echo.*', {"exclusive" => true} do
21
- if request.body == 'Palmolive'
22
- respond nil
23
- else
24
- $stderr.puts request.body
25
- respond "#{request.topic}"
26
- end
27
- end
28
- end
29
-
30
- class FibonacciSubscriber < EventServer
31
- class << self
32
- attr_accessor :last
33
- end
34
-
35
- listen_to 'fib.next' do
36
- old = FibonacciSubscriber.last
37
- FibonacciSubscriber.last = new = request.body
38
- respond(old + new)
39
- end
40
-
41
- listen_to 'fib.init' do
42
- FibonacciSubscriber.last = request.body
43
- end
44
-
45
- end
46
-
47
- EventServer.new