blather 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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