blather 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- # return:: :available if state is nil
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
 
@@ -19,6 +19,12 @@ class Stream
19
19
  STREAM
20
20
  send start_stream.gsub(/\s+/, ' ')
21
21
  end
22
+
23
+ def send(stanza)
24
+ stanza.from = self.jid if stanza.is_a?(Stanza) && !stanza.from.nil?
25
+ super stanza
26
+ end
27
+
22
28
  end #Client
23
29
 
24
30
  end #Stream
@@ -1,7 +1,7 @@
1
1
  module Blather # :nodoc:
2
2
  class Stream # :nodoc:
3
3
 
4
- class Features
4
+ class Features # :nodoc:
5
5
  @@features = {}
6
6
  def self.register(ns)
7
7
  @@features[ns] = self
@@ -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.xpath('body').must_be_empty
24
+ s.find('body').must_be_empty
25
25
 
26
26
  s.body = 'test message'
27
27
  s.body.wont_be_nil
28
- s.xpath('body').wont_be_empty
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.xpath('subject').must_be_empty
34
+ s.find('subject').must_be_empty
35
35
 
36
36
  s.subject = 'test subject'
37
37
  s.subject.wont_be_nil
38
- s.xpath('subject').wont_be_empty
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.xpath('thread').must_be_empty
44
+ s.find('thread').must_be_empty
45
45
 
46
46
  s.thread = 1234
47
47
  s.thread.wont_be_nil
48
- s.xpath('thread').wont_be_empty
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
@@ -83,7 +83,7 @@ describe Blather::Stream::Component do
83
83
  end
84
84
  end
85
85
 
86
- it 'sends stanzas to the wire with ensuring from is set' do
86
+ it 'sends stanzas to the wire ensuring "from" is set' do
87
87
  client = mock()
88
88
  client.stubs(:jid)
89
89
  client.stubs(:jid=)
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.1
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-07 00:00:00 -07:00
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