marquise 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.
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/Guardfile +12 -0
- data/README.md +110 -0
- data/Rakefile +39 -0
- data/lib/marquise.rb +212 -0
- data/lib/marquise/ffi.rb +65 -0
- data/marquise.gemspec +33 -0
- data/spec/marquise_spec.rb +274 -0
- data/spec/marquise_tell_binary_spec.rb +183 -0
- data/spec/marquise_tell_counter_spec.rb +153 -0
- data/spec/marquise_tell_int_spec.rb +172 -0
- data/spec/marquise_tell_real_spec.rb +161 -0
- data/spec/marquise_tell_spec.rb +27 -0
- data/spec/marquise_tell_text_spec.rb +153 -0
- data/spec/spec_helper.rb +25 -0
- metadata +259 -0
data/marquise.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'git-version-bump'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "marquise"
|
6
|
+
|
7
|
+
s.version = GVB.version
|
8
|
+
s.date = GVB.date
|
9
|
+
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
|
12
|
+
s.homepage = "https://github.com/mpalmer/marquise-ruby"
|
13
|
+
s.summary = "Ruby bindings for the marquise data-point transport "+
|
14
|
+
"for Vaultaire"
|
15
|
+
s.authors = ["Matt Palmer"]
|
16
|
+
|
17
|
+
s.extra_rdoc_files = ["README.md"]
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
|
20
|
+
s.add_runtime_dependency 'ffi', '~> 1.9'
|
21
|
+
s.add_runtime_dependency 'ffi_dry'
|
22
|
+
s.add_runtime_dependency 'git-version-bump', '~> 0.7'
|
23
|
+
|
24
|
+
s.add_development_dependency 'rspec', "~> 2.14"
|
25
|
+
s.add_development_dependency 'rake'
|
26
|
+
s.add_development_dependency 'bundler'
|
27
|
+
s.add_development_dependency 'rdoc'
|
28
|
+
s.add_development_dependency 'guard-spork'
|
29
|
+
s.add_development_dependency 'guard-rspec'
|
30
|
+
s.add_development_dependency 'rb-inotify', '~> 0.9'
|
31
|
+
s.add_development_dependency 'plymouth'
|
32
|
+
s.add_development_dependency 'pry-debugger'
|
33
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
|
+
require 'marquise'
|
4
|
+
|
5
|
+
describe Marquise do
|
6
|
+
describe ".new" do
|
7
|
+
it "bombs out without an argument" do
|
8
|
+
expect { Marquise.new }.to raise_error
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns a Marquise instance when given a zmq URL" do
|
12
|
+
expect(Marquise.new('tcp://localhost:4567')).to be_a(Marquise)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "calls marquise_consumer_new with a zmq URL" do
|
16
|
+
Marquise::FFI.
|
17
|
+
should_receive(:marquise_consumer_new).
|
18
|
+
with('tcp://localhost:4567', 5).
|
19
|
+
and_return('19')
|
20
|
+
|
21
|
+
Marquise.new('tcp://localhost:4567')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises RuntimeError if marquise_consumer_new returns nil" do
|
25
|
+
Marquise::FFI.
|
26
|
+
should_receive(:marquise_consumer_new).
|
27
|
+
with('tcp://localhost:4567', 5).
|
28
|
+
and_return(nil)
|
29
|
+
|
30
|
+
expect { Marquise.new('tcp://localhost:4567') }.
|
31
|
+
to raise_error(RuntimeError, "libmarquise failed; check syslog (no, seriously)")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "passes non-default batch_period if given" do
|
35
|
+
Marquise::FFI.
|
36
|
+
should_receive(:marquise_consumer_new).
|
37
|
+
with('tcp://localhost:4567', 0.05).
|
38
|
+
and_return('38')
|
39
|
+
|
40
|
+
Marquise.new('tcp://localhost:4567', 0.05)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "defines a finalizer to call marquise_consumer_shutdown" do
|
44
|
+
Marquise::FFI.
|
45
|
+
should_receive(:marquise_consumer_new).
|
46
|
+
with('tcp://localhost:4567', 5).
|
47
|
+
and_return('xyzzy')
|
48
|
+
ObjectSpace.
|
49
|
+
should_receive(:define_finalizer).
|
50
|
+
with(instance_of(Marquise), mock_janitor = double('janitor'))
|
51
|
+
Marquise::Janitor.
|
52
|
+
should_receive(:new).
|
53
|
+
with('xyzzy', {}).
|
54
|
+
and_return(mock_janitor)
|
55
|
+
|
56
|
+
x = Marquise.new('tcp://localhost:4567')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ".open" do
|
61
|
+
it "bombs out without an argument" do
|
62
|
+
expect { Marquise.open }.to raise_error
|
63
|
+
end
|
64
|
+
|
65
|
+
it "returns a Marquise instance when given a zmq URL" do
|
66
|
+
expect(Marquise.open('tcp://localhost:4567')).to be_a(Marquise)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "calls marquise_consumer_new with a zmq URL" do
|
70
|
+
Marquise::FFI.
|
71
|
+
should_receive(:marquise_consumer_new).
|
72
|
+
with('tcp://localhost:4567', 5).
|
73
|
+
and_return('81')
|
74
|
+
|
75
|
+
Marquise.open('tcp://localhost:4567')
|
76
|
+
end
|
77
|
+
|
78
|
+
it "raises RuntimeError if marquise_consumer_new returns nil" do
|
79
|
+
Marquise::FFI.
|
80
|
+
should_receive(:marquise_consumer_new).
|
81
|
+
with('tcp://localhost:4567', 5).
|
82
|
+
and_return(nil)
|
83
|
+
|
84
|
+
expect { Marquise.open('tcp://localhost:4567') }.
|
85
|
+
to raise_error(RuntimeError, "libmarquise failed; check syslog (no, seriously)")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "passes non-default batch_period if given" do
|
89
|
+
Marquise::FFI.
|
90
|
+
should_receive(:marquise_consumer_new).
|
91
|
+
with('tcp://localhost:4567', 0.05).
|
92
|
+
and_return('100')
|
93
|
+
|
94
|
+
Marquise.open('tcp://localhost:4567', 0.05)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "bombs out with a block but without an argument" do
|
98
|
+
expect { Marquise.open() { |x| puts x } }.to raise_error
|
99
|
+
end
|
100
|
+
|
101
|
+
it "yields a Marquise instance when given a block" do
|
102
|
+
expect do |b|
|
103
|
+
Marquise.open('tcp://localhost:4567', &b)
|
104
|
+
end.to yield_with_args(Marquise)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#connect" do
|
109
|
+
it "calls marquise_connect" do
|
110
|
+
Marquise::FFI.
|
111
|
+
should_receive(:marquise_consumer_new).
|
112
|
+
with('tcp://localhost:4567', 5).
|
113
|
+
and_return('118')
|
114
|
+
Marquise::FFI.
|
115
|
+
should_receive(:marquise_connect).
|
116
|
+
with('118').
|
117
|
+
and_return('122')
|
118
|
+
|
119
|
+
x = Marquise.open('tcp://localhost:4567').connect
|
120
|
+
# This is just cheating... avoid possibly sending insane data
|
121
|
+
# to libmarquise via calling close functions on GC
|
122
|
+
ObjectSpace.undefine_finalizer(x)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "calls marquise_connect only once per thread" do
|
126
|
+
Marquise::FFI.
|
127
|
+
should_receive(:marquise_consumer_new).
|
128
|
+
with('tcp://localhost:4567', 5).
|
129
|
+
and_return('118')
|
130
|
+
Marquise::FFI.
|
131
|
+
should_receive(:marquise_connect).
|
132
|
+
with('118').
|
133
|
+
and_return('122')
|
134
|
+
|
135
|
+
x = Marquise.open('tcp://localhost:4567')
|
136
|
+
# This is just cheating... avoid possibly sending insane data
|
137
|
+
# to libmarquise via calling close functions on GC
|
138
|
+
ObjectSpace.undefine_finalizer(x)
|
139
|
+
|
140
|
+
10.times { x.connect }
|
141
|
+
end
|
142
|
+
|
143
|
+
it "barfs if marquise_connect fails" do
|
144
|
+
Marquise::FFI.
|
145
|
+
should_receive(:marquise_consumer_new).
|
146
|
+
with('tcp://localhost:4567', 5).
|
147
|
+
and_return('134')
|
148
|
+
Marquise::FFI.
|
149
|
+
should_receive(:marquise_connect).
|
150
|
+
with('134').
|
151
|
+
and_return(nil)
|
152
|
+
|
153
|
+
x = Marquise.open('tcp://localhost:4567')
|
154
|
+
# This is just cheating... avoid possibly sending insane data
|
155
|
+
# to libmarquise via calling close functions on GC
|
156
|
+
ObjectSpace.undefine_finalizer(x)
|
157
|
+
|
158
|
+
expect { x.connect }.
|
159
|
+
to raise_error(
|
160
|
+
RuntimeError,
|
161
|
+
"marquise_connect() failed... consult syslog (no, seriously)"
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "creates separate connections for each thread" do
|
166
|
+
Marquise::FFI.
|
167
|
+
should_receive(:marquise_consumer_new).
|
168
|
+
with('tcp://localhost:4567', 5).
|
169
|
+
and_return('156')
|
170
|
+
Marquise::FFI.
|
171
|
+
should_receive(:marquise_connect).
|
172
|
+
twice.
|
173
|
+
with('156').
|
174
|
+
and_return('161.1', '161.2')
|
175
|
+
|
176
|
+
x = Marquise.open('tcp://localhost:4567')
|
177
|
+
# This is just cheating... avoid possibly sending insane data
|
178
|
+
# to libmarquise via calling close functions on GC
|
179
|
+
ObjectSpace.undefine_finalizer(x)
|
180
|
+
|
181
|
+
th1 = Thread.new { 10.times { x.connect } }
|
182
|
+
th2 = Thread.new { 10.times { x.connect } }
|
183
|
+
|
184
|
+
th1.join
|
185
|
+
th2.join
|
186
|
+
|
187
|
+
# Digging inside the object... how naughty...
|
188
|
+
conns = x.instance_variable_get(:@connections)
|
189
|
+
|
190
|
+
expect(conns[th1]).to_not eq(conns[th2])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#close" do
|
195
|
+
it "calls marquise_consumer_shutdown" do
|
196
|
+
Marquise::FFI.
|
197
|
+
should_receive(:marquise_consumer_new).
|
198
|
+
with('tcp://localhost:4567', 5).
|
199
|
+
and_return('121')
|
200
|
+
Marquise::FFI.
|
201
|
+
should_receive(:marquise_consumer_shutdown).
|
202
|
+
with('121')
|
203
|
+
|
204
|
+
Marquise.open('tcp://localhost:4567').close
|
205
|
+
end
|
206
|
+
|
207
|
+
it "doesn't call marquise_consumer_shutdown more than once" do
|
208
|
+
Marquise::FFI.
|
209
|
+
should_receive(:marquise_consumer_new).
|
210
|
+
with('tcp://localhost:4567', 5).
|
211
|
+
and_return('131')
|
212
|
+
Marquise::FFI.
|
213
|
+
should_receive(:marquise_consumer_shutdown).
|
214
|
+
with('131')
|
215
|
+
|
216
|
+
x = Marquise.open('tcp://localhost:4567').close
|
217
|
+
|
218
|
+
# Creating a huge chunk of junk memory and then releasing it should
|
219
|
+
# cause the GC to cleanup everything, running all finalizers
|
220
|
+
x = 'x' * 100_000_000
|
221
|
+
x = nil
|
222
|
+
GC.start
|
223
|
+
end
|
224
|
+
|
225
|
+
it "calls marquise_close on the connection" do
|
226
|
+
Marquise::FFI.
|
227
|
+
should_receive(:marquise_consumer_new).
|
228
|
+
with('tcp://localhost:4567', 5).
|
229
|
+
and_return('148')
|
230
|
+
Marquise::FFI.
|
231
|
+
should_receive(:marquise_consumer_shutdown).
|
232
|
+
with('148')
|
233
|
+
Marquise::FFI.
|
234
|
+
should_receive(:marquise_connect).
|
235
|
+
with('148').
|
236
|
+
and_return('155')
|
237
|
+
Marquise::FFI.
|
238
|
+
should_receive(:marquise_close).
|
239
|
+
with('155')
|
240
|
+
|
241
|
+
x = Marquise.open('tcp://localhost:4567')
|
242
|
+
x.connect
|
243
|
+
x.close
|
244
|
+
end
|
245
|
+
|
246
|
+
it "calls marquise_close on all connections" do
|
247
|
+
Marquise::FFI.
|
248
|
+
should_receive(:marquise_consumer_new).
|
249
|
+
with('tcp://localhost:4567', 5).
|
250
|
+
and_return('255')
|
251
|
+
Marquise::FFI.
|
252
|
+
should_receive(:marquise_consumer_shutdown).
|
253
|
+
with('255')
|
254
|
+
Marquise::FFI.
|
255
|
+
should_receive(:marquise_connect).
|
256
|
+
with('255').
|
257
|
+
twice.
|
258
|
+
and_return('263.1', '263.2')
|
259
|
+
Marquise::FFI.
|
260
|
+
should_receive(:marquise_close).
|
261
|
+
with('263.1')
|
262
|
+
Marquise::FFI.
|
263
|
+
should_receive(:marquise_close).
|
264
|
+
with('263.2')
|
265
|
+
|
266
|
+
x = Marquise.open('tcp://localhost:4567')
|
267
|
+
|
268
|
+
Thread.new { x.connect }.join
|
269
|
+
Thread.new { x.connect }.join
|
270
|
+
|
271
|
+
x.close
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
require 'marquise'
|
3
|
+
|
4
|
+
describe Marquise do
|
5
|
+
describe "#tell" do
|
6
|
+
let(:marquise) do
|
7
|
+
Marquise::FFI.
|
8
|
+
should_receive(:marquise_consumer_new).
|
9
|
+
with('tcp://localhost:4567', 5).
|
10
|
+
and_return('#tell')
|
11
|
+
Marquise::FFI.
|
12
|
+
should_receive(:marquise_connect).
|
13
|
+
with('#tell').
|
14
|
+
and_return('#tellconn')
|
15
|
+
|
16
|
+
x = Marquise.new('tcp://localhost:4567')
|
17
|
+
# Neuter the Marquise
|
18
|
+
ObjectSpace.undefine_finalizer(x)
|
19
|
+
|
20
|
+
x
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "(binary)" do
|
24
|
+
it "sends a simple binary" do
|
25
|
+
Marquise::FFI.stub(:send) do |*args|
|
26
|
+
expect(args).to be_an(Array)
|
27
|
+
expect(args.length).to eq(8)
|
28
|
+
|
29
|
+
method, conn, src_f, src_v, src_c, data, len, tstamp = args
|
30
|
+
|
31
|
+
expect(method).to eq(:marquise_send_binary)
|
32
|
+
expect(conn).to eq('#tellconn')
|
33
|
+
|
34
|
+
expect(tstamp).
|
35
|
+
to be_within(1_000_000_000).
|
36
|
+
of(Time.now.to_f * 1_000_000_000)
|
37
|
+
|
38
|
+
expect(src_c).to eq(0)
|
39
|
+
expect(src_f).to be(nil)
|
40
|
+
expect(src_v).to be(nil)
|
41
|
+
|
42
|
+
expect(len).to eq("hello world".length)
|
43
|
+
expect(data).to eq("hello world")
|
44
|
+
|
45
|
+
0
|
46
|
+
end
|
47
|
+
|
48
|
+
marquise.tell "hello world".encode('ASCII-8BIT')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "sends a non-UTF-8 string as binary" do
|
52
|
+
Marquise::FFI.stub(:send) do |*args|
|
53
|
+
expect(args).to be_an(Array)
|
54
|
+
expect(args.length).to eq(8)
|
55
|
+
|
56
|
+
method, conn, src_f, src_v, src_c, data, len, tstamp = args
|
57
|
+
|
58
|
+
expect(method).to eq(:marquise_send_binary)
|
59
|
+
expect(conn).to eq('#tellconn')
|
60
|
+
|
61
|
+
expect(tstamp).
|
62
|
+
to be_within(1_000_000_000).
|
63
|
+
of(Time.now.to_f * 1_000_000_000)
|
64
|
+
|
65
|
+
expect(src_c).to eq(0)
|
66
|
+
expect(src_f).to be(nil)
|
67
|
+
expect(src_v).to be(nil)
|
68
|
+
|
69
|
+
expect(len).to eq(1)
|
70
|
+
expect(data).to eq("\xC0")
|
71
|
+
|
72
|
+
0
|
73
|
+
end
|
74
|
+
|
75
|
+
marquise.tell "\xC0"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "sends binary and a timestamp" do
|
79
|
+
t = Time.now - 86400
|
80
|
+
|
81
|
+
Marquise::FFI.stub(:send) do |*args|
|
82
|
+
expect(args).to be_an(Array)
|
83
|
+
expect(args.length).to eq(8)
|
84
|
+
|
85
|
+
method, conn, src_f, src_v, src_c, data, len, tstamp = args
|
86
|
+
|
87
|
+
expect(method).to eq(:marquise_send_binary)
|
88
|
+
expect(conn).to eq('#tellconn')
|
89
|
+
|
90
|
+
expect(tstamp).to eq(t.to_f * 1_000_000_000)
|
91
|
+
|
92
|
+
expect(src_c).to eq(0)
|
93
|
+
expect(src_f).to be(nil)
|
94
|
+
expect(src_v).to be(nil)
|
95
|
+
|
96
|
+
expect(len).to eq("hello world".length)
|
97
|
+
expect(data).to eq("hello world")
|
98
|
+
|
99
|
+
0
|
100
|
+
end
|
101
|
+
|
102
|
+
marquise.tell "hello world".encode('ASCII-8BIT'), t
|
103
|
+
end
|
104
|
+
|
105
|
+
it "sends binary and tags" do
|
106
|
+
Marquise::FFI.stub(:send) do |*args|
|
107
|
+
expect(args).to be_an(Array)
|
108
|
+
expect(args.length).to eq(8)
|
109
|
+
|
110
|
+
method, conn, src_f, src_v, src_c, data, len, tstamp = args
|
111
|
+
|
112
|
+
expect(method).to eq(:marquise_send_binary)
|
113
|
+
expect(conn).to eq('#tellconn')
|
114
|
+
|
115
|
+
expect(tstamp).
|
116
|
+
to be_within(1_000_000_000).
|
117
|
+
of(Time.now.to_f * 1_000_000_000)
|
118
|
+
|
119
|
+
expect(src_c).to eq(2)
|
120
|
+
|
121
|
+
expect(src_f).to be_a(FFI::MemoryPointer)
|
122
|
+
expect(src_f.size).to eq(3*src_f.type_size)
|
123
|
+
expect(src_f[0].read_pointer.read_string).to eq('foo')
|
124
|
+
expect(src_f[1].read_pointer.read_string).to eq('answer')
|
125
|
+
expect(src_f[2].read_pointer.address).to eq(0)
|
126
|
+
|
127
|
+
expect(src_v).to be_a(FFI::MemoryPointer)
|
128
|
+
expect(src_v.size).to eq(3*src_f.type_size)
|
129
|
+
expect(src_v[0].read_pointer.read_string).to eq('bar')
|
130
|
+
expect(src_v[1].read_pointer.read_string).to eq('42')
|
131
|
+
expect(src_v[2].read_pointer.address).to eq(0)
|
132
|
+
|
133
|
+
expect(len).to eq("hello world".length)
|
134
|
+
expect(data).to eq("hello world")
|
135
|
+
|
136
|
+
0
|
137
|
+
end
|
138
|
+
|
139
|
+
marquise.tell "hello world".encode('ASCII-8BIT'),
|
140
|
+
:foo => 'bar', 'answer' => 42
|
141
|
+
end
|
142
|
+
|
143
|
+
it "sends binary, timestamp and source" do
|
144
|
+
t = Time.now + 86400
|
145
|
+
|
146
|
+
Marquise::FFI.stub(:send) do |*args|
|
147
|
+
expect(args).to be_an(Array)
|
148
|
+
expect(args.length).to eq(8)
|
149
|
+
|
150
|
+
method, conn, src_f, src_v, src_c, data, len, tstamp = args
|
151
|
+
|
152
|
+
expect(method).to eq(:marquise_send_binary)
|
153
|
+
expect(conn).to eq('#tellconn')
|
154
|
+
|
155
|
+
expect(tstamp).to eq(t.to_f * 1_000_000_000)
|
156
|
+
|
157
|
+
expect(src_c).to eq(2)
|
158
|
+
|
159
|
+
expect(src_f).to be_a(FFI::MemoryPointer)
|
160
|
+
expect(src_f.size).to eq(3*src_f.type_size)
|
161
|
+
expect(src_f[0].read_pointer.read_string).to eq('foo')
|
162
|
+
expect(src_f[1].read_pointer.read_string).to eq('answer')
|
163
|
+
expect(src_f[2].read_pointer.address).to eq(0)
|
164
|
+
|
165
|
+
expect(src_v).to be_a(FFI::MemoryPointer)
|
166
|
+
expect(src_v.size).to eq(3*src_f.type_size)
|
167
|
+
expect(src_v[0].read_pointer.read_string).to eq('bar')
|
168
|
+
expect(src_v[1].read_pointer.read_string).to eq('42')
|
169
|
+
expect(src_v[2].read_pointer.address).to eq(0)
|
170
|
+
|
171
|
+
expect(len).to eq("hello world".length)
|
172
|
+
expect(data).to eq("hello world")
|
173
|
+
|
174
|
+
0
|
175
|
+
end
|
176
|
+
|
177
|
+
marquise.tell "hello world".encode('ASCII-8BIT'),
|
178
|
+
t,
|
179
|
+
:foo => 'bar', 'answer' => 42
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|