shingara-blather 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +162 -0
- data/examples/echo.rb +18 -0
- data/examples/execute.rb +16 -0
- data/examples/ping_pong.rb +37 -0
- data/examples/print_hierarchy.rb +76 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/stream_only.rb +27 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/lib/blather/client/client.rb +310 -0
- data/lib/blather/client/dsl/pubsub.rb +170 -0
- data/lib/blather/client/dsl.rb +264 -0
- data/lib/blather/client.rb +87 -0
- data/lib/blather/core_ext/nokogiri.rb +40 -0
- data/lib/blather/errors/sasl_error.rb +43 -0
- data/lib/blather/errors/stanza_error.rb +107 -0
- data/lib/blather/errors/stream_error.rb +82 -0
- data/lib/blather/errors.rb +69 -0
- data/lib/blather/jid.rb +142 -0
- data/lib/blather/roster.rb +111 -0
- data/lib/blather/roster_item.rb +122 -0
- data/lib/blather/stanza/disco/disco_info.rb +176 -0
- data/lib/blather/stanza/disco/disco_items.rb +132 -0
- data/lib/blather/stanza/disco.rb +25 -0
- data/lib/blather/stanza/iq/query.rb +53 -0
- data/lib/blather/stanza/iq/roster.rb +179 -0
- data/lib/blather/stanza/iq.rb +138 -0
- data/lib/blather/stanza/message.rb +332 -0
- data/lib/blather/stanza/presence/status.rb +212 -0
- data/lib/blather/stanza/presence/subscription.rb +101 -0
- data/lib/blather/stanza/presence.rb +163 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
- data/lib/blather/stanza/pubsub/create.rb +65 -0
- data/lib/blather/stanza/pubsub/errors.rb +18 -0
- data/lib/blather/stanza/pubsub/event.rb +123 -0
- data/lib/blather/stanza/pubsub/items.rb +103 -0
- data/lib/blather/stanza/pubsub/publish.rb +103 -0
- data/lib/blather/stanza/pubsub/retract.rb +92 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
- data/lib/blather/stanza/pubsub/subscription.rb +134 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +81 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +68 -0
- data/lib/blather/stanza/pubsub.rb +129 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
- data/lib/blather/stanza/pubsub_owner.rb +51 -0
- data/lib/blather/stanza.rb +149 -0
- data/lib/blather/stream/client.rb +31 -0
- data/lib/blather/stream/component.rb +38 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/features/sasl.rb +187 -0
- data/lib/blather/stream/features/session.rb +44 -0
- data/lib/blather/stream/features/tls.rb +28 -0
- data/lib/blather/stream/features.rb +53 -0
- data/lib/blather/stream/parser.rb +102 -0
- data/lib/blather/stream.rb +231 -0
- data/lib/blather/xmpp_node.rb +218 -0
- data/lib/blather.rb +78 -0
- data/spec/blather/client/client_spec.rb +559 -0
- data/spec/blather/client/dsl/pubsub_spec.rb +462 -0
- data/spec/blather/client/dsl_spec.rb +143 -0
- data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
- data/spec/blather/errors/sasl_error_spec.rb +33 -0
- data/spec/blather/errors/stanza_error_spec.rb +129 -0
- data/spec/blather/errors/stream_error_spec.rb +108 -0
- data/spec/blather/errors_spec.rb +33 -0
- data/spec/blather/jid_spec.rb +87 -0
- data/spec/blather/roster_item_spec.rb +96 -0
- data/spec/blather/roster_spec.rb +103 -0
- data/spec/blather/stanza/discos/disco_info_spec.rb +226 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +148 -0
- data/spec/blather/stanza/iq/query_spec.rb +64 -0
- data/spec/blather/stanza/iq/roster_spec.rb +140 -0
- data/spec/blather/stanza/iq_spec.rb +45 -0
- data/spec/blather/stanza/message_spec.rb +132 -0
- data/spec/blather/stanza/presence/status_spec.rb +132 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +105 -0
- data/spec/blather/stanza/presence_spec.rb +66 -0
- 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 +67 -0
- data/spec/blather/stanza_spec.rb +116 -0
- data/spec/blather/stream/client_spec.rb +1011 -0
- data/spec/blather/stream/component_spec.rb +95 -0
- data/spec/blather/stream/parser_spec.rb +145 -0
- data/spec/blather/xmpp_node_spec.rb +231 -0
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +43 -0
- metadata +249 -0
@@ -0,0 +1,95 @@
|
|
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(:unbind) unless @client.respond_to?(:unbind)
|
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
|
+
# Blather::Stream connection
|
23
|
+
EM.connect('127.0.0.1', 12345, Blather::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
|
+
Blather::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(:post_init)
|
64
|
+
@client.expects(:receive_data).with do |n|
|
65
|
+
EM.stop
|
66
|
+
n.kind_of? Blather::XMPPNode
|
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
|
+
it 'sends stanzas to the wire 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
|
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="special: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
|
@@ -0,0 +1,231 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. spec_helper])
|
2
|
+
|
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'
|
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
|
15
|
+
end
|
16
|
+
|
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'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets the namespace on creation' do
|
35
|
+
foo = Class.new(Blather::XMPPNode)
|
36
|
+
foo.registered_ns = 'foo'
|
37
|
+
foo.new('foo').namespace.href.must_equal 'foo'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'registers sub classes' do
|
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
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'imports another node' do
|
48
|
+
class ImportSubClass < Blather::XMPPNode; register 'foo', 'foo:bar'; end
|
49
|
+
n = Blather::XMPPNode.new('foo')
|
50
|
+
n.namespace = 'foo:bar'
|
51
|
+
Blather::XMPPNode.import(n).must_be_kind_of ImportSubClass
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'provides an attribute reader' do
|
55
|
+
foo = Blather::XMPPNode.new
|
56
|
+
foo.read_attr(:bar).must_be_nil
|
57
|
+
foo[:bar] = 'baz'
|
58
|
+
foo.read_attr(:bar).must_equal 'baz'
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'provides an attribute reader with converstion' do
|
62
|
+
foo = Blather::XMPPNode.new
|
63
|
+
foo.read_attr(:bar, :to_sym).must_be_nil
|
64
|
+
foo[:bar] = 'baz'
|
65
|
+
foo.read_attr(:bar, :to_sym).must_equal :baz
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'provides an attribute writer' do
|
69
|
+
foo = Blather::XMPPNode.new
|
70
|
+
foo[:bar].must_be_nil
|
71
|
+
foo.write_attr(:bar, 'baz')
|
72
|
+
foo[:bar].must_equal 'baz'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'provides a content reader' do
|
76
|
+
foo = Blather::XMPPNode.new('foo')
|
77
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
78
|
+
bar.content = 'baz'
|
79
|
+
foo.read_content(:bar).must_equal 'baz'
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'provides a content reader that converts the value' do
|
83
|
+
foo = Blather::XMPPNode.new('foo')
|
84
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
85
|
+
bar.content = 'baz'
|
86
|
+
foo.read_content(:bar, :to_sym).must_equal :baz
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'provides a content writer' do
|
90
|
+
foo = Blather::XMPPNode.new('foo')
|
91
|
+
foo.set_content_for :bar, 'baz'
|
92
|
+
foo.content_from(:bar).must_equal 'baz'
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'provides a content writer that removes a child when set to nil' do
|
96
|
+
foo = Blather::XMPPNode.new('foo')
|
97
|
+
foo << (bar = Blather::XMPPNode.new('bar', foo.document))
|
98
|
+
bar.content = 'baz'
|
99
|
+
foo.content_from(:bar).must_equal 'baz'
|
100
|
+
foo.xpath('bar').wont_be_empty
|
101
|
+
|
102
|
+
foo.set_content_for :bar, nil
|
103
|
+
foo.content_from(:bar).must_be_nil
|
104
|
+
foo.xpath('bar').must_be_empty
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'can convert itself into a stanza' do
|
108
|
+
class StanzaConvert < Blather::XMPPNode; register 'foo'; end
|
109
|
+
n = Blather::XMPPNode.new('foo')
|
110
|
+
n.to_stanza.must_be_kind_of StanzaConvert
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'provides "attr_accessor" for namespace' do
|
114
|
+
n = Blather::XMPPNode.new('foo')
|
115
|
+
n.namespace.must_be_nil
|
116
|
+
|
117
|
+
n.namespace = 'foo:bar'
|
118
|
+
n.namespace_href.must_equal 'foo:bar'
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'will remove a child element' do
|
122
|
+
n = Blather::XMPPNode.new 'foo'
|
123
|
+
n << Blather::XMPPNode.new('bar', n.document)
|
124
|
+
n << Blather::XMPPNode.new('bar', n.document)
|
125
|
+
|
126
|
+
n.find(:bar).size.must_equal 2
|
127
|
+
n.remove_child 'bar'
|
128
|
+
n.find(:bar).size.must_equal 1
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'will remove a child with a specific xmlns' do
|
132
|
+
n = Blather::XMPPNode.new 'foo'
|
133
|
+
n << Blather::XMPPNode.new('bar')
|
134
|
+
c = Blather::XMPPNode.new('bar')
|
135
|
+
c.namespace = 'foo:bar'
|
136
|
+
n << c
|
137
|
+
|
138
|
+
n.find(:bar).size.must_equal 2
|
139
|
+
n.remove_child 'bar', 'foo:bar'
|
140
|
+
n.find(:bar).size.must_equal 1
|
141
|
+
n.find(:bar).first.namespace.must_be_nil
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'will remove all child elements' do
|
145
|
+
n = Blather::XMPPNode.new 'foo'
|
146
|
+
n << Blather::XMPPNode.new('bar')
|
147
|
+
n << Blather::XMPPNode.new('bar')
|
148
|
+
|
149
|
+
n.find(:bar).size.must_equal 2
|
150
|
+
n.remove_children 'bar'
|
151
|
+
n.find(:bar).size.must_equal 0
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'provides a copy mechanism' do
|
155
|
+
n = Blather::XMPPNode.new 'foo'
|
156
|
+
n2 = n.copy
|
157
|
+
n2.object_id.wont_equal n.object_id
|
158
|
+
n2.element_name.must_equal n.element_name
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'provides an inherit mechanism' do
|
162
|
+
n = Blather::XMPPNode.new 'foo'
|
163
|
+
n2 = Blather::XMPPNode.new 'foo'
|
164
|
+
n2.content = 'bar'
|
165
|
+
n2['foo'] = 'bar'
|
166
|
+
|
167
|
+
n.inherit(n2)
|
168
|
+
n['foo'].must_equal 'bar'
|
169
|
+
n.content.must_equal 'bar'
|
170
|
+
n2.to_s.must_equal n.to_s
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'holds on to namespaces when inheriting content' do
|
174
|
+
n = parse_stanza('<message><bar:foo xmlns:bar="http://bar.com"></message>').root
|
175
|
+
n2 = Blather::XMPPNode.new('message').inherit n
|
176
|
+
n2.to_s.must_equal n.to_s
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'provides a mechanism to inherit attrs' do
|
180
|
+
n = Blather::XMPPNode.new 'foo'
|
181
|
+
n2 = Blather::XMPPNode.new 'foo'
|
182
|
+
n2['foo'] = 'bar'
|
183
|
+
|
184
|
+
n.inherit_attrs(n2.attributes)
|
185
|
+
n['foo'].must_equal 'bar'
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'has a content_from helper that pulls the content from a child node' do
|
189
|
+
f = Blather::XMPPNode.new('foo')
|
190
|
+
f << (b = Blather::XMPPNode.new('bar'))
|
191
|
+
b.content = 'content'
|
192
|
+
f.content_from(:bar).must_equal 'content'
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'returns nil when sent #content_from and a missing node' do
|
196
|
+
f = Blather::XMPPNode.new('foo')
|
197
|
+
f.content_from(:bar).must_be_nil
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'creates a new node and sets content when sent #set_content_for' do
|
201
|
+
f = Blather::XMPPNode.new('foo')
|
202
|
+
f.must_respond_to :set_content_for
|
203
|
+
f.xpath('bar').must_be_empty
|
204
|
+
f.set_content_for :bar, :baz
|
205
|
+
f.xpath('bar').wont_be_empty
|
206
|
+
f.xpath('bar').first.content.must_equal 'baz'
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'removes a child node when sent #set_content_for with nil' do
|
210
|
+
f = Blather::XMPPNode.new('foo')
|
211
|
+
f << (b = Blather::XMPPNode.new('bar'))
|
212
|
+
f.must_respond_to :set_content_for
|
213
|
+
f.xpath('bar').wont_be_empty
|
214
|
+
f.set_content_for :bar, nil
|
215
|
+
f.xpath('bar').must_be_empty
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'will change the content of an existing node when sent #set_content_for' do
|
219
|
+
f = Blather::XMPPNode.new('foo')
|
220
|
+
f << (b = Blather::XMPPNode.new('bar'))
|
221
|
+
b.content = 'baz'
|
222
|
+
f.must_respond_to :set_content_for
|
223
|
+
f.xpath('bar').wont_be_empty
|
224
|
+
f.xpath('bar').first.content.must_equal 'baz'
|
225
|
+
control = f.xpath('bar').first.pointer_id
|
226
|
+
|
227
|
+
f.set_content_for :bar, 'fiz'
|
228
|
+
f.xpath('bar').first.content.must_equal 'fiz'
|
229
|
+
f.xpath('bar').first.pointer_id.must_equal control
|
230
|
+
end
|
231
|
+
end
|