blather 0.4.1 → 0.4.2
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 +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
|