blather 0.2.1 → 0.2.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.
Files changed (84) hide show
  1. data/LICENSE +2 -0
  2. data/README.rdoc +54 -29
  3. data/Rakefile +94 -13
  4. data/VERSION.yml +4 -0
  5. data/examples/drb_client.rb +2 -4
  6. data/examples/echo.rb +13 -8
  7. data/examples/pubsub/cli.rb +64 -0
  8. data/examples/pubsub/ping_pong.rb +18 -0
  9. data/examples/pubsub/pubsub_dsl.rb +52 -0
  10. data/examples/pubsub_client.rb +39 -0
  11. data/examples/rosterprint.rb +14 -0
  12. data/examples/xmpp4r/echo.rb +35 -0
  13. data/ext/extconf.rb +65 -0
  14. data/lib/blather.rb +18 -121
  15. data/lib/blather/client.rb +13 -0
  16. data/lib/blather/client/client.rb +165 -0
  17. data/lib/blather/client/dsl.rb +99 -0
  18. data/lib/blather/client/pubsub.rb +53 -0
  19. data/lib/blather/client/pubsub/node.rb +27 -0
  20. data/lib/blather/core_ext/active_support.rb +1 -0
  21. data/lib/blather/core_ext/libxml.rb +7 -1
  22. data/lib/blather/errors.rb +39 -18
  23. data/lib/blather/errors/sasl_error.rb +87 -0
  24. data/lib/blather/errors/stanza_error.rb +262 -0
  25. data/lib/blather/errors/stream_error.rb +253 -0
  26. data/lib/blather/jid.rb +9 -16
  27. data/lib/blather/roster.rb +9 -0
  28. data/lib/blather/roster_item.rb +7 -4
  29. data/lib/blather/stanza.rb +19 -25
  30. data/lib/blather/stanza/disco.rb +9 -0
  31. data/lib/blather/stanza/disco/disco_info.rb +84 -0
  32. data/lib/blather/stanza/disco/disco_items.rb +59 -0
  33. data/lib/blather/stanza/iq.rb +16 -4
  34. data/lib/blather/stanza/iq/query.rb +6 -4
  35. data/lib/blather/stanza/iq/roster.rb +38 -38
  36. data/lib/blather/stanza/pubsub.rb +33 -0
  37. data/lib/blather/stanza/pubsub/affiliations.rb +52 -0
  38. data/lib/blather/stanza/pubsub/errors.rb +9 -0
  39. data/lib/blather/stanza/pubsub/event.rb +21 -0
  40. data/lib/blather/stanza/pubsub/items.rb +59 -0
  41. data/lib/blather/stanza/pubsub/owner.rb +9 -0
  42. data/lib/blather/stanza/pubsub/subscriptions.rb +57 -0
  43. data/lib/blather/stream.rb +125 -57
  44. data/lib/blather/stream/client.rb +26 -0
  45. data/lib/blather/stream/component.rb +34 -0
  46. data/lib/blather/stream/parser.rb +17 -27
  47. data/lib/blather/stream/resource.rb +21 -24
  48. data/lib/blather/stream/sasl.rb +60 -37
  49. data/lib/blather/stream/session.rb +12 -19
  50. data/lib/blather/stream/stream_handler.rb +39 -0
  51. data/lib/blather/stream/tls.rb +22 -18
  52. data/lib/blather/xmpp_node.rb +91 -17
  53. data/spec/blather/core_ext/libxml_spec.rb +58 -0
  54. data/spec/blather/errors/sasl_error_spec.rb +56 -0
  55. data/spec/blather/errors/stanza_error_spec.rb +148 -0
  56. data/spec/blather/errors/stream_error_spec.rb +114 -0
  57. data/spec/blather/errors_spec.rb +40 -0
  58. data/spec/blather/jid_spec.rb +0 -7
  59. data/spec/blather/roster_item_spec.rb +5 -0
  60. data/spec/blather/roster_spec.rb +6 -6
  61. data/spec/blather/stanza/discos/disco_info_spec.rb +207 -0
  62. data/spec/blather/stanza/discos/disco_items_spec.rb +136 -0
  63. data/spec/blather/stanza/iq/query_spec.rb +9 -2
  64. data/spec/blather/stanza/iq/roster_spec.rb +117 -1
  65. data/spec/blather/stanza/iq_spec.rb +29 -0
  66. data/spec/blather/stanza/presence/subscription_spec.rb +12 -1
  67. data/spec/blather/stanza/presence_spec.rb +29 -0
  68. data/spec/blather/stanza/pubsub/affiliations_spec.rb +46 -0
  69. data/spec/blather/stanza/pubsub/items_spec.rb +59 -0
  70. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +63 -0
  71. data/spec/blather/stanza/pubsub_spec.rb +26 -0
  72. data/spec/blather/stanza_spec.rb +13 -1
  73. data/spec/blather/stream/client_spec.rb +787 -0
  74. data/spec/blather/stream/component_spec.rb +86 -0
  75. data/spec/blather/xmpp_node_spec.rb +75 -22
  76. data/spec/fixtures/pubsub.rb +157 -0
  77. data/spec/spec_helper.rb +6 -14
  78. metadata +86 -74
  79. data/CHANGELOG +0 -5
  80. data/Manifest +0 -47
  81. data/blather.gemspec +0 -41
  82. data/lib/blather/stanza/error.rb +0 -31
  83. data/spec/blather/stream_spec.rb +0 -462
  84. data/spec/build_safe.rb +0 -20
@@ -0,0 +1,86 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+
3
+ describe 'Blather::Stream::Component' do
4
+ class MockServer; end
5
+ module ServerMock
6
+ def receive_data(data)
7
+ @server ||= MockServer.new
8
+ @server.receive_data data, self
9
+ end
10
+ end
11
+
12
+ def mocked_server(times = nil, &block)
13
+ @client ||= mock()
14
+ @client.stubs(:stopped) unless @client.respond_to?(:stopped)
15
+ @client.stubs(:jid=) unless @client.respond_to?(:jid=)
16
+
17
+ MockServer.any_instance.expects(:receive_data).send(*(times ? [:times, times] : [:at_least, 1])).with &block
18
+ EventMachine::run {
19
+ # Mocked server
20
+ EventMachine::start_server '127.0.0.1', 12345, ServerMock
21
+
22
+ # Stream connection
23
+ EM.connect('127.0.0.1', 12345, Stream::Component, @client, @jid || 'comp.id', 'secret') { |c| @stream = c }
24
+ }
25
+ end
26
+
27
+ it 'can be started' do
28
+ client = mock()
29
+ params = [client, 'comp.id', 'secret', 'host', 1234]
30
+ EM.expects(:connect).with do |*parms|
31
+ parms[0] == 'host' &&
32
+ parms[1] == 1234 &&
33
+ parms[3] == client &&
34
+ parms[4] == 'comp.id'
35
+ end
36
+
37
+ Stream::Component.start *params
38
+ end
39
+
40
+ it 'shakes hands with the server' do
41
+ state = nil
42
+ mocked_server(2) do |val, server|
43
+ case state
44
+ when nil
45
+ state = :started
46
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:component:accept' xmlns:stream='http://etherx.jabber.org/streams' id='12345'>"
47
+ val.must_match(/stream:stream/)
48
+
49
+ when :started
50
+ server.send_data '<handshake/>'
51
+ EM.stop
52
+ val.must_equal "<handshake>#{Digest::SHA1.hexdigest('12345'+"secret")}</handshake>"
53
+
54
+ end
55
+ end
56
+ end
57
+
58
+ it 'starts the stream once the connection is complete' do
59
+ mocked_server(1) { |val, _| EM.stop; val.must_match(/stream:stream/) }
60
+ end
61
+
62
+ it 'sends stanzas to the client when the stream is ready' do
63
+ @client = mock(:stream_started)
64
+ @client.expects(:call).with do |n|
65
+ EM.stop
66
+ n.kind_of?(Stanza::Message) && @stream.ready?.must_equal(true)
67
+ end
68
+
69
+ state = nil
70
+ mocked_server(2) do |val, server|
71
+ case state
72
+ when nil
73
+ state = :started
74
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:component:accept' xmlns:stream='http://etherx.jabber.org/streams' id='12345'>"
75
+ val.must_match(/stream:stream/)
76
+
77
+ when :started
78
+ server.send_data '<handshake/>'
79
+ server.send_data "<message to='comp.id' from='d@e/f' type='chat' xml:lang='en'><body>Message!</body></message>"
80
+ val.must_equal "<handshake>#{Digest::SHA1.hexdigest('12345'+"secret")}</handshake>"
81
+
82
+ end
83
+ end
84
+ end
85
+
86
+ end
@@ -14,38 +14,85 @@ describe 'Blather::XMPPNode' do
14
14
 
15
15
  it 'sets the namespace on creation' do
16
16
  class Foo < XMPPNode; end
17
- Foo.xmlns = 'foo'
18
- Foo.new('foo').xmlns.must_equal 'foo'
17
+ Foo.ns = 'foo'
18
+ Foo.new('foo').namespace.must_equal 'foo'
19
19
  end
20
20
 
21
21
  it 'registers sub classes' do
22
22
  class Foo < XMPPNode; register 'foo', 'foo:bar'; end
23
23
  Foo.name.must_equal 'foo'
24
- Foo.xmlns.must_equal 'foo:bar'
24
+ Foo.ns.must_equal 'foo:bar'
25
25
  XMPPNode.class_from_registration('foo', 'foo:bar').must_equal Foo
26
26
  end
27
27
 
28
28
  it 'imports another node' do
29
29
  class Foo < XMPPNode; register 'foo', 'foo:bar'; end
30
30
  n = XMPPNode.new('foo')
31
- n.xmlns = 'foo:bar'
31
+ n.namespace = 'foo:bar'
32
32
  XMPPNode.import(n).must_be_kind_of Foo
33
33
  end
34
34
 
35
+ it 'provides an attribute_reader' do
36
+ class Foo < XMPPNode
37
+ attribute_reader :foo
38
+ end
39
+ f = Foo.new
40
+ f.must_respond_to :foo
41
+ f.foo.must_be_nil
42
+ f.attributes[:foo] = 'bar'
43
+ f.foo.must_equal :bar
44
+ end
45
+
46
+ it 'provides an attribute_reader and not convert to syms' do
47
+ class Foo < XMPPNode
48
+ attribute_reader :foo, :to_sym => false
49
+ end
50
+ f = Foo.new
51
+ f.must_respond_to :foo
52
+ f.foo.must_be_nil
53
+ f.attributes[:foo] = 'bar'
54
+ f.foo.must_equal 'bar'
55
+ end
56
+
57
+ it 'provides an attribute_writer' do
58
+ class Foo < XMPPNode
59
+ attribute_writer :foo
60
+ end
61
+ f = Foo.new
62
+ f.attributes[:foo].must_be_nil
63
+ f.foo = 'bar'
64
+ f.attributes[:foo].must_equal 'bar'
65
+ end
66
+
67
+ it 'provides an attribute_accessor' do
68
+ class Foo < XMPPNode
69
+ attribute_accessor :foo
70
+ attribute_accessor :bar, :to_sym => false
71
+ end
72
+ f = Foo.new
73
+ f.must_respond_to :foo
74
+ f.foo.must_be_nil
75
+ f.foo = 'bar'
76
+ f.foo.must_equal :bar
77
+
78
+ f.must_respond_to :bar
79
+ f.bar.must_be_nil
80
+ f.bar = 'baz'
81
+ f.bar.must_equal 'baz'
82
+ end
83
+
35
84
  it 'can convert itself into a stanza' do
36
85
  class Foo < XMPPNode; register 'foo'; end
37
86
  n = XMPPNode.new('foo')
38
87
  n.to_stanza.must_be_kind_of Foo
39
88
  end
40
89
 
41
- it 'provides "attr_accessor" for xmlns' do
90
+ it 'provides "attr_accessor" for namespace' do
42
91
  n = XMPPNode.new('foo')
43
- n.xmlns.must_be_nil
44
- n['xmlns'].must_be_nil
92
+ n.namespace.must_be_nil
45
93
 
46
- n.xmlns = 'foo:bar'
47
- n.xmlns.must_equal 'foo:bar'
48
- n['xmlns'].must_equal 'foo:bar'
94
+ n.namespace = 'foo:bar'
95
+ n.namespace.must_equal 'foo:bar'
49
96
  end
50
97
 
51
98
  it 'will remove a child element' do
@@ -53,22 +100,22 @@ describe 'Blather::XMPPNode' do
53
100
  n << XMPPNode.new('bar')
54
101
  n << XMPPNode.new('bar')
55
102
 
56
- n.find('bar').size.must_equal 2
103
+ n.find(:bar).size.must_equal 2
57
104
  n.remove_child 'bar'
58
- n.find('bar').size.must_equal 1
105
+ n.find(:bar).size.must_equal 1
59
106
  end
60
107
 
61
108
  it 'will remove a child with a specific xmlns' do
62
109
  n = XMPPNode.new 'foo'
63
110
  n << XMPPNode.new('bar')
64
111
  c = XMPPNode.new('bar')
65
- c.xmlns = 'foo:bar'
112
+ c.namespace = 'foo:bar'
66
113
  n << c
67
114
 
68
- n.find('bar').size.must_equal 2
115
+ n.find(:bar).size.must_equal 2
69
116
  n.remove_child 'bar', 'foo:bar'
70
- n.find('bar').size.must_equal 1
71
- n.find('bar').first.xmlns.must_be_nil
117
+ n.find(:bar).size.must_equal 1
118
+ n.find(:bar).first.namespace.must_be_nil
72
119
  end
73
120
 
74
121
  it 'will remove all child elements' do
@@ -76,9 +123,9 @@ describe 'Blather::XMPPNode' do
76
123
  n << XMPPNode.new('bar')
77
124
  n << XMPPNode.new('bar')
78
125
 
79
- n.find('bar').size.must_equal 2
126
+ n.find(:bar).size.must_equal 2
80
127
  n.remove_children 'bar'
81
- n.find('bar').size.must_equal 0
128
+ n.find(:bar).size.must_equal 0
82
129
  end
83
130
 
84
131
  it 'provides a helper to grab content from a child' do
@@ -113,18 +160,24 @@ describe 'Blather::XMPPNode' do
113
160
  n['foo'].must_equal 'bar'
114
161
  end
115
162
 
116
- it 'cuts line breaks out of #to_s' do
163
+ it 'cuts line breaks out of #to_xml' do
117
164
  n = XMPPNode.new 'foo'
118
165
  n << XMPPNode.new('bar', 'baz')
119
- n.to_s.scan(">\n<").size.must_equal 0
166
+ n.to_xml.scan(">\n<").size.must_equal 0
120
167
  end
121
168
 
122
169
  it 'overrides #find to find without xpath' do
123
170
  n = XMPPNode.new 'foo'
124
171
  n << XMPPNode.new('bar', 'baz')
125
- n.find('bar').must_be_kind_of Array
172
+ n.find(:bar).must_be_kind_of Array
126
173
 
127
174
  XML::Document.new.root = n
128
- n.find('bar').must_be_kind_of XML::XPath::Object
175
+ n.find(:bar).must_be_kind_of XML::XPath::Object
176
+ end
177
+
178
+ it 'can find using a symbol' do
179
+ n = XMPPNode.new 'foo'
180
+ n << XMPPNode.new('bar', 'baz')
181
+ n.find(:bar).first.to_s.must_equal "<bar>baz</bar>"
129
182
  end
130
183
  end
@@ -0,0 +1,157 @@
1
+ def items_all_nodes_xml
2
+ <<-ITEMS
3
+ <iq type='result'
4
+ from='pubsub.shakespeare.lit'
5
+ to='francisco@denmark.lit/barracks'
6
+ id='items1'>
7
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
8
+ <items node='princely_musings'>
9
+ <item id='368866411b877c30064a5f62b917cffe'>
10
+ <entry xmlns='http://www.w3.org/2005/Atom'>
11
+ <title>The Uses of This World</title>
12
+ <summary>
13
+ O, that this too too solid flesh would melt
14
+ Thaw and resolve itself into a dew!
15
+ </summary>
16
+ <link rel='alternate' type='text/html'
17
+ href='http://denmark.lit/2003/12/13/atom03'/>
18
+ <id>tag:denmark.lit,2003:entry-32396</id>
19
+ <published>2003-12-12T17:47:23Z</published>
20
+ <updated>2003-12-12T17:47:23Z</updated>
21
+ </entry>
22
+ </item>
23
+ <item id='3300659945416e274474e469a1f0154c'>
24
+ <entry xmlns='http://www.w3.org/2005/Atom'>
25
+ <title>Ghostly Encounters</title>
26
+ <summary>
27
+ O all you host of heaven! O earth! what else?
28
+ And shall I couple hell? O, fie! Hold, hold, my heart;
29
+ And you, my sinews, grow not instant old,
30
+ But bear me stiffly up. Remember thee!
31
+ </summary>
32
+ <link rel='alternate' type='text/html'
33
+ href='http://denmark.lit/2003/12/13/atom03'/>
34
+ <id>tag:denmark.lit,2003:entry-32396</id>
35
+ <published>2003-12-12T23:21:34Z</published>
36
+ <updated>2003-12-12T23:21:34Z</updated>
37
+ </entry>
38
+ </item>
39
+ <item id='4e30f35051b7b8b42abe083742187228'>
40
+ <entry xmlns='http://www.w3.org/2005/Atom'>
41
+ <title>Alone</title>
42
+ <summary>
43
+ Now I am alone.
44
+ O, what a rogue and peasant slave am I!
45
+ </summary>
46
+ <link rel='alternate' type='text/html'
47
+ href='http://denmark.lit/2003/12/13/atom03'/>
48
+ <id>tag:denmark.lit,2003:entry-32396</id>
49
+ <published>2003-12-13T11:09:53Z</published>
50
+ <updated>2003-12-13T11:09:53Z</updated>
51
+ </entry>
52
+ </item>
53
+ <item id='ae890ac52d0df67ed7cfdf51b644e901'>
54
+ <entry xmlns='http://www.w3.org/2005/Atom'>
55
+ <title>Soliloquy</title>
56
+ <summary>
57
+ To be, or not to be: that is the question:
58
+ Whether 'tis nobler in the mind to suffer
59
+ The slings and arrows of outrageous fortune,
60
+ Or to take arms against a sea of troubles,
61
+ And by opposing end them?
62
+ </summary>
63
+ <link rel='alternate' type='text/html'
64
+ href='http://denmark.lit/2003/12/13/atom03'/>
65
+ <id>tag:denmark.lit,2003:entry-32397</id>
66
+ <published>2003-12-13T18:30:02Z</published>
67
+ <updated>2003-12-13T18:30:02Z</updated>
68
+ </entry>
69
+ </item>
70
+ </items>
71
+ </pubsub>
72
+ </iq>
73
+ ITEMS
74
+ end
75
+
76
+ def pubsub_items_some_xml
77
+ <<-ITEMS
78
+ <iq type='result'
79
+ from='pubsub.shakespeare.lit'
80
+ to='francisco@denmark.lit/barracks'
81
+ id='items1'>
82
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
83
+ <items node='princely_musings'>
84
+ <item id='368866411b877c30064a5f62b917cffe'>
85
+ <entry xmlns='http://www.w3.org/2005/Atom'>
86
+ <title>The Uses of This World</title>
87
+ <summary>
88
+ O, that this too too solid flesh would melt
89
+ Thaw and resolve itself into a dew!
90
+ </summary>
91
+ <link rel='alternate' type='text/html'
92
+ href='http://denmark.lit/2003/12/13/atom03'/>
93
+ <id>tag:denmark.lit,2003:entry-32396</id>
94
+ <published>2003-12-12T17:47:23Z</published>
95
+ <updated>2003-12-12T17:47:23Z</updated>
96
+ </entry>
97
+ </item>
98
+ <item id='3300659945416e274474e469a1f0154c'>
99
+ <entry xmlns='http://www.w3.org/2005/Atom'>
100
+ <title>Ghostly Encounters</title>
101
+ <summary>
102
+ O all you host of heaven! O earth! what else?
103
+ And shall I couple hell? O, fie! Hold, hold, my heart;
104
+ And you, my sinews, grow not instant old,
105
+ But bear me stiffly up. Remember thee!
106
+ </summary>
107
+ <link rel='alternate' type='text/html'
108
+ href='http://denmark.lit/2003/12/13/atom03'/>
109
+ <id>tag:denmark.lit,2003:entry-32396</id>
110
+ <published>2003-12-12T23:21:34Z</published>
111
+ <updated>2003-12-12T23:21:34Z</updated>
112
+ </entry>
113
+ </item>
114
+ <item id='4e30f35051b7b8b42abe083742187228'>
115
+ <entry xmlns='http://www.w3.org/2005/Atom'>
116
+ <title>Alone</title>
117
+ <summary>
118
+ Now I am alone.
119
+ O, what a rogue and peasant slave am I!
120
+ </summary>
121
+ <link rel='alternate' type='text/html'
122
+ href='http://denmark.lit/2003/12/13/atom03'/>
123
+ <id>tag:denmark.lit,2003:entry-32396</id>
124
+ <published>2003-12-13T11:09:53Z</published>
125
+ <updated>2003-12-13T11:09:53Z</updated>
126
+ </entry>
127
+ </item>
128
+ </items>
129
+ <set xmlns='http://jabber.org/protocol/rsm'>
130
+ <first index='0'>368866411b877c30064a5f62b917cffe</first>
131
+ <last>4e30f35051b7b8b42abe083742187228</last>
132
+ <count>19</count>
133
+ </set>
134
+ </pubsub>
135
+ </iq>
136
+ ITEMS
137
+ end
138
+
139
+ def affiliations_xml
140
+ <<-NODE
141
+ <iq type='result'
142
+ from='pubsub.shakespeare.lit'
143
+ to='francisco@denmark.lit'
144
+ id='affil1'>
145
+ <pubsub xmlns='http://jabber.org/protocol/pubsub'>
146
+ <affiliations>
147
+ <affiliation node='node1' affiliation='owner'/>
148
+ <affiliation node='node2' affiliation='owner'/>
149
+ <affiliation node='node3' affiliation='publisher'/>
150
+ <affiliation node='node4' affiliation='outcast'/>
151
+ <affiliation node='node5' affiliation='member'/>
152
+ <affiliation node='node6' affiliation='none'/>
153
+ </affiliations>
154
+ </pubsub>
155
+ </iq>
156
+ NODE
157
+ end
@@ -2,27 +2,23 @@ require File.join(File.dirname(__FILE__), *%w[.. lib blather])
2
2
  require 'rubygems'
3
3
  require 'minitest/spec'
4
4
  require 'mocha'
5
+ require 'mocha/expectation_error'
5
6
 
6
7
  module MiniTest
7
- if MINI_DIR =~ %r{^./}
8
- require 'pathname'
9
- path = Pathname.new(MINI_DIR).realpath
10
- # remove_const 'MINI_DIR'
11
- # const_set 'MINI_DIR', path.to_s
12
- end
8
+ require 'pathname' if MINI_DIR =~ %r{^./}
13
9
 
14
10
  module Assertions
15
- def assert_change(obj, method, args = {}, msg = nil)
11
+ def assert_change(stmt, args = {}, msg = nil)
16
12
  msg ||= proc {
17
- m = "Expected #{obj}.#{method} to change"
13
+ m = "Expected #{stmt} to change"
18
14
  m << " by #{mu_pp args[:by]}" if args[:by]
19
15
  m << (args[:from] ? " from #{mu_pp args[:from]}" : '') + " to #{mu_pp args[:to]}" if args[:to]
20
16
  m
21
17
  }.call
22
18
 
23
- init_val = eval(obj).__send__(method)
19
+ init_val = eval(stmt)
24
20
  yield
25
- new_val = eval(obj).__send__(method)
21
+ new_val = eval(stmt)
26
22
 
27
23
  assert_equal(args[:by], (new_val - init_val), msg) if args[:by]
28
24
  assert_equal([args[:from], args[:to]], [(init_val if args[:from]), new_val], msg) if args[:to]
@@ -37,11 +33,7 @@ class Object
37
33
  end
38
34
  end
39
35
 
40
- require 'mocha/expectation_error'
41
-
42
36
  include Blather
43
37
  include MiniTest
44
38
 
45
- LOG.level = Logger::INFO
46
-
47
39
  Unit.autorun