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/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