blather 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.rdoc +41 -12
- data/examples/echo.rb +1 -1
- data/examples/execute.rb +0 -5
- data/examples/pubsub/cli.rb +64 -0
- data/examples/pubsub/ping_pong.rb +18 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/lib/blather.rb +35 -12
- data/lib/blather/client.rb +1 -1
- data/lib/blather/client/client.rb +19 -13
- data/lib/blather/client/dsl.rb +16 -0
- data/lib/blather/client/dsl/pubsub.rb +133 -0
- data/lib/blather/core_ext/active_support.rb +1 -117
- data/lib/blather/core_ext/active_support/inheritable_attributes.rb +117 -0
- data/lib/blather/core_ext/nokogiri.rb +35 -0
- data/lib/blather/errors.rb +3 -20
- data/lib/blather/errors/sasl_error.rb +3 -1
- data/lib/blather/errors/stanza_error.rb +10 -17
- data/lib/blather/errors/stream_error.rb +11 -14
- data/lib/blather/jid.rb +1 -0
- data/lib/blather/roster.rb +9 -0
- data/lib/blather/roster_item.rb +6 -1
- data/lib/blather/stanza.rb +35 -18
- data/lib/blather/stanza/disco.rb +7 -1
- data/lib/blather/stanza/disco/disco_info.rb +45 -33
- data/lib/blather/stanza/disco/disco_items.rb +32 -21
- data/lib/blather/stanza/iq.rb +13 -8
- data/lib/blather/stanza/iq/query.rb +16 -8
- data/lib/blather/stanza/iq/roster.rb +33 -22
- data/lib/blather/stanza/message.rb +20 -31
- data/lib/blather/stanza/presence.rb +3 -5
- data/lib/blather/stanza/presence/status.rb +13 -21
- data/lib/blather/stanza/presence/subscription.rb +11 -16
- data/lib/blather/stanza/pubsub.rb +63 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +50 -0
- data/lib/blather/stanza/pubsub/create.rb +43 -0
- data/lib/blather/stanza/pubsub/errors.rb +9 -0
- data/lib/blather/stanza/pubsub/event.rb +77 -0
- data/lib/blather/stanza/pubsub/items.rb +63 -0
- data/lib/blather/stanza/pubsub/publish.rb +58 -0
- data/lib/blather/stanza/pubsub/retract.rb +53 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +42 -0
- data/lib/blather/stanza/pubsub/subscription.rb +66 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +55 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +42 -0
- data/lib/blather/stanza/pubsub_owner.rb +41 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +34 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +34 -0
- data/lib/blather/stream.rb +76 -168
- data/lib/blather/stream/client.rb +1 -2
- data/lib/blather/stream/component.rb +9 -5
- data/lib/blather/stream/features.rb +53 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/{sasl.rb → features/sasl.rb} +53 -52
- data/lib/blather/stream/features/session.rb +44 -0
- data/lib/blather/stream/features/tls.rb +28 -0
- data/lib/blather/stream/parser.rb +70 -46
- data/lib/blather/xmpp_node.rb +113 -52
- data/spec/blather/client/client_spec.rb +44 -58
- data/spec/blather/client/dsl/pubsub_spec.rb +465 -0
- data/spec/blather/client/dsl_spec.rb +19 -6
- data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
- data/spec/blather/errors/sasl_error_spec.rb +8 -8
- data/spec/blather/errors/stanza_error_spec.rb +25 -33
- data/spec/blather/errors/stream_error_spec.rb +21 -16
- data/spec/blather/errors_spec.rb +4 -11
- data/spec/blather/jid_spec.rb +31 -30
- data/spec/blather/roster_item_spec.rb +34 -23
- data/spec/blather/roster_spec.rb +27 -12
- data/spec/blather/stanza/discos/disco_info_spec.rb +61 -42
- data/spec/blather/stanza/discos/disco_items_spec.rb +47 -35
- data/spec/blather/stanza/iq/query_spec.rb +34 -11
- data/spec/blather/stanza/iq/roster_spec.rb +47 -30
- data/spec/blather/stanza/iq_spec.rb +19 -14
- data/spec/blather/stanza/message_spec.rb +30 -17
- data/spec/blather/stanza/presence/status_spec.rb +43 -20
- data/spec/blather/stanza/presence/subscription_spec.rb +41 -21
- data/spec/blather/stanza/presence_spec.rb +34 -21
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
- data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
- data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
- data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
- data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
- data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
- data/spec/blather/stanza/pubsub_spec.rb +62 -0
- data/spec/blather/stanza_spec.rb +53 -38
- data/spec/blather/stream/client_spec.rb +231 -88
- data/spec/blather/stream/component_spec.rb +14 -5
- data/spec/blather/stream/parser_spec.rb +145 -0
- data/spec/blather/xmpp_node_spec.rb +192 -96
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +5 -4
- metadata +54 -18
- data/Rakefile +0 -139
- data/ext/extconf.rb +0 -65
- data/ext/push_parser.c +0 -209
- data/lib/blather/core_ext/libxml.rb +0 -28
- data/lib/blather/stream/resource.rb +0 -48
- data/lib/blather/stream/session.rb +0 -36
- data/lib/blather/stream/stream_handler.rb +0 -39
- data/lib/blather/stream/tls.rb +0 -33
- data/spec/blather/core_ext/libxml_spec.rb +0 -58
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe Blather::Stream::Component do
|
4
4
|
class MockServer; end
|
5
5
|
module ServerMock
|
6
6
|
def receive_data(data)
|
@@ -19,8 +19,8 @@ describe 'Blather::Stream::Component' do
|
|
19
19
|
# Mocked server
|
20
20
|
EventMachine::start_server '127.0.0.1', 12345, ServerMock
|
21
21
|
|
22
|
-
# Stream connection
|
23
|
-
EM.connect('127.0.0.1', 12345, Stream::Component, @client, @jid || 'comp.id', 'secret') { |c| @stream = c }
|
22
|
+
# Blather::Stream connection
|
23
|
+
EM.connect('127.0.0.1', 12345, Blather::Stream::Component, @client, @jid || 'comp.id', 'secret') { |c| @stream = c }
|
24
24
|
}
|
25
25
|
end
|
26
26
|
|
@@ -34,7 +34,7 @@ describe 'Blather::Stream::Component' do
|
|
34
34
|
parms[4] == 'comp.id'
|
35
35
|
end
|
36
36
|
|
37
|
-
Stream::Component.start *params
|
37
|
+
Blather::Stream::Component.start *params
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'shakes hands with the server' do
|
@@ -63,7 +63,7 @@ describe 'Blather::Stream::Component' do
|
|
63
63
|
@client = mock(:post_init)
|
64
64
|
@client.expects(:receive_data).with do |n|
|
65
65
|
EM.stop
|
66
|
-
n.kind_of?
|
66
|
+
n.kind_of? Blather::XMPPNode
|
67
67
|
end
|
68
68
|
|
69
69
|
state = nil
|
@@ -83,4 +83,13 @@ 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
|
87
|
+
client = mock()
|
88
|
+
client.stubs(:jid)
|
89
|
+
client.stubs(:jid=)
|
90
|
+
msg = Blather::Stanza::Message.new 'to@jid.com', 'body'
|
91
|
+
comp = Blather::Stream::Component.new nil, client, 'jid.com', 'pass'
|
92
|
+
comp.expects(:send_data).with { |s| s.must_match(/^<message[^>]*from="jid\.com"/) }
|
93
|
+
comp.send msg
|
94
|
+
end
|
86
95
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
|
2
|
+
|
3
|
+
describe Blather::Stream::Parser do
|
4
|
+
before do
|
5
|
+
@client = Class.new do
|
6
|
+
attr_reader :data
|
7
|
+
def stopped?; false; end
|
8
|
+
def receive(data)
|
9
|
+
@data ||= []
|
10
|
+
@data << data
|
11
|
+
end
|
12
|
+
end.new
|
13
|
+
@parser = Blather::Stream::Parser.new @client
|
14
|
+
end
|
15
|
+
|
16
|
+
after { @client = @parser = nil}
|
17
|
+
|
18
|
+
def check_parse(data)
|
19
|
+
@parser.receive_data data
|
20
|
+
@client.data.size.must_equal 1
|
21
|
+
@client.data[0].to_s.gsub(/\n\s*/,'').must_equal data
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'handles fragmented parsing' do
|
25
|
+
@parser.receive_data '<foo>'
|
26
|
+
@parser.receive_data '<bar/>'
|
27
|
+
@parser.receive_data '</foo>'
|
28
|
+
@client.data.size.must_equal 1
|
29
|
+
@client.data[0].to_s.gsub(/\n\s*/,'').must_equal '<foo><bar/></foo>'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'handles a basic example' do
|
33
|
+
check_parse "<foo/>"
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'handles a basic namespace definition' do
|
37
|
+
check_parse '<foo xmlns="bar:baz"/>'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'handles multiple namespace definitions' do
|
41
|
+
check_parse '<foo xmlns="bar:baz" xmlns:bar="baz"/>'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'handles prefix namespacing' do
|
45
|
+
check_parse '<bar:foo xmlns="bar:baz" xmlns:bar="baz"/>'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'handles namespaces with children' do
|
49
|
+
check_parse "<foo xmlns=\"bar:baz\"><bar/></foo>"
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'handles multiple namespaces with children' do
|
53
|
+
check_parse "<foo xmlns=\"bar:baz\" xmlns:bar=\"baz\"><bar/></foo>"
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'handles prefix namespaces with children' do
|
57
|
+
check_parse "<bar:foo xmlns=\"bar:baz\" xmlns:bar=\"baz\"><bar/></bar:foo>"
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'handles prefix namespaces with children in the namespace' do
|
61
|
+
check_parse "<bar:foo xmlns=\"bar:baz\" xmlns:bar=\"baz\"><bar:bar/></bar:foo>"
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'handles prefix namespaces with children in the namespace' do
|
65
|
+
check_parse "<bar:foo xmlns=\"bar:baz\" xmlns:bar=\"baz\"><baz><bar:buz/></baz></bar:foo>"
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'handles attributes' do
|
69
|
+
check_parse '<foo bar="baz"/>'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'handles character input' do
|
73
|
+
check_parse '<foo>bar baz fizbuz</foo>'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'handles a complex input' do
|
77
|
+
data = [
|
78
|
+
'<message type="error" id="another-id">',
|
79
|
+
'<error type="modify">',
|
80
|
+
'<undefined-condition xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>',
|
81
|
+
'<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Some special application diagnostic information...</text>',
|
82
|
+
'<special-application-condition xmlns="application-ns"/>',
|
83
|
+
"</error>",
|
84
|
+
"</message>"
|
85
|
+
]
|
86
|
+
data.each { |d| @parser.receive_data d }
|
87
|
+
@client.data.size.must_equal 1
|
88
|
+
@client.data[0].to_s.split("\n").map{|n|n.strip}.must_equal data
|
89
|
+
@client.data[0].xpath('//*[namespace-uri()="urn:ietf:params:xml:ns:xmpp-stanzas"]').size.must_equal 2
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'responds with stream:stream as a separate response' do
|
93
|
+
data = [
|
94
|
+
'<stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" to="example.com" version="1.0">',
|
95
|
+
'<foo/>'
|
96
|
+
]
|
97
|
+
data.each { |d| @parser.receive_data d }
|
98
|
+
@client.data.size.must_equal 2
|
99
|
+
@client.data[0].document.xpath('/stream:stream[@to="example.com" and @version="1.0"]', 'xmlns' => 'jabber:client', 'stream' => 'http://etherx.jabber.org/streams').size.must_equal 1
|
100
|
+
@client.data[1].to_s.must_equal '<foo/>'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'response with stream:end when receiving </stream:stream>' do
|
104
|
+
@parser.receive_data '<stream:stream xmlns:stream="http://etherx.jabber.org/streams"/>'
|
105
|
+
@client.data.size.must_equal 2
|
106
|
+
@client.data[1].to_s.must_equal '<stream:end xmlns:stream="http://etherx.jabber.org/streams"/>'
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'raises ParseError when an error is sent' do
|
110
|
+
lambda { @parser.receive_data "<stream:stream>" }.must_raise(Blather::ParseError)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'handles stream stanzas without an issue' do
|
114
|
+
data = [
|
115
|
+
'<stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" to="example.com" version="1.0">',
|
116
|
+
'<stream:features/>'
|
117
|
+
]
|
118
|
+
data.each { |d| @parser.receive_data d }
|
119
|
+
@client.data.size.must_equal 2
|
120
|
+
@client.data[0].document.xpath('/stream:stream[@to="example.com" and @version="1.0"]', 'xmlns' => 'jabber:client', 'stream' => 'http://etherx.jabber.org/streams').size.must_equal 1
|
121
|
+
@client.data[1].to_s.must_equal '<stream:features xmlns:stream="http://etherx.jabber.org/streams"/>'
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'ignores the client namespace on stanzas' do
|
125
|
+
[ "<message type='chat' to='n@d' from='n@d/r' id='id1' xmlns='jabber:client'>",
|
126
|
+
"<body>exit</body>",
|
127
|
+
"<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>exit</body></html>",
|
128
|
+
"</message>"
|
129
|
+
].each { |d| @parser.receive_data d }
|
130
|
+
@client.data.size.must_equal 1
|
131
|
+
@client.data[0].document.xpath('/message/body[.="exit"]').wont_be_empty
|
132
|
+
@client.data[0].document.xpath('/message/im:html/xhtml:body[.="exit"]', 'im' => 'http://jabber.org/protocol/xhtml-im', 'xhtml' => 'http://www.w3.org/1999/xhtml').wont_be_empty
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'ignores the component namespace on stanzas' do
|
136
|
+
[ "<message type='chat' to='n@d' from='n@d/r' id='id1' xmlns='jabber:component:accept'>",
|
137
|
+
"<body>exit</body>",
|
138
|
+
"<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>exit</body></html>",
|
139
|
+
"</message>"
|
140
|
+
].each { |d| @parser.receive_data d }
|
141
|
+
@client.data.size.must_equal 1
|
142
|
+
@client.data[0].document.xpath('/message/body[.="exit"]').wont_be_empty
|
143
|
+
@client.data[0].document.xpath('/message/im:html/xhtml:body[.="exit"]', 'im' => 'http://jabber.org/protocol/xhtml-im', 'xhtml' => 'http://www.w3.org/1999/xhtml').wont_be_empty
|
144
|
+
end
|
145
|
+
end
|
@@ -1,104 +1,182 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), *%w[.. spec_helper])
|
2
2
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
3
|
+
describe Blather::XMPPNode do
|
4
|
+
before { @doc = Nokogiri::XML::Document.new }
|
5
|
+
|
6
|
+
it 'generates a new node automatically setting the document' do
|
7
|
+
n = Blather::XMPPNode.new 'foo'
|
6
8
|
n.element_name.must_equal 'foo'
|
9
|
+
n.document.wont_equal @doc
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'sets the new document root to the node' do
|
13
|
+
n = Blather::XMPPNode.new 'foo'
|
14
|
+
n.document.root.must_equal n
|
7
15
|
end
|
8
16
|
|
9
|
-
it '
|
10
|
-
|
11
|
-
|
12
|
-
|
17
|
+
it 'does not set the document root if the document is provided' do
|
18
|
+
n = Blather::XMPPNode.new 'foo', @doc
|
19
|
+
n.document.root.wont_equal n
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'generates a new node with the given document' do
|
23
|
+
n = Blather::XMPPNode.new 'foo', @doc
|
24
|
+
n.element_name.must_equal 'foo'
|
25
|
+
n.document.must_equal @doc
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'generates a node based on the registered_name' do
|
29
|
+
foo = Class.new(Blather::XMPPNode)
|
30
|
+
foo.registered_name = 'foo'
|
31
|
+
foo.new.element_name.must_equal 'foo'
|
13
32
|
end
|
14
33
|
|
15
34
|
it 'sets the namespace on creation' do
|
16
|
-
|
17
|
-
|
18
|
-
|
35
|
+
foo = Class.new(Blather::XMPPNode)
|
36
|
+
foo.registered_ns = 'foo'
|
37
|
+
foo.new('foo').namespace.href.must_equal 'foo'
|
19
38
|
end
|
20
39
|
|
21
40
|
it 'registers sub classes' do
|
22
|
-
class
|
23
|
-
|
24
|
-
|
25
|
-
XMPPNode.class_from_registration('foo', 'foo:bar').must_equal
|
41
|
+
class RegistersSubClass < Blather::XMPPNode; register 'foo', 'foo:bar'; end
|
42
|
+
RegistersSubClass.registered_name.must_equal 'foo'
|
43
|
+
RegistersSubClass.registered_ns.must_equal 'foo:bar'
|
44
|
+
Blather::XMPPNode.class_from_registration('foo', 'foo:bar').must_equal RegistersSubClass
|
26
45
|
end
|
27
46
|
|
28
47
|
it 'imports another node' do
|
29
|
-
class
|
30
|
-
n = XMPPNode.new('foo')
|
48
|
+
class ImportSubClass < Blather::XMPPNode; register 'foo', 'foo:bar'; end
|
49
|
+
n = Blather::XMPPNode.new('foo')
|
31
50
|
n.namespace = 'foo:bar'
|
32
|
-
XMPPNode.import(n).must_be_kind_of
|
51
|
+
Blather::XMPPNode.import(n).must_be_kind_of ImportSubClass
|
33
52
|
end
|
34
53
|
|
35
54
|
it 'provides an attribute_reader' do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
+
foo = Class.new(Blather::XMPPNode) { attribute_reader :bar }.new
|
56
|
+
foo.must_respond_to :bar
|
57
|
+
foo.bar.must_be_nil
|
58
|
+
foo[:bar] = 'baz'
|
59
|
+
foo.bar.must_equal 'baz'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'provides an attribute_reader with converstion' do
|
63
|
+
foo = Class.new(Blather::XMPPNode) { attribute_reader :bar, :call => :to_sym }.new
|
64
|
+
foo.must_respond_to :bar
|
65
|
+
foo.bar.must_be_nil
|
66
|
+
foo[:bar] = 'baz'
|
67
|
+
foo.bar.must_equal :baz
|
55
68
|
end
|
56
69
|
|
57
70
|
it 'provides an attribute_writer' do
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
f.attributes[:foo].must_be_nil
|
63
|
-
f.foo = 'bar'
|
64
|
-
f.attributes[:foo].must_equal 'bar'
|
71
|
+
foo = Class.new(Blather::XMPPNode) { attribute_writer :bar }.new
|
72
|
+
foo[:bar].must_be_nil
|
73
|
+
foo.bar = 'baz'
|
74
|
+
foo[:bar].must_equal 'baz'
|
65
75
|
end
|
66
76
|
|
67
77
|
it 'provides an attribute_accessor' do
|
68
|
-
|
69
|
-
attribute_accessor :
|
70
|
-
attribute_accessor :
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
foo = Class.new(Blather::XMPPNode) do
|
79
|
+
attribute_accessor :bar, :call => :to_sym
|
80
|
+
attribute_accessor :baz
|
81
|
+
end.new
|
82
|
+
foo.must_respond_to :bar
|
83
|
+
foo.bar.must_be_nil
|
84
|
+
foo.bar = 'fiz'
|
85
|
+
foo.bar.must_equal :fiz
|
86
|
+
|
87
|
+
foo.must_respond_to :baz
|
88
|
+
foo.baz.must_be_nil
|
89
|
+
foo.baz = 'buz'
|
90
|
+
foo.baz.must_equal 'buz'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'provides a content reader' do
|
94
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_reader :bar }.new('foo')
|
95
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
96
|
+
bar.content = 'baz'
|
97
|
+
foo.must_respond_to :bar
|
98
|
+
foo.bar.must_equal 'baz'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'provides a content reader that converts the value' do
|
102
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_reader :bar, :to_sym }.new('foo')
|
103
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
104
|
+
bar.content = 'baz'
|
105
|
+
foo.must_respond_to :bar
|
106
|
+
foo.bar.must_equal :baz
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'provides a content reader with a different node' do
|
110
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_reader :bar, nil, :fiz }.new('foo')
|
111
|
+
foo << (fiz = Blather::XMPPNode.new('fiz', foo.document))
|
112
|
+
fiz.content = 'baz'
|
113
|
+
foo.must_respond_to :bar
|
114
|
+
foo.bar.must_equal 'baz'
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'provides a content writer' do
|
118
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_writer :bar }.new('foo')
|
119
|
+
foo.must_respond_to :bar=
|
120
|
+
foo.bar = 'baz'
|
121
|
+
foo.content_from(:bar).must_equal 'baz'
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'provides a content writer with a different node' do
|
125
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_writer :bar, :fiz }.new('foo')
|
126
|
+
foo.must_respond_to :bar=
|
127
|
+
foo.bar = 'baz'
|
128
|
+
foo.content_from(:fiz).must_equal 'baz'
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'provides a content accessor' do
|
132
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_accessor :bar }.new('foo')
|
133
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
134
|
+
foo.must_respond_to :bar=
|
135
|
+
foo.must_respond_to :bar
|
136
|
+
foo.bar = 'baz'
|
137
|
+
foo.bar.must_equal 'baz'
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'provides a content accessor with conversion' do
|
141
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_accessor :bar, :to_sym }.new('foo')
|
142
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
143
|
+
foo.must_respond_to :bar=
|
144
|
+
foo.must_respond_to :bar
|
145
|
+
foo.bar = 'baz'
|
146
|
+
foo.bar.must_equal :baz
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'provides a content writer that removes a child when set to nil' do
|
150
|
+
foo = Class.new(Blather::XMPPNode) { content_attr_writer :bar }.new('foo')
|
151
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
152
|
+
bar.content = 'baz'
|
153
|
+
foo.content_from(:bar).must_equal 'baz'
|
154
|
+
foo.xpath('bar').wont_be_empty
|
155
|
+
|
156
|
+
foo.must_respond_to :bar=
|
157
|
+
foo.bar = nil
|
158
|
+
foo.content_from(:bar).must_be_nil
|
159
|
+
foo.xpath('bar').must_be_empty
|
82
160
|
end
|
83
161
|
|
84
162
|
it 'can convert itself into a stanza' do
|
85
|
-
class
|
86
|
-
n = XMPPNode.new('foo')
|
87
|
-
n.to_stanza.must_be_kind_of
|
163
|
+
class StanzaConvert < Blather::XMPPNode; register 'foo'; end
|
164
|
+
n = Blather::XMPPNode.new('foo')
|
165
|
+
n.to_stanza.must_be_kind_of StanzaConvert
|
88
166
|
end
|
89
167
|
|
90
168
|
it 'provides "attr_accessor" for namespace' do
|
91
|
-
n = XMPPNode.new('foo')
|
169
|
+
n = Blather::XMPPNode.new('foo')
|
92
170
|
n.namespace.must_be_nil
|
93
171
|
|
94
172
|
n.namespace = 'foo:bar'
|
95
|
-
n.
|
173
|
+
n.namespace_href.must_equal 'foo:bar'
|
96
174
|
end
|
97
175
|
|
98
176
|
it 'will remove a child element' do
|
99
|
-
n = XMPPNode.new 'foo'
|
100
|
-
n << XMPPNode.new('bar')
|
101
|
-
n << XMPPNode.new('bar')
|
177
|
+
n = Blather::XMPPNode.new 'foo'
|
178
|
+
n << Blather::XMPPNode.new('bar', n.document)
|
179
|
+
n << Blather::XMPPNode.new('bar', n.document)
|
102
180
|
|
103
181
|
n.find(:bar).size.must_equal 2
|
104
182
|
n.remove_child 'bar'
|
@@ -106,9 +184,9 @@ describe 'Blather::XMPPNode' do
|
|
106
184
|
end
|
107
185
|
|
108
186
|
it 'will remove a child with a specific xmlns' do
|
109
|
-
n = XMPPNode.new 'foo'
|
110
|
-
n << XMPPNode.new('bar')
|
111
|
-
c = XMPPNode.new('bar')
|
187
|
+
n = Blather::XMPPNode.new 'foo'
|
188
|
+
n << Blather::XMPPNode.new('bar')
|
189
|
+
c = Blather::XMPPNode.new('bar')
|
112
190
|
c.namespace = 'foo:bar'
|
113
191
|
n << c
|
114
192
|
|
@@ -119,31 +197,26 @@ describe 'Blather::XMPPNode' do
|
|
119
197
|
end
|
120
198
|
|
121
199
|
it 'will remove all child elements' do
|
122
|
-
n = XMPPNode.new 'foo'
|
123
|
-
n << XMPPNode.new('bar')
|
124
|
-
n << XMPPNode.new('bar')
|
200
|
+
n = Blather::XMPPNode.new 'foo'
|
201
|
+
n << Blather::XMPPNode.new('bar')
|
202
|
+
n << Blather::XMPPNode.new('bar')
|
125
203
|
|
126
204
|
n.find(:bar).size.must_equal 2
|
127
205
|
n.remove_children 'bar'
|
128
206
|
n.find(:bar).size.must_equal 0
|
129
207
|
end
|
130
208
|
|
131
|
-
it 'provides a helper to grab content from a child' do
|
132
|
-
n = XMPPNode.new 'foo'
|
133
|
-
n << XMPPNode.new('bar', 'baz')
|
134
|
-
n.content_from(:bar).must_equal 'baz'
|
135
|
-
end
|
136
|
-
|
137
209
|
it 'provides a copy mechanism' do
|
138
|
-
n = XMPPNode.new 'foo'
|
210
|
+
n = Blather::XMPPNode.new 'foo'
|
139
211
|
n2 = n.copy
|
140
212
|
n2.object_id.wont_equal n.object_id
|
141
213
|
n2.element_name.must_equal n.element_name
|
142
214
|
end
|
143
215
|
|
144
216
|
it 'provides an inhert mechanism' do
|
145
|
-
n = XMPPNode.new 'foo'
|
146
|
-
n2 = XMPPNode.new 'foo'
|
217
|
+
n = Blather::XMPPNode.new 'foo'
|
218
|
+
n2 = Blather::XMPPNode.new 'foo'
|
219
|
+
n2.content = 'bar'
|
147
220
|
n2['foo'] = 'bar'
|
148
221
|
|
149
222
|
n.inherit(n2)
|
@@ -152,32 +225,55 @@ describe 'Blather::XMPPNode' do
|
|
152
225
|
end
|
153
226
|
|
154
227
|
it 'provides a mechanism to inherit attrs' do
|
155
|
-
n = XMPPNode.new 'foo'
|
156
|
-
n2 = XMPPNode.new 'foo'
|
228
|
+
n = Blather::XMPPNode.new 'foo'
|
229
|
+
n2 = Blather::XMPPNode.new 'foo'
|
157
230
|
n2['foo'] = 'bar'
|
158
231
|
|
159
232
|
n.inherit_attrs(n2.attributes)
|
160
233
|
n['foo'].must_equal 'bar'
|
161
234
|
end
|
162
235
|
|
163
|
-
it '
|
164
|
-
|
165
|
-
|
166
|
-
|
236
|
+
it 'has a content_from helper that pulls the content from a child node' do
|
237
|
+
f = Blather::XMPPNode.new('foo')
|
238
|
+
f << (b = Blather::XMPPNode.new('bar'))
|
239
|
+
b.content = 'content'
|
240
|
+
f.content_from(:bar).must_equal 'content'
|
167
241
|
end
|
168
242
|
|
169
|
-
it '
|
170
|
-
|
171
|
-
|
172
|
-
|
243
|
+
it 'returns nil when sent #content_from and a missing node' do
|
244
|
+
f = Blather::XMPPNode.new('foo')
|
245
|
+
f.content_from(:bar).must_be_nil
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'creates a new node and sets content when sent #set_content_for' do
|
249
|
+
f = Blather::XMPPNode.new('foo')
|
250
|
+
f.must_respond_to :set_content_for
|
251
|
+
f.xpath('bar').must_be_empty
|
252
|
+
f.set_content_for :bar, :baz
|
253
|
+
f.xpath('bar').wont_be_empty
|
254
|
+
f.xpath('bar').first.content.must_equal 'baz'
|
255
|
+
end
|
173
256
|
|
174
|
-
|
175
|
-
|
257
|
+
it 'removes a child node when sent #set_content_for with nil' do
|
258
|
+
f = Blather::XMPPNode.new('foo')
|
259
|
+
f << (b = Blather::XMPPNode.new('bar'))
|
260
|
+
f.must_respond_to :set_content_for
|
261
|
+
f.xpath('bar').wont_be_empty
|
262
|
+
f.set_content_for :bar, nil
|
263
|
+
f.xpath('bar').must_be_empty
|
176
264
|
end
|
177
265
|
|
178
|
-
it '
|
179
|
-
|
180
|
-
|
181
|
-
|
266
|
+
it 'will change the content of an existing node when sent #set_content_for' do
|
267
|
+
f = Blather::XMPPNode.new('foo')
|
268
|
+
f << (b = Blather::XMPPNode.new('bar'))
|
269
|
+
b.content = 'baz'
|
270
|
+
f.must_respond_to :set_content_for
|
271
|
+
f.xpath('bar').wont_be_empty
|
272
|
+
f.xpath('bar').first.content.must_equal 'baz'
|
273
|
+
control = f.xpath('bar').first.pointer_id
|
274
|
+
|
275
|
+
f.set_content_for :bar, 'fiz'
|
276
|
+
f.xpath('bar').first.content.must_equal 'fiz'
|
277
|
+
f.xpath('bar').first.pointer_id.must_equal control
|
182
278
|
end
|
183
279
|
end
|