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.
@@ -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