rbzmq 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +165 -0
- data/README.md +70 -0
- data/doc/file.README.html +125 -0
- data/lib/rbzmq.rb +11 -0
- data/lib/rbzmq/context.rb +40 -0
- data/lib/rbzmq/errors.rb +45 -0
- data/lib/rbzmq/message.rb +41 -0
- data/lib/rbzmq/poller.rb +198 -0
- data/lib/rbzmq/socket.rb +287 -0
- data/lib/rbzmq/version.rb +14 -0
- data/rbzmq.gemspec +24 -0
- data/spec/functional/rbzmq/poller_spec.rb +95 -0
- data/spec/functional/rbzmq/socket_spec.rb +40 -0
- data/spec/rbzmq/socket_spec.rb +255 -0
- data/spec/scripts/test.rb +5 -0
- data/spec/spec_helper.rb +27 -0
- metadata +94 -0
data/rbzmq.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rbzmq/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'rbzmq'
|
8
|
+
spec.version = RbZMQ::VERSION
|
9
|
+
spec.authors = ['Jan Graichen']
|
10
|
+
spec.email = ['jg@altimos.de']
|
11
|
+
spec.summary = %q{An opinionated ruby library wrapping ffi-rzmq for more rubish flair.}
|
12
|
+
spec.description = %q{An opinionated ruby library wrapping ffi-rzmq for more rubish flair.}
|
13
|
+
spec.homepage = 'https://github.com/jgraichen/rbzmq'
|
14
|
+
spec.license = 'LGPLv3'
|
15
|
+
|
16
|
+
spec.files = Dir['**/*'].grep(%r{^((bin|lib|test|spec|features)/|.*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)})
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'ffi-rzmq', '~> 2.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
24
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RbZMQ::Poller do
|
4
|
+
let!(:poller) { RbZMQ::Poller.new }
|
5
|
+
let!(:receiver) do
|
6
|
+
RbZMQ::Socket.new(ZMQ::PULL).tap{|s| s.bind 'inproc://test' }
|
7
|
+
end
|
8
|
+
let!(:sender) do
|
9
|
+
RbZMQ::Socket.new(ZMQ::PUSH).tap{|s| s.connect 'inproc://test' }
|
10
|
+
end
|
11
|
+
let(:delta) { 0.01 }
|
12
|
+
after { receiver.close! }
|
13
|
+
after { sender.close! }
|
14
|
+
|
15
|
+
describe '#register' do
|
16
|
+
let(:action) { -> { poller.register(receiver, ZMQ::POLLIN) } }
|
17
|
+
subject { action }
|
18
|
+
|
19
|
+
it { should change(poller, :size).from(0).to(1) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#deregister' do
|
23
|
+
let(:action) { -> { poller.deregister(receiver, ZMQ::POLLIN) } }
|
24
|
+
subject { action }
|
25
|
+
|
26
|
+
context 'with all events removed' do
|
27
|
+
before { poller.register(receiver, ZMQ::POLLIN) }
|
28
|
+
it { should change(poller, :size).from(1).to(0) }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with events leaving' do
|
32
|
+
before { poller.register(receiver, ZMQ::POLLIN | ZMQ::POLLOUT) }
|
33
|
+
it { should_not change(poller, :size) }
|
34
|
+
it { expect(subject.call).to eq ZMQ::POLLOUT }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#poll' do
|
39
|
+
let!(:start_time) { Time.now.to_f }
|
40
|
+
let(:end_time) { Time.now.to_f }
|
41
|
+
|
42
|
+
context 'without any registered' do
|
43
|
+
it 'should not block' do
|
44
|
+
poller.poll(1_000)
|
45
|
+
expect(end_time).to be_within(delta).of(start_time)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with registered ZMQ socket' do
|
50
|
+
before { poller.register(receiver, ZMQ::POLLIN) }
|
51
|
+
|
52
|
+
it 'should timeout without event' do
|
53
|
+
poller.poll(100)
|
54
|
+
expect(end_time).to be > (start_time + 0.1)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should interrupt on event' do
|
58
|
+
Thread.new do
|
59
|
+
sleep 0.1
|
60
|
+
sender.send 'MSG'
|
61
|
+
end
|
62
|
+
poller.poll(1_000)
|
63
|
+
expect(receiver.recv.to_s).to eq 'MSG'
|
64
|
+
expect(end_time).to be < (start_time + 1.0)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with registered IO' do
|
69
|
+
let(:pipe) { IO.pipe }
|
70
|
+
let(:reader) { pipe[0] }
|
71
|
+
let(:writer) { pipe[1] }
|
72
|
+
before { poller.register(reader, ZMQ::POLLIN) }
|
73
|
+
|
74
|
+
it 'should timeout without event' do
|
75
|
+
ret = poller.poll(100)
|
76
|
+
expect(ret).to be_a Enumerator
|
77
|
+
expect(ret).to have(0).items
|
78
|
+
expect(end_time).to be > (start_time + 0.1)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should interrupt on event' do
|
82
|
+
Thread.new do
|
83
|
+
sleep 0.1
|
84
|
+
writer.write 'MSG'
|
85
|
+
writer.close
|
86
|
+
end
|
87
|
+
ret = poller.poll(1_000)
|
88
|
+
expect(end_time).to be < (start_time + 1.0)
|
89
|
+
expect(ret).to be_a Enumerator
|
90
|
+
expect(ret).to have(1).item
|
91
|
+
expect(reader.read_nonblock(4096)).to eq 'MSG'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RbZMQ::Socket do
|
4
|
+
let!(:server) do
|
5
|
+
described_class.new(ZMQ::PULL).tap{|s| s.bind 'inproc://test' }
|
6
|
+
end
|
7
|
+
after { server.close! }
|
8
|
+
|
9
|
+
let!(:socket) { described_class.new(ZMQ::PUSH) }
|
10
|
+
after { socket.close! }
|
11
|
+
|
12
|
+
describe 'recv' do
|
13
|
+
describe 'timeout' do
|
14
|
+
before do
|
15
|
+
socket.connect 'inproc://test'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should poll messages' do
|
19
|
+
Thread.new do
|
20
|
+
sleep 0.2
|
21
|
+
socket.send 'TEST'
|
22
|
+
end
|
23
|
+
|
24
|
+
start = Time.now
|
25
|
+
str = server.recv(timeout: 1000).to_s
|
26
|
+
|
27
|
+
expect(Time.now - start).to be < 1
|
28
|
+
expect(str).to eq 'TEST'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should raise error when timeout is reached' do
|
32
|
+
start = Time.now
|
33
|
+
|
34
|
+
expect{ server.recv(timeout: 1000).to_s }.to raise_error Errno::EAGAIN
|
35
|
+
|
36
|
+
expect(Time.now - start).to be_within(0.1).of(1)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RbZMQ::Socket do
|
4
|
+
let(:socket) { RbZMQ::Socket.new ZMQ::ROUTER }
|
5
|
+
|
6
|
+
describe '#initialize' do
|
7
|
+
let(:opts) { Hash.new }
|
8
|
+
let(:socket) { RbZMQ::Socket.new ZMQ::ROUTER, opts }
|
9
|
+
|
10
|
+
context 'opts: ctx' do
|
11
|
+
let(:opts) { {ctx: ctx} }
|
12
|
+
|
13
|
+
context 'with valid ctx' do
|
14
|
+
subject { socket }
|
15
|
+
|
16
|
+
context 'with ZMQ::Context' do
|
17
|
+
let(:ctx) { ZMQ::Context.new }
|
18
|
+
it { expect(socket.zmq_ctx).to eq ctx.pointer }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with RbZMQ::Context' do
|
22
|
+
let(:ctx) { RbZMQ::Context.new }
|
23
|
+
it { expect(socket.zmq_ctx).to eq ctx.pointer }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with invalid object' do
|
28
|
+
subject { ->{ socket } }
|
29
|
+
|
30
|
+
context 'with String' do
|
31
|
+
let(:ctx) { '' }
|
32
|
+
it { should raise_error ArgumentError }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with ZMQ raising an error' do
|
38
|
+
let(:zmq_err) { ZMQ::ZeroMQError.new('zmq_source', 42, 21, 'abc MSG') }
|
39
|
+
before do
|
40
|
+
expect(ZMQ::Socket).to receive(:new).and_raise zmq_err
|
41
|
+
end
|
42
|
+
|
43
|
+
subject do
|
44
|
+
begin
|
45
|
+
socket
|
46
|
+
rescue => err
|
47
|
+
err
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it { should be_a RbZMQ::ZMQError }
|
52
|
+
its(:rc) { should eq 42 }
|
53
|
+
its(:result_code) { should eq 42 }
|
54
|
+
its(:errno) { should eq 21 }
|
55
|
+
its(:error_code) { should eq 21 }
|
56
|
+
its(:message) { should eq 'abc MSG' }
|
57
|
+
its(:to_s) { should eq '[ERRNO 21, RC 42] abc MSG' }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#close' do
|
62
|
+
subject { socket.close }
|
63
|
+
|
64
|
+
it 'should successfully call #close on ZMQ socket' do
|
65
|
+
expect(socket.zmq_socket).to receive(:close).and_return(0)
|
66
|
+
expect(subject).to eq true
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should return false on failure' do
|
70
|
+
expect(socket.zmq_socket).to receive(:close).and_return(-1)
|
71
|
+
should eq false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#close!' do
|
76
|
+
subject { socket.close! }
|
77
|
+
|
78
|
+
it 'should successfully call #close on ZMQ socket' do
|
79
|
+
expect(socket.zmq_socket).to receive(:close)
|
80
|
+
expect(subject).to eq true
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should raise error on failure' do
|
84
|
+
expect(socket.zmq_socket).to receive(:close).and_return(-1)
|
85
|
+
expect { subject }.to raise_error RbZMQ::ZMQError
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#bind' do
|
90
|
+
subject { socket.bind 'tcp://127.0.0.1:5555' }
|
91
|
+
|
92
|
+
it 'should successfully call #bind on ZMQ socket' do
|
93
|
+
expect(socket.zmq_socket).to receive(:bind)
|
94
|
+
.with('tcp://127.0.0.1:5555').and_return(0)
|
95
|
+
expect(subject).to eq socket
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should raise error on failure' do
|
99
|
+
expect(socket.zmq_socket).to receive(:bind).and_return(-1)
|
100
|
+
expect { subject }.to raise_error RbZMQ::ZMQError
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#connect' do
|
105
|
+
subject { socket.connect 'tcp://127.0.0.1:5555' }
|
106
|
+
|
107
|
+
it 'should successfully call #connect on ZMQ socket' do
|
108
|
+
expect(socket.zmq_socket).to receive(:connect)
|
109
|
+
.with('tcp://127.0.0.1:5555').and_return(0)
|
110
|
+
expect(subject).to eq socket
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should raise error on failure' do
|
114
|
+
expect(socket.zmq_socket).to receive(:connect).and_return(-1)
|
115
|
+
expect { subject }.to raise_error RbZMQ::ZMQError
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#send' do
|
120
|
+
context 'with single message' do
|
121
|
+
let(:dup) { double('ZMQMSG') }
|
122
|
+
let(:msg) { RbZMQ::Message.new }
|
123
|
+
let(:args) { [msg, 42] }
|
124
|
+
subject { socket.send(*args) }
|
125
|
+
before { allow(msg).to receive(:to_zmq).and_return(dup) }
|
126
|
+
|
127
|
+
it 'should successfully call #sendmsg on ZMQ socket' do
|
128
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
129
|
+
.with(dup, 42).and_return(0)
|
130
|
+
expect(subject).to eq socket
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should raise error on failure' do
|
134
|
+
expect(socket.zmq_socket).to receive(:sendmsg).and_return(-1)
|
135
|
+
expect { subject }.to raise_error RbZMQ::ZMQError
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'with :block option' do
|
139
|
+
let(:args) { [msg, {block: false}] }
|
140
|
+
|
141
|
+
it 'should set ZMQ::DONTWAIT flag' do
|
142
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
143
|
+
.with(dup, ZMQ::DONTWAIT).and_return(0)
|
144
|
+
expect(subject).to eq socket
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'with :more option' do
|
149
|
+
let(:args) { [msg, {more: true}] }
|
150
|
+
|
151
|
+
it 'should set ZMQ::SNDMORE flag' do
|
152
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
153
|
+
.with(dup, ZMQ::SNDMORE).and_return(0)
|
154
|
+
expect(subject).to eq socket
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'with multiple messages' do
|
160
|
+
let(:dup1) { double('msg1') }
|
161
|
+
let(:dup2) { double('msg2') }
|
162
|
+
let(:msg1) { RbZMQ::Message.new }
|
163
|
+
let(:msg2) { RbZMQ::Message.new }
|
164
|
+
let(:args) { [[msg1, msg2]] }
|
165
|
+
subject { socket.send(*args) }
|
166
|
+
before { allow(msg1).to receive(:to_zmq).and_return(dup1) }
|
167
|
+
before { allow(msg2).to receive(:to_zmq).and_return(dup2) }
|
168
|
+
|
169
|
+
it 'should successfully call #sendmsg on ZMQ socket' do
|
170
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
171
|
+
.with(dup1, ZMQ::SNDMORE).ordered.and_return(0)
|
172
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
173
|
+
.with(dup2, 0).ordered.and_return(0)
|
174
|
+
expect(subject).to eq socket
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should raise error on failure' do
|
178
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
179
|
+
.with(dup1, ZMQ::SNDMORE).ordered.and_return(-1)
|
180
|
+
expect { subject }.to raise_error RbZMQ::ZMQError
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'with :block option' do
|
184
|
+
let(:args) { super() + [{block: false}] }
|
185
|
+
|
186
|
+
it 'should successfully call #sendmsg on ZMQ socket' do
|
187
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
188
|
+
.with(dup1, ZMQ::SNDMORE | ZMQ::DONTWAIT).ordered.and_return(0)
|
189
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
190
|
+
.with(dup2, ZMQ::DONTWAIT).ordered.and_return(0)
|
191
|
+
expect(subject).to eq socket
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'with :more option' do
|
196
|
+
let(:args) { super() + [{more: true}] }
|
197
|
+
|
198
|
+
it 'should successfully call #sendmsg on ZMQ socket' do
|
199
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
200
|
+
.with(dup1, ZMQ::SNDMORE).ordered.and_return(0)
|
201
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
202
|
+
.with(dup2, ZMQ::SNDMORE).ordered.and_return(0)
|
203
|
+
expect(subject).to eq socket
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'with single string' do
|
209
|
+
subject { socket.send 'abc', 42 }
|
210
|
+
|
211
|
+
it 'should successfully call #send_string on ZMQ socket' do
|
212
|
+
expect(socket.zmq_socket).to receive(:sendmsg)
|
213
|
+
.with(kind_of(ZMQ::Message), 42).and_return(0)
|
214
|
+
expect(subject).to eq socket
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should raise error on failure' do
|
218
|
+
expect(socket.zmq_socket).to receive(:sendmsg).and_return(-1)
|
219
|
+
expect { subject }.to raise_error RbZMQ::ZMQError
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context 'with multiple strings' do
|
224
|
+
subject { socket.send %w(abc cde), 0 }
|
225
|
+
|
226
|
+
it 'should successfully call #send_strings on ZMQ socket' do
|
227
|
+
expect(socket.zmq_socket).to receive(:sendmsg).ordered
|
228
|
+
.with(kind_of(ZMQ::Message), ZMQ::SNDMORE).and_return(0)
|
229
|
+
expect(socket.zmq_socket).to receive(:sendmsg).ordered
|
230
|
+
.with(kind_of(ZMQ::Message), 0).and_return(0)
|
231
|
+
expect(subject).to eq socket
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'should raise error on failure' do
|
235
|
+
expect(socket.zmq_socket).to receive(:sendmsg).and_return(-1)
|
236
|
+
expect { subject }.to raise_error RbZMQ::ZMQError
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe '#setsockopt' do
|
242
|
+
subject { socket.setsockopt(ZMQ::SUBSCRIBE, 'ABC') }
|
243
|
+
|
244
|
+
it 'should call #setsockopt on ZMQ socket' do
|
245
|
+
expect(socket.zmq_socket).to receive(:setsockopt)
|
246
|
+
.with(ZMQ::SUBSCRIBE, 'ABC').and_return(0)
|
247
|
+
expect(subject).to eq true
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should return false on failure' do
|
251
|
+
expect(socket.zmq_socket).to receive(:setsockopt).and_return(-1)
|
252
|
+
expect(subject).to eq false
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require :default, :test
|
3
|
+
|
4
|
+
if ENV['CI'] || (defined?(:RUBY_ENGINE) && RUBY_ENGINE != 'rbx')
|
5
|
+
require 'coveralls'
|
6
|
+
Coveralls.wear! do
|
7
|
+
add_filter 'spec'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'rbzmq'
|
12
|
+
|
13
|
+
Dir[File.expand_path('spec/support/**/*.rb')].each {|f| require f}
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.order = 'random'
|
17
|
+
|
18
|
+
config.around do |example|
|
19
|
+
begin
|
20
|
+
Timeout.timeout(30) do
|
21
|
+
example.call
|
22
|
+
end
|
23
|
+
rescue
|
24
|
+
raise Timeout::Error.new 'Spec exceeded maximum execution time'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|