blather 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +16 -1
- data/examples/echo.rb +1 -1
- data/examples/ping.rb +11 -0
- data/examples/pong.rb +6 -0
- data/lib/blather/client.rb +5 -3
- data/lib/blather/client/client.rb +150 -51
- data/lib/blather/client/dsl.rb +24 -0
- data/lib/blather/client/dsl/pubsub.rb +16 -16
- data/lib/blather/errors.rb +14 -3
- data/lib/blather/jid.rb +14 -22
- data/lib/blather/stanza.rb +0 -16
- data/lib/blather/stanza/iq.rb +46 -5
- data/lib/blather/stanza/message.rb +141 -9
- data/lib/blather/stanza/presence.rb +49 -5
- data/lib/blather/stanza/presence/status.rb +68 -8
- data/lib/blather/stream/client.rb +6 -0
- data/lib/blather/stream/features.rb +1 -1
- data/spec/blather/client/client_spec.rb +131 -0
- data/spec/blather/client/dsl_spec.rb +20 -0
- data/spec/blather/stanza/message_spec.rb +17 -6
- data/spec/blather/stream/client_spec.rb +20 -0
- data/spec/blather/stream/component_spec.rb +1 -1
- metadata +4 -2
@@ -2,8 +2,68 @@ module Blather
|
|
2
2
|
class Stanza
|
3
3
|
class Presence
|
4
4
|
|
5
|
+
# = Status Stanza
|
6
|
+
#
|
7
|
+
# Presence stanzas are used to express an entity's current network availability (offline or online, along with
|
8
|
+
# various sub-states of the latter and optional user-defined descriptive text), and to notify other entities of
|
9
|
+
# that availability.
|
10
|
+
#
|
11
|
+
# == State Attribute
|
12
|
+
#
|
13
|
+
# The +state+ attribute determains the availability of the entity and can be one of the following:
|
14
|
+
#
|
15
|
+
# * +:available+ -- The entity or resource is available
|
16
|
+
# * +:away+ -- The entity or resource is temporarily away.
|
17
|
+
# * +:chat+ -- The entity or resource is actively interested in chatting.
|
18
|
+
# * +:dnd+ -- The entity or resource is busy (dnd = "Do Not Disturb").
|
19
|
+
# * +:xa+ -- The entity or resource is away for an extended period (xa = "eXtended Away").
|
20
|
+
#
|
21
|
+
# Blather provides a helper for each possible state:
|
22
|
+
#
|
23
|
+
# Status#available?
|
24
|
+
# Status#away?
|
25
|
+
# Status#chat?
|
26
|
+
# Status#dnd?
|
27
|
+
# Status#xa?
|
28
|
+
#
|
29
|
+
# Blather treats the +type+ attribute like a normal ruby object attribute providing a getter and setter.
|
30
|
+
# The default +type+ is +available+.
|
31
|
+
#
|
32
|
+
# status = Status.new
|
33
|
+
# status.state # => :available
|
34
|
+
# status.available? # => true
|
35
|
+
# status.state = :away
|
36
|
+
# status.away? # => true
|
37
|
+
# status.available? # => false
|
38
|
+
# status
|
39
|
+
# status.state = :invalid # => RuntimeError
|
40
|
+
#
|
41
|
+
# == Type Attribute
|
42
|
+
#
|
43
|
+
# The +type+ attribute is inherited from Presence, but limits the value to either +nil+ or +:unavailable+
|
44
|
+
# as these are the only types that relate to Status.
|
45
|
+
#
|
46
|
+
# == Priority Attribute
|
47
|
+
#
|
48
|
+
# The +priority+ attribute sets the priority of the status for the entity and must be an integer between
|
49
|
+
# -128 and 127.
|
50
|
+
#
|
51
|
+
# == Message Attribute
|
52
|
+
#
|
53
|
+
# The optional +message+ element contains XML character data specifying a natural-language description of
|
54
|
+
# availability status. It is normally used in conjunction with the show element to provide a detailed
|
55
|
+
# description of an availability state (e.g., "In a meeting").
|
56
|
+
#
|
57
|
+
# Blather treats the +message+ attribute like a normal ruby object attribute providing a getter and setter.
|
58
|
+
# The default +message+ is nil.
|
59
|
+
#
|
60
|
+
# status = Status.new
|
61
|
+
# status.message # => nil
|
62
|
+
# status.message = "gone!"
|
63
|
+
# status.message # => "gone!"
|
64
|
+
#
|
5
65
|
class Status < Presence
|
6
|
-
VALID_STATES = [:away, :chat, :dnd, :xa]
|
66
|
+
VALID_STATES = [:away, :chat, :dnd, :xa] # :nodoc:
|
7
67
|
|
8
68
|
include Comparable
|
9
69
|
|
@@ -16,18 +76,18 @@ class Presence
|
|
16
76
|
node
|
17
77
|
end
|
18
78
|
|
19
|
-
attribute_helpers_for(:state, VALID_STATES)
|
79
|
+
attribute_helpers_for(:state, [:available] + VALID_STATES)
|
20
80
|
|
21
81
|
##
|
22
82
|
# Ensures type is nil or :unavailable
|
23
|
-
def type=(type)
|
83
|
+
def type=(type) # :nodoc:
|
24
84
|
raise ArgumentError, "Invalid type (#{type}). Must be nil or unavailable" if type && type.to_sym != :unavailable
|
25
85
|
super
|
26
86
|
end
|
27
87
|
|
28
88
|
##
|
29
|
-
# Ensure state is one of :away, :chat, :dnd, :xa or nil
|
30
|
-
def state=(state)
|
89
|
+
# Ensure state is one of :available, :away, :chat, :dnd, :xa or nil
|
90
|
+
def state=(state) # :nodoc:
|
31
91
|
state = state.to_sym if state
|
32
92
|
state = nil if state == :available
|
33
93
|
raise ArgumentError, "Invalid Status (#{state}), use: #{VALID_STATES*' '}" if state && !VALID_STATES.include?(state)
|
@@ -36,14 +96,14 @@ class Presence
|
|
36
96
|
end
|
37
97
|
|
38
98
|
##
|
39
|
-
#
|
40
|
-
def state
|
99
|
+
# :available if state is nil
|
100
|
+
def state # :nodoc:
|
41
101
|
(type || content_from(:show) || :available).to_sym
|
42
102
|
end
|
43
103
|
|
44
104
|
##
|
45
105
|
# Ensure priority is between -128 and 127
|
46
|
-
def priority=(new_priority)
|
106
|
+
def priority=(new_priority) # :nodoc:
|
47
107
|
raise ArgumentError, 'Priority must be between -128 and +127' if new_priority && !(-128..127).include?(new_priority.to_i)
|
48
108
|
set_content_for :priority, new_priority
|
49
109
|
|
@@ -114,6 +114,43 @@ describe Blather::Client do
|
|
114
114
|
@client.receive_data stanza
|
115
115
|
@client.receive_data stanza
|
116
116
|
end
|
117
|
+
|
118
|
+
it 'allows for breaking out of handlers' do
|
119
|
+
stanza = Blather::Stanza::Iq.new
|
120
|
+
response = mock(:iq => nil)
|
121
|
+
@client.register_handler(:iq) do |_|
|
122
|
+
response.iq
|
123
|
+
throw :halt
|
124
|
+
response.fail
|
125
|
+
end
|
126
|
+
@client.receive_data stanza
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'allows for passing to the next handler of the same type' do
|
130
|
+
stanza = Blather::Stanza::Iq.new
|
131
|
+
response = mock(:iq1 => nil, :iq2 => nil)
|
132
|
+
@client.register_handler(:iq) do |_|
|
133
|
+
response.iq1
|
134
|
+
throw :pass
|
135
|
+
response.fail
|
136
|
+
end
|
137
|
+
@client.register_handler(:iq) do |_|
|
138
|
+
response.iq2
|
139
|
+
end
|
140
|
+
@client.receive_data stanza
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'allows for passing to the next handler in the heirarchy' do
|
144
|
+
stanza = Blather::Stanza::Iq::Query.new
|
145
|
+
response = mock(:query => nil, :iq => nil)
|
146
|
+
@client.register_handler(:query) do |_|
|
147
|
+
response.query
|
148
|
+
throw :pass
|
149
|
+
response.fail
|
150
|
+
end
|
151
|
+
@client.register_handler(:iq) { |_| response.iq }
|
152
|
+
@client.receive_data stanza
|
153
|
+
end
|
117
154
|
end
|
118
155
|
|
119
156
|
describe 'Blather::Client#write' do
|
@@ -200,11 +237,37 @@ describe 'Blather::Client default handlers' do
|
|
200
237
|
@client.receive_data status
|
201
238
|
end
|
202
239
|
|
240
|
+
it 'lets status stanzas fall through to other handlers' do
|
241
|
+
jid = 'friend@jabber.local'
|
242
|
+
status = Blather::Stanza::Presence::Status.new :away
|
243
|
+
status.stubs(:from).returns jid
|
244
|
+
roster_item = mock()
|
245
|
+
roster_item.expects(:status=).with status
|
246
|
+
@client.stubs(:roster).returns({status.from => roster_item})
|
247
|
+
|
248
|
+
response = mock()
|
249
|
+
response.expects(:call).with jid
|
250
|
+
@client.register_handler(:status) { |s| response.call s.from.to_s }
|
251
|
+
@client.receive_data status
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'handles an incoming roster node by processing it through the roster' do
|
255
|
+
roster = Blather::Stanza::Iq::Roster.new
|
256
|
+
client_roster = mock()
|
257
|
+
client_roster.expects(:process).with roster
|
258
|
+
@client.stubs(:roster).returns client_roster
|
259
|
+
@client.receive_data roster
|
260
|
+
end
|
261
|
+
|
203
262
|
it 'handles an incoming roster node by processing it through the roster' do
|
204
263
|
roster = Blather::Stanza::Iq::Roster.new
|
205
264
|
client_roster = mock()
|
206
265
|
client_roster.expects(:process).with roster
|
207
266
|
@client.stubs(:roster).returns client_roster
|
267
|
+
|
268
|
+
response = mock()
|
269
|
+
response.expects(:call)
|
270
|
+
@client.register_handler(:roster) { |_| response.call }
|
208
271
|
@client.receive_data roster
|
209
272
|
end
|
210
273
|
end
|
@@ -250,6 +313,74 @@ describe 'Blather::Client with a Client stream' do
|
|
250
313
|
end
|
251
314
|
end
|
252
315
|
|
316
|
+
describe 'Blather::Client filters' do
|
317
|
+
before do
|
318
|
+
@client = Blather::Client.new
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'raises an error when an invalid filter type is registered' do
|
322
|
+
lambda { @client.register_filter(:invalid) {} }.must_raise RuntimeError
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'can be guarded' do
|
326
|
+
stanza = Blather::Stanza::Iq.new
|
327
|
+
ready = mock()
|
328
|
+
ready.expects(:call).once
|
329
|
+
@client.register_filter(:before, :iq, :id => stanza.id) { |_| ready.call }
|
330
|
+
@client.register_filter(:before, :iq, :id => 'not-id') { |_| ready.call }
|
331
|
+
@client.receive_data stanza
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'can pass to the next handler' do
|
335
|
+
stanza = Blather::Stanza::Iq.new
|
336
|
+
ready = mock()
|
337
|
+
ready.expects(:call).once
|
338
|
+
@client.register_filter(:before) { |_| throw :pass; ready.call }
|
339
|
+
@client.register_filter(:before) { |_| ready.call }
|
340
|
+
@client.receive_data stanza
|
341
|
+
end
|
342
|
+
|
343
|
+
it 'runs them in order' do
|
344
|
+
stanza = Blather::Stanza::Iq.new
|
345
|
+
count = 0
|
346
|
+
@client.register_filter(:before) { |_| count.must_equal 0; count = 1 }
|
347
|
+
@client.register_filter(:before) { |_| count.must_equal 1; count = 2 }
|
348
|
+
@client.register_handler(:iq) { |_| count.must_equal 2; count = 3 }
|
349
|
+
@client.register_filter(:after) { |_| count.must_equal 3; count = 4 }
|
350
|
+
@client.register_filter(:after) { |_| count.must_equal 4 }
|
351
|
+
@client.receive_data stanza
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'can modify the stanza' do
|
355
|
+
stanza = Blather::Stanza::Iq.new
|
356
|
+
stanza.from = 'from@test.local'
|
357
|
+
new_jid = 'before@filter.local'
|
358
|
+
ready = mock()
|
359
|
+
ready.expects(:call).with new_jid
|
360
|
+
@client.register_filter(:before) { |s| s.from = new_jid }
|
361
|
+
@client.register_handler(:iq) { |s| ready.call s.from.to_s }
|
362
|
+
@client.receive_data stanza
|
363
|
+
end
|
364
|
+
|
365
|
+
it 'can halt the handler chain' do
|
366
|
+
stanza = Blather::Stanza::Iq.new
|
367
|
+
ready = mock()
|
368
|
+
ready.expects(:call).never
|
369
|
+
@client.register_filter(:before) { |_| throw :halt }
|
370
|
+
@client.register_handler(:iq) { |_| ready.call }
|
371
|
+
@client.receive_data stanza
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'can be specific to a handler' do
|
375
|
+
stanza = Blather::Stanza::Iq.new
|
376
|
+
ready = mock()
|
377
|
+
ready.expects(:call).once
|
378
|
+
@client.register_filter(:before, :iq) { |_| ready.call }
|
379
|
+
@client.register_filter(:before, :message) { |_| ready.call }
|
380
|
+
@client.receive_data stanza
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
253
384
|
describe 'Blather::Client guards' do
|
254
385
|
before do
|
255
386
|
@client = Blather::Client.new
|
@@ -31,6 +31,26 @@ describe Blather::DSL do
|
|
31
31
|
@dsl.shutdown
|
32
32
|
end
|
33
33
|
|
34
|
+
it 'can throw a halt' do
|
35
|
+
catch(:halt) { @dsl.halt }
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can throw a pass' do
|
39
|
+
catch(:pass) { @dsl.pass }
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'can setup before filters' do
|
43
|
+
guards = [:chat?, {:body => 'exit'}]
|
44
|
+
@client.expects(:register_filter).with :before, nil, *guards
|
45
|
+
@dsl.before nil, *guards
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can setup after filters' do
|
49
|
+
guards = [:chat?, {:body => 'exit'}]
|
50
|
+
@client.expects(:register_filter).with :after, nil, *guards
|
51
|
+
@dsl.after nil, *guards
|
52
|
+
end
|
53
|
+
|
34
54
|
it 'sets up handlers' do
|
35
55
|
type = :message
|
36
56
|
guards = [:chat?, {:body => 'exit'}]
|
@@ -21,31 +21,42 @@ describe Blather::Stanza::Message do
|
|
21
21
|
it 'provides "attr_accessor" for body' do
|
22
22
|
s = Blather::Stanza::Message.new
|
23
23
|
s.body.must_be_nil
|
24
|
-
s.
|
24
|
+
s.find('body').must_be_empty
|
25
25
|
|
26
26
|
s.body = 'test message'
|
27
27
|
s.body.wont_be_nil
|
28
|
-
s.
|
28
|
+
s.find('body').wont_be_empty
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'provides "attr_accessor" for subject' do
|
32
32
|
s = Blather::Stanza::Message.new
|
33
33
|
s.subject.must_be_nil
|
34
|
-
s.
|
34
|
+
s.find('subject').must_be_empty
|
35
35
|
|
36
36
|
s.subject = 'test subject'
|
37
37
|
s.subject.wont_be_nil
|
38
|
-
s.
|
38
|
+
s.find('subject').wont_be_empty
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'provides "attr_accessor" for thread' do
|
42
42
|
s = Blather::Stanza::Message.new
|
43
43
|
s.thread.must_be_nil
|
44
|
-
s.
|
44
|
+
s.find('thread').must_be_empty
|
45
45
|
|
46
46
|
s.thread = 1234
|
47
47
|
s.thread.wont_be_nil
|
48
|
-
s.
|
48
|
+
s.find('thread').wont_be_empty
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'can set a parent attribute for thread' do
|
52
|
+
s = Blather::Stanza::Message.new
|
53
|
+
s.thread.must_be_nil
|
54
|
+
s.find('thread').must_be_empty
|
55
|
+
|
56
|
+
s.thread = {4321 => 1234}
|
57
|
+
s.thread.must_equal '1234'
|
58
|
+
s.parent_thread.must_equal '4321'
|
59
|
+
s.find('thread[@parent="4321"]').wont_be_empty
|
49
60
|
end
|
50
61
|
|
51
62
|
it 'ensures type is one of Blather::Stanza::Message::VALID_TYPES' do
|
@@ -988,4 +988,24 @@ describe Blather::Stream::Client do
|
|
988
988
|
end
|
989
989
|
end
|
990
990
|
|
991
|
+
it 'sends stanzas to the wire ensuring "from" is the full JID if set' do
|
992
|
+
client = mock()
|
993
|
+
client.stubs(:jid)
|
994
|
+
client.stubs(:jid=)
|
995
|
+
msg = Blather::Stanza::Message.new 'to@jid.com', 'body'
|
996
|
+
msg.from = 'node@jid.com'
|
997
|
+
comp = Blather::Stream::Client.new nil, client, 'node@jid.com/resource', 'pass'
|
998
|
+
comp.expects(:send_data).with { |s| s.must_match(/^<message[^>]*from="node@jid\.com\/resource"/) }
|
999
|
+
comp.send msg
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
it 'sends stanzas to the wire leaving "from" nil if not set' do
|
1003
|
+
client = mock()
|
1004
|
+
client.stubs(:jid)
|
1005
|
+
client.stubs(:jid=)
|
1006
|
+
msg = Blather::Stanza::Message.new 'to@jid.com', 'body'
|
1007
|
+
comp = Blather::Stream::Client.new nil, client, 'node@jid.com/resource', 'pass'
|
1008
|
+
comp.expects(:send_data).with { |s| s.wont_match(/^<message[^>]*from=/); true }
|
1009
|
+
comp.send msg
|
1010
|
+
end
|
991
1011
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blather
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Smick
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-06-
|
12
|
+
date: 2009-06-13 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -45,7 +45,9 @@ files:
|
|
45
45
|
- examples/drb_client.rb
|
46
46
|
- examples/echo.rb
|
47
47
|
- examples/execute.rb
|
48
|
+
- examples/ping.rb
|
48
49
|
- examples/ping_pong.rb
|
50
|
+
- examples/pong.rb
|
49
51
|
- examples/print_heirarchy.rb
|
50
52
|
- examples/pubsub/cli.rb
|
51
53
|
- examples/pubsub/ping_pong.rb
|