gilmour 0.3.4 → 0.4.1

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