rbzmq 0.1.0
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 +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
|