blather 0.3.0 → 0.3.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.
- data/README.rdoc +17 -2
- data/Rakefile +1 -1
- data/examples/ping_pong.rb +37 -0
- data/lib/blather.rb +1 -4
- data/lib/blather/client.rb +75 -3
- data/lib/blather/client/client.rb +67 -53
- data/lib/blather/client/dsl.rb +13 -18
- data/lib/blather/stanza/disco/disco_info.rb +0 -12
- data/lib/blather/stanza/disco/disco_items.rb +0 -7
- data/lib/blather/stream.rb +4 -4
- data/lib/blather/stream/component.rb +1 -1
- data/spec/blather/client/client_spec.rb +385 -0
- data/spec/blather/client/dsl_spec.rb +101 -0
- data/spec/blather/stream/client_spec.rb +18 -18
- data/spec/blather/stream/component_spec.rb +3 -3
- data/spec/spec_helper.rb +4 -1
- metadata +4 -6
- data/lib/blather/stanza/pubsub/subscriber.rb +0 -64
- data/spec/blather/client_spec.rb +0 -0
- data/spec/blather/stanza/pubsub/subscriber_spec.rb +0 -70
@@ -52,13 +52,6 @@ class Stanza
|
|
52
52
|
self.category = category
|
53
53
|
end
|
54
54
|
end
|
55
|
-
|
56
|
-
def eql?(other)
|
57
|
-
other.kind_of?(self.class) &&
|
58
|
-
other.name == self.name &&
|
59
|
-
other.type == self.type &&
|
60
|
-
other.category == self.category
|
61
|
-
end
|
62
55
|
end
|
63
56
|
|
64
57
|
class Feature < XMPPNode
|
@@ -72,11 +65,6 @@ class Stanza
|
|
72
65
|
self.var = var
|
73
66
|
end
|
74
67
|
end
|
75
|
-
|
76
|
-
def eql?(other)
|
77
|
-
other.kind_of?(self.class) &&
|
78
|
-
other.var == self.var
|
79
|
-
end
|
80
68
|
end
|
81
69
|
end
|
82
70
|
|
data/lib/blather/stream.rb
CHANGED
@@ -87,8 +87,8 @@ module Blather
|
|
87
87
|
def unbind # :nodoc:
|
88
88
|
# @keepalive.cancel
|
89
89
|
@state = :stopped
|
90
|
-
@client.
|
91
|
-
@client.
|
90
|
+
@client.receive_data @error if @error
|
91
|
+
@client.unbind
|
92
92
|
end
|
93
93
|
|
94
94
|
##
|
@@ -162,7 +162,7 @@ module Blather
|
|
162
162
|
# Called when @state == :ready
|
163
163
|
# Simply passes the stanza to the client
|
164
164
|
def ready
|
165
|
-
@client.
|
165
|
+
@client.receive_data @node.to_stanza
|
166
166
|
end
|
167
167
|
|
168
168
|
def handle_stream_error
|
@@ -242,7 +242,7 @@ module Blather
|
|
242
242
|
@session = Session.new self, @to
|
243
243
|
# on success destroy the session object, let the client know the stream has been started
|
244
244
|
# then continue the features dispatch process
|
245
|
-
@session.on_success { LOG.debug "SESSION: SUCCESS"; @session = nil; @client.
|
245
|
+
@session.on_success { LOG.debug "SESSION: SUCCESS"; @session = nil; @client.post_init; @state = :features; dispatch }
|
246
246
|
# on failure end the stream
|
247
247
|
@session.on_failure { |err| LOG.debug "SESSION: FAILURE"; @error = err; stop }
|
248
248
|
|
@@ -1,4 +1,389 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
|
2
|
+
require 'blather/client/client'
|
2
3
|
|
3
4
|
describe 'Blather::Client' do
|
5
|
+
before do
|
6
|
+
@client = Blather::Client.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'provides a JID accessor' do
|
10
|
+
@client.must_respond_to :jid
|
11
|
+
@client.jid.must_be_nil
|
12
|
+
|
13
|
+
jid = 'me@me.com/test'
|
14
|
+
@client.must_respond_to :jid=
|
15
|
+
@client.jid = jid
|
16
|
+
@client.jid.must_be_kind_of JID
|
17
|
+
@client.jid.must_equal JID.new(jid)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'provides a reader for the roster' do
|
21
|
+
@client.must_respond_to :roster
|
22
|
+
@client.roster.must_be_kind_of Roster
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'provides a status reader' do
|
26
|
+
@client.must_respond_to :status
|
27
|
+
@client.status = :away
|
28
|
+
@client.status.must_equal :away
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'can be setup' do
|
32
|
+
@client.must_respond_to :setup
|
33
|
+
@client.setup('me@me.com', 'pass').must_equal @client
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'knows if it has been setup' do
|
37
|
+
@client.must_respond_to :setup?
|
38
|
+
@client.setup?.must_equal false
|
39
|
+
@client.setup 'me@me.com', 'pass'
|
40
|
+
@client.setup?.must_equal true
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'cannot be run before being setup' do
|
44
|
+
lambda { @client.run }.must_raise RuntimeError
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'starts up a Component connection when setup without a node' do
|
48
|
+
setup = 'pubsub.jabber.local', 'secret'
|
49
|
+
@client.setup *setup
|
50
|
+
Blather::Stream::Component.expects(:start).with @client, *setup
|
51
|
+
@client.run
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'starts up a Client connection when setup with a node' do
|
55
|
+
setup = 'test@jabber.local', 'secret'
|
56
|
+
@client.setup *setup
|
57
|
+
Blather::Stream::Client.expects(:start).with @client, *setup
|
58
|
+
@client.run
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'writes to the connection the closes when #close is called' do
|
62
|
+
stream = mock()
|
63
|
+
stream.expects(:close_connection_after_writing)
|
64
|
+
Blather::Stream::Component.stubs(:start).returns stream
|
65
|
+
@client.setup('me.com', 'secret').run
|
66
|
+
@client.close
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'shuts down EM when #unbind is called if it is running' do
|
70
|
+
EM.expects(:reactor_running?).returns true
|
71
|
+
EM.expects(:stop)
|
72
|
+
@client.unbind
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'does nothing when #unbind is called and EM is not running' do
|
76
|
+
EM.expects(:reactor_running?).returns false
|
77
|
+
EM.expects(:stop).never
|
78
|
+
@client.unbind
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'raises an error if the stream type somehow is not supported' do
|
82
|
+
Blather::Stream::Component.stubs(:start).returns nil
|
83
|
+
@client.setup('me.com', 'secret').run
|
84
|
+
lambda { @client.post_init }.must_raise RuntimeError
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'can register a temporary handler based on stanza ID' do
|
88
|
+
stanza = Stanza::Iq.new
|
89
|
+
response = mock()
|
90
|
+
response.expects(:call)
|
91
|
+
@client.register_tmp_handler(stanza.id) { |_| response.call }
|
92
|
+
@client.receive_data stanza
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'removes a tmp handler as soon as it is used' do
|
96
|
+
stanza = Stanza::Iq.new
|
97
|
+
response = mock()
|
98
|
+
response.expects(:call)
|
99
|
+
@client.register_tmp_handler(stanza.id) { |_| response.call }
|
100
|
+
@client.receive_data stanza
|
101
|
+
@client.receive_data stanza
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'will create a handler then write the stanza' do
|
105
|
+
stanza = Stanza::Iq.new
|
106
|
+
response = mock()
|
107
|
+
response.expects(:call)
|
108
|
+
@client.expects(:write).with do |s|
|
109
|
+
@client.receive_data stanza
|
110
|
+
s.must_equal stanza
|
111
|
+
end
|
112
|
+
@client.write_with_handler(stanza) { |_| response.call }
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'can register a handler' do
|
116
|
+
stanza = Stanza::Iq.new
|
117
|
+
response = mock()
|
118
|
+
response.expects(:call).times(2)
|
119
|
+
@client.register_handler(:iq) { |_| response.call }
|
120
|
+
@client.receive_data stanza
|
121
|
+
@client.receive_data stanza
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'Blather::Client#write' do
|
126
|
+
before do
|
127
|
+
@client = Blather::Client.new
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'sets the from attr on a stanza' do
|
131
|
+
jid = 'me@me.com'
|
132
|
+
stanza = mock(:from => nil)
|
133
|
+
stanza.expects(:from=).with jid
|
134
|
+
@client.jid = jid
|
135
|
+
@client.write stanza
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'does not set the from attr if it already exists' do
|
139
|
+
jid = 'me@me.com'
|
140
|
+
stanza = Stanza::Iq.new
|
141
|
+
stanza.from = jid
|
142
|
+
stanza.expects(:from).returns jid
|
143
|
+
stanza.expects(:from=).never
|
144
|
+
@client.jid = jid
|
145
|
+
@client.write stanza
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'writes to the stream' do
|
149
|
+
stanza = Stanza::Iq.new
|
150
|
+
stream = mock()
|
151
|
+
stream.expects(:send).with stanza
|
152
|
+
Blather::Stream::Client.expects(:start).returns stream
|
153
|
+
@client.setup('me@me.com', 'me').run
|
154
|
+
@client.write stanza
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe 'Blather::Client#status=' do
|
159
|
+
before do
|
160
|
+
@client = Blather::Client.new
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'updates the state when not sending to a JID' do
|
164
|
+
@client.status.wont_equal :away
|
165
|
+
@client.status = :away, 'message'
|
166
|
+
@client.status.must_equal :away
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'does not update the state when sending to a JID' do
|
170
|
+
@client.status.wont_equal :away
|
171
|
+
@client.status = :away, 'message', 'me@me.com'
|
172
|
+
@client.status.wont_equal :away
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'writes the new status to the stream' do
|
176
|
+
Stanza::Presence::Status.stubs(:next_id).returns 0
|
177
|
+
status = [:away, 'message']
|
178
|
+
@client.expects(:write).with do |s|
|
179
|
+
s.must_be_kind_of Stanza::Presence::Status
|
180
|
+
s.to_s.must_equal Stanza::Presence::Status.new(*status).to_s
|
181
|
+
end
|
182
|
+
@client.status = status
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe 'Blather::Client default handlers' do
|
187
|
+
before do
|
188
|
+
@client = Blather::Client.new
|
189
|
+
end
|
190
|
+
|
191
|
+
it 're-raises errors' do
|
192
|
+
err = BlatherError.new
|
193
|
+
lambda { @client.receive_data err }.must_raise BlatherError
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'responds to iq:get with a "service-unavailable" error' do
|
197
|
+
get = Stanza::Iq.new :get
|
198
|
+
err = StanzaError.new(get, 'service-unavailable', :cancel).to_node
|
199
|
+
@client.expects(:write).with err
|
200
|
+
@client.receive_data get
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'responds to iq:get with a "service-unavailable" error' do
|
204
|
+
get = Stanza::Iq.new :get
|
205
|
+
err = StanzaError.new(get, 'service-unavailable', :cancel).to_node
|
206
|
+
@client.expects(:write).with err
|
207
|
+
@client.receive_data get
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'responds to iq:set with a "service-unavailable" error' do
|
211
|
+
get = Stanza::Iq.new :set
|
212
|
+
err = StanzaError.new(get, 'service-unavailable', :cancel).to_node
|
213
|
+
@client.expects(:write).with err
|
214
|
+
@client.receive_data get
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'handles status changes by updating the roster if the status is from a JID in the roster' do
|
218
|
+
jid = 'friend@jabber.local'
|
219
|
+
status = Stanza::Presence::Status.new :away
|
220
|
+
status.stubs(:from).returns jid
|
221
|
+
roster_item = mock()
|
222
|
+
roster_item.expects(:status=).with status
|
223
|
+
@client.stubs(:roster).returns({status.from => roster_item})
|
224
|
+
@client.receive_data status
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'handles an incoming roster node by processing it through the roster' do
|
228
|
+
roster = Stanza::Iq::Roster.new
|
229
|
+
client_roster = mock()
|
230
|
+
client_roster.expects(:process).with roster
|
231
|
+
@client.stubs(:roster).returns client_roster
|
232
|
+
@client.receive_data roster
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe 'Blather::Client with a Component stream' do
|
237
|
+
before do
|
238
|
+
class MockComponent < Blather::Stream::Component; def initialize(); end; end
|
239
|
+
@client = Blather::Client.new
|
240
|
+
Blather::Stream::Component.stubs(:start).returns MockComponent.new('')
|
241
|
+
@client.setup('me.com', 'secret').run
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'calls the ready handler when sent post_init' do
|
245
|
+
ready = mock()
|
246
|
+
ready.expects(:call)
|
247
|
+
@client.register_handler(:ready) { ready.call }
|
248
|
+
@client.post_init
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe 'Blather::Client with a Client stream' do
|
253
|
+
before do
|
254
|
+
class MockClientStream < Blather::Stream::Client; def initialize(); end; end
|
255
|
+
@stream = MockClientStream.new('')
|
256
|
+
@client = Blather::Client.new
|
257
|
+
Blather::Stream::Client.stubs(:start).returns @stream
|
258
|
+
@client.setup('me@me.com', 'secret').run
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'sends a request for the roster when post_init is called' do
|
262
|
+
@stream.expects(:send).with { |stanza| stanza.must_be_kind_of Stanza::Iq::Roster }
|
263
|
+
@client.post_init
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'calls the ready handler after post_init and roster is received' do
|
267
|
+
result_roster = Stanza::Iq::Roster.new :result
|
268
|
+
@stream.stubs(:send).with { |s| result_roster.id = s.id; @client.receive_data result_roster; true }
|
269
|
+
|
270
|
+
ready = mock()
|
271
|
+
ready.expects(:call)
|
272
|
+
@client.register_handler(:ready) { ready.call }
|
273
|
+
@client.post_init
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe 'Blather::Client guards' do
|
278
|
+
before do
|
279
|
+
@client = Blather::Client.new
|
280
|
+
@stanza = Stanza::Iq.new
|
281
|
+
@response = mock()
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'can be a symbol' do
|
285
|
+
@response.expects :call
|
286
|
+
@client.register_handler(:iq, :chat?) { |_| @response.call }
|
287
|
+
|
288
|
+
@stanza.expects(:chat?).returns true
|
289
|
+
@client.receive_data @stanza
|
290
|
+
|
291
|
+
@stanza.expects(:chat?).returns false
|
292
|
+
@client.receive_data @stanza
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'can be a hash with string match' do
|
296
|
+
@response.expects :call
|
297
|
+
@client.register_handler(:iq, :body => 'exit') { |_| @response.call }
|
298
|
+
|
299
|
+
@stanza.expects(:body).returns 'exit'
|
300
|
+
@client.receive_data @stanza
|
301
|
+
|
302
|
+
@stanza.expects(:body).returns 'not-exit'
|
303
|
+
@client.receive_data @stanza
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'can be a hash with a value' do
|
307
|
+
@response.expects :call
|
308
|
+
@client.register_handler(:iq, :number => 0) { |_| @response.call }
|
309
|
+
|
310
|
+
@stanza.expects(:number).returns 0
|
311
|
+
@client.receive_data @stanza
|
312
|
+
|
313
|
+
@stanza.expects(:number).returns 1
|
314
|
+
@client.receive_data @stanza
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'can be a hash with a regexp' do
|
318
|
+
@response.expects :call
|
319
|
+
@client.register_handler(:iq, :body => /exit/) { |_| @response.call }
|
320
|
+
|
321
|
+
@stanza.expects(:body).returns 'more than just exit, but exit still'
|
322
|
+
@client.receive_data @stanza
|
323
|
+
|
324
|
+
@stanza.expects(:body).returns 'keyword not found'
|
325
|
+
@client.receive_data @stanza
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'can be a hash with an array' do
|
329
|
+
@response.expects(:call).times(2)
|
330
|
+
@client.register_handler(:iq, :type => [:result, :error]) { |_| @response.call }
|
331
|
+
|
332
|
+
stanza = Stanza::Iq.new
|
333
|
+
stanza.expects(:type).at_least_once.returns :result
|
334
|
+
@client.receive_data stanza
|
335
|
+
|
336
|
+
stanza = Stanza::Iq.new
|
337
|
+
stanza.expects(:type).at_least_once.returns :error
|
338
|
+
@client.receive_data stanza
|
339
|
+
|
340
|
+
stanza = Stanza::Iq.new
|
341
|
+
stanza.expects(:type).at_least_once.returns :get
|
342
|
+
@client.receive_data stanza
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'chained are treated like andand (short circuited)' do
|
346
|
+
@response.expects :call
|
347
|
+
@client.register_handler(:iq, :type => :get, :body => 'test') { |_| @response.call }
|
348
|
+
|
349
|
+
stanza = Stanza::Iq.new
|
350
|
+
stanza.expects(:type).at_least_once.returns :get
|
351
|
+
stanza.expects(:body).returns 'test'
|
352
|
+
@client.receive_data stanza
|
353
|
+
|
354
|
+
stanza = Stanza::Iq.new
|
355
|
+
stanza.expects(:type).at_least_once.returns :set
|
356
|
+
stanza.expects(:body).never
|
357
|
+
@client.receive_data stanza
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'within an Array are treated as oror (short circuited)' do
|
361
|
+
@response.expects(:call).times 2
|
362
|
+
@client.register_handler(:iq, [{:type => :get}, {:body => 'test'}]) { |_| @response.call }
|
363
|
+
|
364
|
+
stanza = Stanza::Iq.new
|
365
|
+
stanza.expects(:type).at_least_once.returns :set
|
366
|
+
stanza.expects(:body).returns 'test'
|
367
|
+
@client.receive_data stanza
|
368
|
+
|
369
|
+
stanza = Stanza::Iq.new
|
370
|
+
stanza.stubs(:type).at_least_once.returns :get
|
371
|
+
stanza.expects(:body).never
|
372
|
+
@client.receive_data stanza
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'can be a lambda' do
|
376
|
+
@response.expects :call
|
377
|
+
@client.register_handler(:iq, lambda { |s| s.number % 3 == 0 }) { |_| @response.call }
|
378
|
+
|
379
|
+
@stanza.expects(:number).at_least_once.returns 3
|
380
|
+
@client.receive_data @stanza
|
381
|
+
|
382
|
+
@stanza.expects(:number).at_least_once.returns 2
|
383
|
+
@client.receive_data @stanza
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'raises an error when a bad guard is tried' do
|
387
|
+
lambda { @client.register_handler(:iq, 0) {} }.must_raise RuntimeError
|
388
|
+
end
|
4
389
|
end
|