blather 0.4.7 → 0.4.8
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.
- 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
|