blather 0.4.7 → 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +162 -0
- data/examples/{print_heirarchy.rb → print_hierarchy.rb} +5 -5
- data/examples/stream_only.rb +27 -0
- data/lib/blather.rb +4 -0
- data/lib/blather/client/client.rb +91 -73
- data/lib/blather/client/dsl.rb +156 -32
- data/lib/blather/client/dsl/pubsub.rb +86 -54
- data/lib/blather/core_ext/active_support.rb +9 -9
- data/lib/blather/core_ext/active_support/inheritable_attributes.rb +2 -2
- data/lib/blather/core_ext/nokogiri.rb +12 -7
- data/lib/blather/errors.rb +25 -14
- data/lib/blather/errors/sasl_error.rb +21 -3
- data/lib/blather/errors/stanza_error.rb +37 -21
- data/lib/blather/errors/stream_error.rb +27 -17
- data/lib/blather/jid.rb +79 -24
- data/lib/blather/roster.rb +39 -21
- data/lib/blather/roster_item.rb +43 -21
- data/lib/blather/stanza.rb +88 -40
- data/lib/blather/stanza/disco.rb +12 -2
- data/lib/blather/stanza/disco/disco_info.rb +112 -20
- data/lib/blather/stanza/disco/disco_items.rb +81 -12
- data/lib/blather/stanza/iq.rb +94 -38
- data/lib/blather/stanza/iq/query.rb +16 -22
- data/lib/blather/stanza/iq/roster.rb +98 -20
- data/lib/blather/stanza/message.rb +266 -111
- data/lib/blather/stanza/presence.rb +118 -42
- data/lib/blather/stanza/presence/status.rb +140 -60
- data/lib/blather/stanza/presence/subscription.rb +44 -10
- data/lib/blather/stanza/pubsub.rb +70 -15
- data/lib/blather/stanza/pubsub/affiliations.rb +36 -7
- data/lib/blather/stanza/pubsub/create.rb +26 -4
- data/lib/blather/stanza/pubsub/errors.rb +13 -4
- data/lib/blather/stanza/pubsub/event.rb +56 -10
- data/lib/blather/stanza/pubsub/items.rb +46 -6
- data/lib/blather/stanza/pubsub/publish.rb +52 -7
- data/lib/blather/stanza/pubsub/retract.rb +45 -6
- data/lib/blather/stanza/pubsub/subscribe.rb +30 -4
- data/lib/blather/stanza/pubsub/subscription.rb +74 -6
- data/lib/blather/stanza/pubsub/subscriptions.rb +35 -9
- data/lib/blather/stanza/pubsub/unsubscribe.rb +30 -4
- data/lib/blather/stanza/pubsub_owner.rb +17 -7
- data/lib/blather/stanza/pubsub_owner/delete.rb +23 -5
- data/lib/blather/stanza/pubsub_owner/purge.rb +23 -5
- data/lib/blather/stream.rb +96 -29
- data/lib/blather/stream/parser.rb +6 -9
- data/lib/blather/xmpp_node.rb +101 -153
- data/spec/blather/client/client_spec.rb +1 -1
- data/spec/blather/errors_spec.rb +5 -5
- data/spec/blather/stanza/message_spec.rb +56 -0
- data/spec/blather/stanza/presence/status_spec.rb +1 -1
- data/spec/blather/stanza_spec.rb +3 -3
- data/spec/blather/xmpp_node_spec.rb +19 -74
- metadata +6 -10
- data/README.rdoc +0 -185
- data/examples/drb_client.rb +0 -5
- data/examples/ping.rb +0 -11
- data/examples/pong.rb +0 -6
- data/examples/pubsub/cli.rb +0 -64
- data/examples/pubsub/ping_pong.rb +0 -18
@@ -1,36 +1,62 @@
|
|
1
1
|
module Blather
|
2
2
|
class Stanza
|
3
3
|
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# # DiscoInfo Stanza
|
5
|
+
#
|
6
|
+
# [XEP-0030 Disco Info](http://xmpp.org/extensions/xep-0030.html#info)
|
7
|
+
#
|
8
|
+
# Disco Info node that provides or retreives information about a jabber entity
|
9
|
+
#
|
10
|
+
# @handler :disco_info
|
7
11
|
class DiscoInfo < Disco
|
8
12
|
register :disco_info, nil, 'http://jabber.org/protocol/disco#info'
|
9
13
|
|
14
|
+
# Create a new DiscoInfo stanza
|
15
|
+
# @param [:get, :set, :result, :error, nil] type the Iq stanza type
|
16
|
+
# @param [String, nil] node the name of the node the info belongs to
|
17
|
+
# @param [Array<Array, DiscoInfo::Identity>, nil] identities a list of
|
18
|
+
# identities. these are passed directly to DiscoInfo::Identity.new
|
19
|
+
# @param [Array<Array, DiscoInfo::Identity>, nil] features a list of
|
20
|
+
# features. these are passed directly to DiscoInfo::Feature.new
|
21
|
+
# @return [DiscoInfo] a new DiscoInfo stanza
|
10
22
|
def self.new(type = nil, node = nil, identities = [], features = [])
|
11
23
|
new_node = super type
|
12
24
|
new_node.node = node
|
13
|
-
[identities].flatten.each { |
|
14
|
-
[features].flatten.each { |
|
25
|
+
[identities].flatten.each { |i| new_node.query << Identity.new(i) }
|
26
|
+
[features].flatten.each { |f| new_node.query << Feature.new(f) }
|
15
27
|
new_node
|
16
28
|
end
|
17
29
|
|
18
|
-
##
|
19
30
|
# List of identity objects
|
20
31
|
def identities
|
21
|
-
query.find('//
|
32
|
+
query.find('//ns:identity', :ns => self.class.registered_ns).map do |i|
|
33
|
+
Identity.new i
|
34
|
+
end
|
22
35
|
end
|
23
36
|
|
24
|
-
##
|
25
37
|
# List of feature objects
|
26
38
|
def features
|
27
|
-
query.find('//
|
39
|
+
query.find('//ns:feature', :ns => self.class.registered_ns).map do |f|
|
40
|
+
Feature.new f
|
41
|
+
end
|
28
42
|
end
|
29
43
|
|
30
44
|
class Identity < XMPPNode
|
31
|
-
|
32
|
-
|
33
|
-
|
45
|
+
# Create a new DiscoInfo Identity
|
46
|
+
# @overload new(node)
|
47
|
+
# Imports the XML::Node to create a Identity object
|
48
|
+
# @param [XML::Node] node the node object to import
|
49
|
+
# @overload new(opts = {})
|
50
|
+
# Creates a new Identity using a hash of options
|
51
|
+
# @param [Hash] opts a hash of options
|
52
|
+
# @option opts [String] :name the name of the identity
|
53
|
+
# @option opts [String] :type the type of the identity
|
54
|
+
# @option opts [String] :category the category of the identity
|
55
|
+
# @overload new(name, type = nil, category = nil)
|
56
|
+
# Create a new Identity by name
|
57
|
+
# @param [String] name the name of the Identity
|
58
|
+
# @param [String, nil] type the type of the Identity
|
59
|
+
# @param [String, nil] category the category of the Identity
|
34
60
|
def self.new(name, type = nil, category = nil)
|
35
61
|
new_node = super :identity
|
36
62
|
|
@@ -49,18 +75,66 @@ class Stanza
|
|
49
75
|
new_node
|
50
76
|
end
|
51
77
|
|
78
|
+
# The Identity's category
|
79
|
+
# @return [Symbol, nil]
|
80
|
+
def category
|
81
|
+
read_attr :category, :to_sym
|
82
|
+
end
|
83
|
+
|
84
|
+
# Set the Identity's category
|
85
|
+
# @param [String, Symbol] category the new category
|
86
|
+
def category=(category)
|
87
|
+
write_attr :category, category
|
88
|
+
end
|
89
|
+
|
90
|
+
# The Identity's type
|
91
|
+
# @return [Symbol, nil]
|
92
|
+
def type
|
93
|
+
read_attr :type, :to_sym
|
94
|
+
end
|
95
|
+
|
96
|
+
# Set the Identity's type
|
97
|
+
# @param [String, Symbol] type the new category
|
98
|
+
def type=(type)
|
99
|
+
write_attr :type, type
|
100
|
+
end
|
101
|
+
|
102
|
+
# The Identity's name
|
103
|
+
# @return [String]
|
104
|
+
def name
|
105
|
+
read_attr :name
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set the Identity's name
|
109
|
+
# @param [String] name the new name for the identity
|
110
|
+
def name=(name)
|
111
|
+
write_attr :name, name
|
112
|
+
end
|
113
|
+
|
114
|
+
# Compare two Identity objects by name, type and category
|
115
|
+
# @param [DiscoInfo::Identity] o the Identity object to compare against
|
116
|
+
# @return [true, false]
|
52
117
|
def eql?(o)
|
53
|
-
|
118
|
+
unless o.is_a?(self.class)
|
119
|
+
raise "Cannot compare #{self.class} with #{o.class}"
|
120
|
+
end
|
121
|
+
|
54
122
|
o.name == self.name &&
|
55
123
|
o.type == self.type &&
|
56
124
|
o.category == self.category
|
57
125
|
end
|
58
126
|
alias_method :==, :eql?
|
59
|
-
end
|
127
|
+
end # Identity
|
60
128
|
|
61
129
|
class Feature < XMPPNode
|
62
|
-
|
63
|
-
|
130
|
+
# Create a new DiscoInfo::Feature object
|
131
|
+
# @overload new(node)
|
132
|
+
# Create a new Feature by importing an XML::Node
|
133
|
+
# @param [XML::Node] node an XML::Node to import
|
134
|
+
# @overload new(var)
|
135
|
+
# Create a new feature by var
|
136
|
+
# @param [String] var a the Feautre's var
|
137
|
+
# @return [DiscoInfo::Feature]
|
64
138
|
def self.new(var)
|
65
139
|
new_node = super :feature
|
66
140
|
case var
|
@@ -72,13 +146,31 @@ class Stanza
|
|
72
146
|
new_node
|
73
147
|
end
|
74
148
|
|
149
|
+
# The Feature's var
|
150
|
+
# @return [String]
|
151
|
+
def var
|
152
|
+
read_attr :var
|
153
|
+
end
|
154
|
+
|
155
|
+
# Set the Feature's var
|
156
|
+
# @param [String] var the new var
|
157
|
+
def var=(var)
|
158
|
+
write_attr :var, var
|
159
|
+
end
|
160
|
+
|
161
|
+
# Compare two Feature objects by var
|
162
|
+
# @param [DiscoInfo::Feature] o the Feature object to compare against
|
163
|
+
# @return [true, false]
|
75
164
|
def eql?(o)
|
76
|
-
|
165
|
+
unless o.is_a?(self.class)
|
166
|
+
raise "Cannot compare #{self.class} with #{o.class}"
|
167
|
+
end
|
168
|
+
|
77
169
|
o.var == self.var
|
78
170
|
end
|
79
171
|
alias_method :==, :eql?
|
80
172
|
end
|
81
|
-
end
|
173
|
+
end # Feature
|
82
174
|
|
83
|
-
end #Stanza
|
84
|
-
end #Blather
|
175
|
+
end # Stanza
|
176
|
+
end # Blather
|
@@ -1,9 +1,23 @@
|
|
1
1
|
module Blather
|
2
2
|
class Stanza
|
3
3
|
|
4
|
+
# # DiscoItems Stanza
|
5
|
+
#
|
6
|
+
# [XEP-0030 Disco Info](http://xmpp.org/extensions/xep-0030.html#items)
|
7
|
+
#
|
8
|
+
# Disco Items node that provides or retreives items associated with a
|
9
|
+
# jabbery entity
|
10
|
+
#
|
11
|
+
# @handler :disco_items
|
4
12
|
class DiscoItems < Disco
|
5
13
|
register :disco_items, nil, 'http://jabber.org/protocol/disco#items'
|
6
14
|
|
15
|
+
# Create a new DiscoItems node
|
16
|
+
#
|
17
|
+
# @param [#to_s] type the IQ type
|
18
|
+
# @param [#to_s] node the node the items are associated with
|
19
|
+
# @param [Array<Blather::XMPPNode>] items an array of Disco::Items
|
20
|
+
# @return [Blather::Stanza::DiscoItems]
|
7
21
|
def self.new(type = nil, node = nil, items = [])
|
8
22
|
new_node = super type
|
9
23
|
new_node.node = node
|
@@ -11,19 +25,33 @@ class Stanza
|
|
11
25
|
new_node
|
12
26
|
end
|
13
27
|
|
28
|
+
# Set of items associated with the node
|
29
|
+
#
|
30
|
+
# @return [Array<Blather::Stanza::DiscoItems::Item>]
|
14
31
|
def items
|
15
|
-
query.find('//
|
16
|
-
|
17
|
-
|
18
|
-
def node=(node)
|
19
|
-
query[:node] = node
|
20
|
-
end
|
21
|
-
|
22
|
-
def node
|
23
|
-
query[:node]
|
32
|
+
query.find('//ns:item', :ns => self.class.registered_ns).map do |i|
|
33
|
+
Item.new i
|
34
|
+
end
|
24
35
|
end
|
25
36
|
|
37
|
+
# An individual Disco Item
|
26
38
|
class Item < XMPPNode
|
39
|
+
# Create a new Blather::Stanza::DiscoItems::Item
|
40
|
+
#
|
41
|
+
# @overload new(node)
|
42
|
+
# Create a new Item by inheriting an existing node
|
43
|
+
# @param [XML::Node] node an XML::Node to inherit from
|
44
|
+
# @overload new(opts)
|
45
|
+
# Create a new Item through a hash of options
|
46
|
+
# @param [Hash] opts a hash options
|
47
|
+
# @option opts [Blather::JID, String] :jid the JID to attach to the item
|
48
|
+
# @option opts [#to_s] :node the node the item is attached to
|
49
|
+
# @option opts [#to_S] :name the name of the Item
|
50
|
+
# @overload new(jid, node = nil, name = nil)
|
51
|
+
# Create a new Item
|
52
|
+
# @param [Blather::JID, String] jid the JID to attach to the item
|
53
|
+
# @param [#to_s] node the node the item is attached to
|
54
|
+
# @param [#to_s] name the name of the Item
|
27
55
|
def self.new(jid, node = nil, name = nil)
|
28
56
|
new_node = super :item
|
29
57
|
|
@@ -42,15 +70,56 @@ class Stanza
|
|
42
70
|
new_node
|
43
71
|
end
|
44
72
|
|
73
|
+
# Get the JID attached to the node
|
74
|
+
#
|
75
|
+
# @return [Blather::JID, nil]
|
45
76
|
def jid
|
46
77
|
(j = self[:jid]) ? JID.new(j) : nil
|
47
78
|
end
|
48
|
-
attribute_writer :jid
|
49
79
|
|
50
|
-
|
80
|
+
# Set the JID of the node
|
81
|
+
#
|
82
|
+
# @param [Blather::JID, String, nil] jid the new JID
|
83
|
+
def jid=(jid)
|
84
|
+
write_attr :jid, jid
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get the name of the node
|
88
|
+
#
|
89
|
+
# @return [String, nil]
|
90
|
+
def node
|
91
|
+
read_attr :node
|
92
|
+
end
|
93
|
+
|
94
|
+
# Set the name of the node
|
95
|
+
#
|
96
|
+
# @param [String, nil] node the new node name
|
97
|
+
def node=(node)
|
98
|
+
write_attr :node, node
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get the Item name
|
102
|
+
#
|
103
|
+
# @return [String, nil]
|
104
|
+
def name
|
105
|
+
read_attr :name
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set the Item name
|
109
|
+
#
|
110
|
+
# @param [#to_s] name the Item name
|
111
|
+
def name=(name)
|
112
|
+
write_attr :name, name
|
113
|
+
end
|
51
114
|
|
115
|
+
# Check for equality based on jid, node, and name
|
116
|
+
#
|
117
|
+
# @param [Blather::Stanza::DiscoItems::Item] o the other Item
|
52
118
|
def eql?(o)
|
53
|
-
|
119
|
+
unless o.is_a?(self.class)
|
120
|
+
raise "Cannot compare #{self.class} with #{o.class}"
|
121
|
+
end
|
122
|
+
|
54
123
|
o.jid == self.jid &&
|
55
124
|
o.node == self.node &&
|
56
125
|
o.name == self.name
|
data/lib/blather/stanza/iq.rb
CHANGED
@@ -1,52 +1,69 @@
|
|
1
1
|
module Blather
|
2
2
|
class Stanza
|
3
3
|
|
4
|
-
#
|
4
|
+
# # Iq Stanza
|
5
5
|
#
|
6
|
-
#
|
7
|
-
# to make a request of, and receive a response from, another entity. The data content of the request and response is
|
8
|
-
# defined by the namespace declaration of a direct child element of the IQ element, and the interaction is tracked by the
|
9
|
-
# requesting entity through use of the 'id' attribute. Thus, IQ interactions follow a common pattern of structured data
|
10
|
-
# exchange such as get/result or set/result (although an error may be returned in reply to a request if appropriate).
|
6
|
+
# [RFC 3920 Section 9.2.3 - IQ Semantics](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.2.3)
|
11
7
|
#
|
12
|
-
#
|
8
|
+
# Info/Query, or IQ, is a request-response mechanism, similar in some ways
|
9
|
+
# to HTTP. The semantics of IQ enable an entity to make a request of, and
|
10
|
+
# receive a response from, another entity. The data content of the request
|
11
|
+
# and response is defined by the namespace declaration of a direct child
|
12
|
+
# element of the IQ element, and the interaction is tracked by the
|
13
|
+
# requesting entity through use of the 'id' attribute. Thus, IQ interactions
|
14
|
+
# follow a common pattern of structured data exchange such as get/result or
|
15
|
+
# set/result (although an error may be returned in reply to a request if
|
16
|
+
# appropriate).
|
13
17
|
#
|
14
|
-
#
|
18
|
+
# ## "ID" Attribute
|
15
19
|
#
|
16
|
-
#
|
20
|
+
# Iq Stanzas require the ID attribute be set. Blather will handle this
|
21
|
+
# automatically when a new Iq is created.
|
17
22
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# *
|
21
|
-
#
|
23
|
+
# ## "Type" Attribute
|
24
|
+
#
|
25
|
+
# * `:get` -- The stanza is a request for information or requirements.
|
26
|
+
#
|
27
|
+
# * `:set` -- The stanza provides required data, sets new values, or
|
28
|
+
# replaces existing values.
|
29
|
+
#
|
30
|
+
# * `:result` -- The stanza is a response to a successful get or set request.
|
31
|
+
#
|
32
|
+
# * `:error` -- An error has occurred regarding processing or delivery of a
|
33
|
+
# previously-sent get or set (see Stanza Errors).
|
22
34
|
#
|
23
35
|
# Blather provides a helper for each possible type:
|
24
36
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
37
|
+
# Iq#get?
|
38
|
+
# Iq#set?
|
39
|
+
# Iq#result?
|
40
|
+
# Iq#error?
|
29
41
|
#
|
30
|
-
# Blather treats the
|
31
|
-
# The default
|
42
|
+
# Blather treats the `type` attribute like a normal ruby object attribute
|
43
|
+
# providing a getter and setter. The default `type` is `get`.
|
32
44
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
45
|
+
# iq = Iq.new
|
46
|
+
# iq.type # => :get
|
47
|
+
# iq.get? # => true
|
48
|
+
# iq.type = :set
|
49
|
+
# iq.set? # => true
|
50
|
+
# iq.get? # => false
|
39
51
|
#
|
40
|
-
#
|
52
|
+
# iq.type = :invalid # => RuntimeError
|
41
53
|
#
|
54
|
+
# @handler :iq
|
42
55
|
class Iq < Stanza
|
43
|
-
VALID_TYPES = [:get, :set, :result, :error]
|
56
|
+
VALID_TYPES = [:get, :set, :result, :error].freeze
|
44
57
|
|
45
58
|
register :iq
|
46
59
|
|
47
|
-
|
60
|
+
# @private
|
61
|
+
def self.import(node)
|
48
62
|
klass = nil
|
49
|
-
node.children.
|
63
|
+
node.children.detect do |e|
|
64
|
+
ns = e.namespace ? e.namespace.href : nil
|
65
|
+
klass = class_from_registration(e.element_name, ns)
|
66
|
+
end
|
50
67
|
|
51
68
|
if klass && klass != self
|
52
69
|
klass.import(node)
|
@@ -55,11 +72,12 @@ class Stanza
|
|
55
72
|
end
|
56
73
|
end
|
57
74
|
|
58
|
-
##
|
59
75
|
# Create a new Iq
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
76
|
+
#
|
77
|
+
# @param [Symbol, nil] type the type of stanza (:get, :set, :result, :error)
|
78
|
+
# @param [Blather::JID, String, nil] jid the JID of the inteded recipient
|
79
|
+
# @param [#to_s] id the stanza's ID. Leaving this nil will set the ID to
|
80
|
+
# the next unique number
|
63
81
|
def self.new(type = nil, to = nil, id = nil)
|
64
82
|
node = super :iq
|
65
83
|
node.type = type || :get
|
@@ -68,15 +86,53 @@ class Stanza
|
|
68
86
|
node
|
69
87
|
end
|
70
88
|
|
71
|
-
|
89
|
+
# Check if the IQ is of type :get
|
90
|
+
#
|
91
|
+
# @return [true, false]
|
92
|
+
def get?
|
93
|
+
self.type == :get
|
94
|
+
end
|
95
|
+
|
96
|
+
# Check if the IQ is of type :set
|
97
|
+
#
|
98
|
+
# @return [true, false]
|
99
|
+
def set?
|
100
|
+
self.type == :set
|
101
|
+
end
|
102
|
+
|
103
|
+
# Check if the IQ is of type :result
|
104
|
+
#
|
105
|
+
# @return [true, false]
|
106
|
+
def result?
|
107
|
+
self.type == :result
|
108
|
+
end
|
109
|
+
|
110
|
+
# Check if the IQ is of type :error
|
111
|
+
#
|
112
|
+
# @return [true, false]
|
113
|
+
def error?
|
114
|
+
self.type == :error
|
115
|
+
end
|
72
116
|
|
73
|
-
##
|
74
117
|
# Ensures type is :get, :set, :result or :error
|
75
|
-
|
76
|
-
|
118
|
+
#
|
119
|
+
# @param [#to_sym] type the Iq type. Must be one of VALID_TYPES
|
120
|
+
def type=(type)
|
121
|
+
if type && !VALID_TYPES.include?(type.to_sym)
|
122
|
+
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
|
123
|
+
end
|
77
124
|
super
|
78
125
|
end
|
126
|
+
|
127
|
+
# Overrides the parent method to ensure the reply is of type :result
|
128
|
+
#
|
129
|
+
# @return [self]
|
130
|
+
def reply!
|
131
|
+
super
|
132
|
+
self.type = :result
|
133
|
+
self
|
134
|
+
end
|
79
135
|
end
|
80
136
|
|
81
|
-
end
|
137
|
+
end
|
82
138
|
end
|