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
@@ -2,27 +2,37 @@ module Blather
|
|
2
2
|
class Stanza
|
3
3
|
class Iq
|
4
4
|
|
5
|
+
# # Query Stanza
|
6
|
+
#
|
7
|
+
# This is a base class for any query based Iq stanzas. It provides a base set
|
8
|
+
# of methods for working with query stanzas
|
9
|
+
#
|
10
|
+
# @handler :query
|
5
11
|
class Query < Iq
|
6
12
|
register :query, :query
|
7
13
|
|
8
|
-
|
9
|
-
#
|
14
|
+
# Overrides the parent method to ensure a query node is created
|
15
|
+
#
|
16
|
+
# @see Blather::Stanza::Iq.new
|
10
17
|
def self.new(type = nil)
|
11
18
|
node = super
|
12
19
|
node.query
|
13
20
|
node
|
14
21
|
end
|
15
22
|
|
16
|
-
|
17
|
-
#
|
23
|
+
# Overrides the parent method to ensure the current query node is destroyed
|
24
|
+
#
|
25
|
+
# @see Blather::Stanza::Iq#inherit
|
18
26
|
def inherit(node)
|
19
27
|
query.remove
|
20
28
|
super
|
21
29
|
end
|
22
30
|
|
23
|
-
##
|
24
31
|
# Query node accessor
|
25
|
-
#
|
32
|
+
# If a query node exists it will be returned.
|
33
|
+
# Otherwise a new node will be created and returned
|
34
|
+
#
|
35
|
+
# @return [Balather::XMPPNode]
|
26
36
|
def query
|
27
37
|
q = if self.class.registered_ns
|
28
38
|
find_first('query_ns:query', :query_ns => self.class.registered_ns)
|
@@ -36,22 +46,6 @@ class Iq
|
|
36
46
|
end
|
37
47
|
q
|
38
48
|
end
|
39
|
-
|
40
|
-
##
|
41
|
-
# A query reply should have type set to "result"
|
42
|
-
def reply
|
43
|
-
elem = super
|
44
|
-
elem.type = :result
|
45
|
-
elem
|
46
|
-
end
|
47
|
-
|
48
|
-
##
|
49
|
-
# A query reply should have type set to "result"
|
50
|
-
def reply!
|
51
|
-
super
|
52
|
-
self.type = :result
|
53
|
-
self
|
54
|
-
end
|
55
49
|
end #Query
|
56
50
|
|
57
51
|
end #Iq
|
@@ -2,20 +2,28 @@ module Blather
|
|
2
2
|
class Stanza
|
3
3
|
class Iq
|
4
4
|
|
5
|
+
# # Roster Stanza
|
6
|
+
#
|
7
|
+
# [RFC 3921 Section 7 - Roster Management](http://xmpp.org/rfcs/rfc3921.html#roster)
|
8
|
+
#
|
9
|
+
# @handler :roster
|
5
10
|
class Roster < Query
|
6
11
|
register :roster, nil, 'jabber:iq:roster'
|
7
12
|
|
8
|
-
|
9
|
-
#
|
13
|
+
# Create a new roster stanza and (optionally) load it with an item
|
14
|
+
#
|
15
|
+
# @param [<Blather::Stanza::Iq::VALID_TYPES>] type the stanza type
|
16
|
+
# @param [Blather::XMPPNode] item a roster item
|
10
17
|
def self.new(type = nil, item = nil)
|
11
18
|
node = super type
|
12
19
|
node.query << item if item
|
13
20
|
node
|
14
21
|
end
|
15
22
|
|
16
|
-
##
|
17
23
|
# Inherit the XMPPNode to create a proper Roster object.
|
18
24
|
# Creates RosterItem objects out of each roster item as well.
|
25
|
+
#
|
26
|
+
# @param [Blather::XMPPNode] node a node to inherit
|
19
27
|
def inherit(node)
|
20
28
|
# remove the current set of nodes
|
21
29
|
remove_children :item
|
@@ -25,18 +33,41 @@ class Iq
|
|
25
33
|
self
|
26
34
|
end
|
27
35
|
|
28
|
-
|
29
|
-
#
|
36
|
+
# The list of roster items
|
37
|
+
#
|
38
|
+
# @return [Array<Blather::Stanza::Iq::Roster::RosterItem>]
|
30
39
|
def items
|
31
|
-
query.find('//ns:item', :ns => self.class.registered_ns).map
|
40
|
+
query.find('//ns:item', :ns => self.class.registered_ns).map do |i|
|
41
|
+
RosterItem.new i
|
42
|
+
end
|
32
43
|
end
|
33
44
|
|
45
|
+
# # RosterItem Fragment
|
46
|
+
#
|
47
|
+
# Individual roster items.
|
48
|
+
# This is a convenience class to attach methods to the node
|
34
49
|
class RosterItem < XMPPNode
|
35
|
-
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# [
|
50
|
+
|
51
|
+
# Create a new RosterItem
|
52
|
+
# @overload new(XML::Node)
|
53
|
+
# Create a RosterItem by inheriting a node
|
54
|
+
# @param [XML::Node] node an xml node to inherit
|
55
|
+
# @overload new(opts)
|
56
|
+
# Create a RosterItem through a hash of options
|
57
|
+
# @param [Hash] opts the options
|
58
|
+
# @option opts [Blather::JID, String, nil] :jid the JID of the item
|
59
|
+
# @option opts [String, nil] :name the alias to give the JID
|
60
|
+
# @option opts [Symbol, nil] :subscription the subscription status of
|
61
|
+
# the RosterItem must be one of
|
62
|
+
# Blather::RosterItem::VALID_SUBSCRIPTION_TYPES
|
63
|
+
# @option opts [:subscribe, nil] :ask the ask value of the RosterItem
|
64
|
+
# @overload new(jid = nil, name = nil, subscription = nil, ask = nil)
|
65
|
+
# @param [Blather::JID, String, nil] jid the JID of the item
|
66
|
+
# @param [String, nil] name the alias to give the JID
|
67
|
+
# @param [Symbol, nil] subscription the subscription status of the
|
68
|
+
# RosterItem must be one of
|
69
|
+
# Blather::RosterItem::VALID_SUBSCRIPTION_TYPES
|
70
|
+
# @param [:subscribe, nil] ask the ask value of the RosterItem
|
40
71
|
def self.new(jid = nil, name = nil, subscription = nil, ask = nil)
|
41
72
|
new_node = super :item
|
42
73
|
|
@@ -57,26 +88,72 @@ class Iq
|
|
57
88
|
new_node
|
58
89
|
end
|
59
90
|
|
60
|
-
|
61
|
-
#
|
91
|
+
# Get the JID attached to the item
|
92
|
+
#
|
93
|
+
# @return [Blather::JID, nil]
|
62
94
|
def jid
|
63
95
|
(j = self[:jid]) ? JID.new(j) : nil
|
64
96
|
end
|
65
|
-
attribute_writer :jid
|
66
97
|
|
67
|
-
|
98
|
+
# Set the JID of the item
|
99
|
+
#
|
100
|
+
# @param [Blather::JID, String, nil] jid the new JID
|
101
|
+
def jid=(jid)
|
102
|
+
write_attr :jid, jid
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get the item name
|
106
|
+
#
|
107
|
+
# @return [String, nil]
|
108
|
+
def name
|
109
|
+
read_attr :name
|
110
|
+
end
|
111
|
+
|
112
|
+
# Set the item name
|
113
|
+
#
|
114
|
+
# @param [#to_s] name the name of the item
|
115
|
+
def name=(name)
|
116
|
+
write_attr :name, name
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get the subscription value of the item
|
120
|
+
#
|
121
|
+
# @return [<:both, :from, :none, :remove, :to>]
|
122
|
+
def subscription
|
123
|
+
read_attr :subscription, :to_sym
|
124
|
+
end
|
125
|
+
|
126
|
+
# Set the subscription value of the item
|
127
|
+
#
|
128
|
+
# @param [<:both, :from, :none, :remove, :to>] subscription
|
129
|
+
def subscription=(subscription)
|
130
|
+
write_attr :subscription, subscription
|
131
|
+
end
|
68
132
|
|
69
|
-
|
133
|
+
# Get the ask value of the item
|
134
|
+
#
|
135
|
+
# @return [<:subscribe, nil>]
|
136
|
+
def ask
|
137
|
+
read_attr :ask, :to_sym
|
138
|
+
end
|
139
|
+
|
140
|
+
# Set the ask value of the item
|
141
|
+
#
|
142
|
+
# @param [<:subscribe, nil>] ask
|
143
|
+
def ask=(ask)
|
144
|
+
write_attr :ask, ask
|
145
|
+
end
|
70
146
|
|
71
|
-
##
|
72
147
|
# The groups roster item belongs to
|
148
|
+
#
|
149
|
+
# @return [Array<String>]
|
73
150
|
def groups
|
74
151
|
find('child::*[local-name()="group"]').map { |g| g.content }
|
75
152
|
end
|
76
153
|
|
77
|
-
##
|
78
154
|
# Set the roster item's groups
|
79
|
-
#
|
155
|
+
#
|
156
|
+
# @param [Array<#to_s>] new_groups an array of group names
|
80
157
|
def groups=(new_groups)
|
81
158
|
remove_children :group
|
82
159
|
if new_groups
|
@@ -87,9 +164,10 @@ class Iq
|
|
87
164
|
end
|
88
165
|
end
|
89
166
|
|
90
|
-
##
|
91
167
|
# Convert the roster item to a proper stanza all wrapped up
|
92
168
|
# This facilitates new subscriptions
|
169
|
+
#
|
170
|
+
# @return [Blather::Stanza::Iq::Roster]
|
93
171
|
def to_stanza
|
94
172
|
Roster.new(:set, self)
|
95
173
|
end
|
@@ -1,138 +1,176 @@
|
|
1
1
|
module Blather
|
2
2
|
class Stanza
|
3
3
|
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# attribute
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# *
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# *
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
4
|
+
# # Message Stanza
|
5
|
+
#
|
6
|
+
# [RFC 3921 Section 2.1 - Message Syntax](http://xmpp.org/rfcs/rfc3921.html#rfc.section.2.1)
|
7
|
+
#
|
8
|
+
# Exchanging messages is a basic use of XMPP and occurs when a user
|
9
|
+
# generates a message stanza that is addressed to another entity. The
|
10
|
+
# sender's server is responsible for delivering the message to the intended
|
11
|
+
# recipient (if the recipient is on the same local server) or for routing
|
12
|
+
# the message to the recipient's server (if the recipient is on a remote
|
13
|
+
# server). Thus a message stanza is used to "push" information to another
|
14
|
+
# entity.
|
15
|
+
#
|
16
|
+
# ## "To" Attribute
|
17
|
+
#
|
18
|
+
# An instant messaging client specifies an intended recipient for a message
|
19
|
+
# by providing the JID of an entity other than the sender in the `to`
|
20
|
+
# attribute of the Message stanza. If the message is being sent outside the
|
21
|
+
# context of any existing chat session or received message, the value of the
|
22
|
+
# `to` address SHOULD be of the form "user@domain" rather than of the form
|
23
|
+
# "user@domain/resource".
|
24
|
+
#
|
25
|
+
# msg = Message.new 'user@domain.tld/resource'
|
26
|
+
# msg.to == 'user@domain.tld/resource'
|
27
|
+
#
|
28
|
+
# msg.to = 'another-user@some-domain.tld/resource'
|
29
|
+
# msg.to == 'another-user@some-domain.tld/resource'
|
30
|
+
#
|
31
|
+
# The `to` attribute on a Message stanza works like any regular ruby object
|
32
|
+
# attribute
|
33
|
+
#
|
34
|
+
# ## "Type" Attribute
|
35
|
+
#
|
36
|
+
# Common uses of the message stanza in instant messaging applications
|
37
|
+
# include: single messages; messages sent in the context of a one-to-one
|
38
|
+
# chat session; messages sent in the context of a multi-user chat room;
|
39
|
+
# alerts, notifications, or other information to which no reply is expected;
|
40
|
+
# and errors. These uses are differentiated via the `type` attribute. If
|
41
|
+
# included, the `type` attribute MUST have one of the following values:
|
42
|
+
#
|
43
|
+
# * `:chat` -- The message is sent in the context of a one-to-one chat
|
44
|
+
# session. Typically a receiving client will present message of type
|
45
|
+
# `chat` in an interface that enables one-to-one chat between the two
|
46
|
+
# parties, including an appropriate conversation history.
|
47
|
+
#
|
48
|
+
# * `:error` -- The message is generated by an entity that experiences an
|
49
|
+
# error in processing a message received from another entity. A client
|
50
|
+
# that receives a message of type `error` SHOULD present an appropriate
|
51
|
+
# interface informing the sender of the nature of the error.
|
52
|
+
#
|
53
|
+
# * `:groupchat` -- The message is sent in the context of a multi-user chat
|
54
|
+
# environment (similar to that of [IRC]). Typically a receiving client
|
55
|
+
# will present a message of type `groupchat` in an interface that enables
|
56
|
+
# many-to-many chat between the parties, including a roster of parties in
|
57
|
+
# the chatroom and an appropriate conversation history.
|
58
|
+
#
|
59
|
+
# * `:headline` -- The message provides an alert, a notification, or other
|
60
|
+
# information to which no reply is expected (e.g., news headlines, sports
|
61
|
+
# updates, near-real-time market data, and syndicated content). Because no
|
62
|
+
# reply to the message is expected, typically a receiving client will
|
63
|
+
# present a message of type "headline" in an interface that appropriately
|
64
|
+
# differentiates the message from standalone messages, chat messages, or
|
65
|
+
# groupchat messages (e.g., by not providing the recipient with the
|
66
|
+
# ability to reply).
|
67
|
+
#
|
68
|
+
# * `:normal` -- The message is a standalone message that is sent outside
|
69
|
+
# the context of a one-to-one conversation or groupchat, and to which it
|
70
|
+
# is expected that the recipient will reply. Typically a receiving client
|
71
|
+
# will present a message of type `normal` in an interface that enables the
|
72
|
+
# recipient to reply, but without a conversation history. The default
|
73
|
+
# value of the `type` attribute is `normal`.
|
52
74
|
#
|
53
75
|
# Blather provides a helper for each possible type:
|
54
76
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
77
|
+
# Message#chat?
|
78
|
+
# Message#error?
|
79
|
+
# Message#groupchat?
|
80
|
+
# Message#headline?
|
81
|
+
# Message#normal?
|
82
|
+
#
|
83
|
+
# Blather treats the `type` attribute like a normal ruby object attribute
|
84
|
+
# providing a getter and setter. The default `type` is `chat`.
|
60
85
|
#
|
61
|
-
#
|
62
|
-
#
|
86
|
+
# msg = Message.new
|
87
|
+
# msg.type # => :chat
|
88
|
+
# msg.chat? # => true
|
89
|
+
# msg.type = :normal
|
90
|
+
# msg.normal? # => true
|
91
|
+
# msg.chat? # => false
|
63
92
|
#
|
64
|
-
#
|
65
|
-
# msg.type # => :chat
|
66
|
-
# msg.chat? # => true
|
67
|
-
# msg.type = :normal
|
68
|
-
# msg.normal? # => true
|
69
|
-
# msg.chat? # => false
|
93
|
+
# msg.type = :invalid # => RuntimeError
|
70
94
|
#
|
71
|
-
# msg.type = :invalid # => RuntimeError
|
72
95
|
#
|
73
|
-
#
|
96
|
+
# ## "Body" Element
|
74
97
|
#
|
75
|
-
# The
|
76
|
-
#
|
98
|
+
# The `body` element contains human-readable XML character data that
|
99
|
+
# specifies the textual contents of the message; this child element is
|
100
|
+
# normally included but is optional.
|
77
101
|
#
|
78
|
-
# Blather provides an attribute-like syntax for Message
|
102
|
+
# Blather provides an attribute-like syntax for Message `body` elements.
|
79
103
|
#
|
80
|
-
#
|
81
|
-
#
|
104
|
+
# msg = Message.new 'user@domain.tld', 'message body'
|
105
|
+
# msg.body # => 'message body'
|
82
106
|
#
|
83
|
-
#
|
84
|
-
#
|
107
|
+
# msg.body = 'other message'
|
108
|
+
# msg.body # => 'other message'
|
85
109
|
#
|
86
|
-
#
|
110
|
+
# ## "Subject" Element
|
87
111
|
#
|
88
|
-
# The
|
112
|
+
# The `subject` element contains human-readable XML character data that
|
113
|
+
# specifies the topic of the message.
|
89
114
|
#
|
90
|
-
# Blather provides an attribute-like syntax for Message
|
115
|
+
# Blather provides an attribute-like syntax for Message `subject` elements.
|
91
116
|
#
|
92
|
-
#
|
93
|
-
#
|
117
|
+
# msg = Message.new 'user@domain.tld', 'message body'
|
118
|
+
# msg.subject = 'message subject'
|
119
|
+
# msg.subject # => 'message subject'
|
94
120
|
#
|
95
|
-
#
|
96
|
-
# msg.subject # => 'other subject'
|
121
|
+
# ## "Thread" Element
|
97
122
|
#
|
98
|
-
#
|
123
|
+
# The primary use of the XMPP `thread` element is to uniquely identify a
|
124
|
+
# conversation thread or "chat session" between two entities instantiated by
|
125
|
+
# Message stanzas of type `chat`. However, the XMPP thread element can also
|
126
|
+
# be used to uniquely identify an analogous thread between two entities
|
127
|
+
# instantiated by Message stanzas of type `headline` or `normal`, or among
|
128
|
+
# multiple entities in the context of a multi-user chat room instantiated by
|
129
|
+
# Message stanzas of type `groupchat`. It MAY also be used for Message
|
130
|
+
# stanzas not related to a human conversation, such as a game session or an
|
131
|
+
# interaction between plugins. The `thread` element is not used to identify
|
132
|
+
# individual messages, only conversations or messagingg sessions. The
|
133
|
+
# inclusion of the `thread` element is optional.
|
99
134
|
#
|
100
|
-
# The
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
# conversation, such as a game session or an interaction between plugins. The +thread+ element is not used to
|
106
|
-
# identify individual messages, only conversations or messagingg sessions. The inclusion of the +thread+ element
|
107
|
-
# is optional.
|
135
|
+
# The value of the `thread` element is not human-readable and MUST be
|
136
|
+
# treated as opaque by entities; no semantic meaning can be derived from it,
|
137
|
+
# and only exact comparisons can be made against it. The value of the
|
138
|
+
# `thread` element MUST be a universally unique identifier (UUID) as
|
139
|
+
# described in [UUID].
|
108
140
|
#
|
109
|
-
# The
|
110
|
-
#
|
111
|
-
#
|
141
|
+
# The `thread` element MAY possess a 'parent' attribute that identifies
|
142
|
+
# another thread of which the current thread is an offshoot or child; the
|
143
|
+
# value of the 'parent' must conform to the syntax of the `thread` element
|
144
|
+
# itself.
|
112
145
|
#
|
113
|
-
#
|
114
|
-
# thread is an offshoot or child; the value of the 'parent' must conform to the syntax of the +thread+ element itself.
|
146
|
+
# Blather provides an attribute-like syntax for Message `thread` elements.
|
115
147
|
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
# msg.thread = '12345'
|
120
|
-
# msg.thread # => '12345'
|
148
|
+
# msg = Message.new
|
149
|
+
# msg.thread = '12345'
|
150
|
+
# msg.thread # => '12345'
|
121
151
|
#
|
122
152
|
# Parent threads can be set using a hash:
|
123
153
|
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
154
|
+
# msg.thread = {'parent-id' => 'thread-id'}
|
155
|
+
# msg.thread # => 'thread-id'
|
156
|
+
# msg.parent_thread # => 'parent-id'
|
127
157
|
#
|
158
|
+
# @handler :message
|
128
159
|
class Message < Stanza
|
129
|
-
VALID_TYPES = [:chat, :error, :groupchat, :headline, :normal]
|
160
|
+
VALID_TYPES = [:chat, :error, :groupchat, :headline, :normal].freeze
|
161
|
+
|
162
|
+
HTML_NS = 'http://jabber.org/protocol/xhtml-im'.freeze
|
163
|
+
HTML_BODY_NS = 'http://www.w3.org/1999/xhtml'.freeze
|
130
164
|
|
131
165
|
register :message
|
132
166
|
|
133
|
-
|
167
|
+
# @private
|
168
|
+
def self.import(node)
|
134
169
|
klass = nil
|
135
|
-
node.children.
|
170
|
+
node.children.detect do |e|
|
171
|
+
ns = e.namespace ? e.namespace.href : nil
|
172
|
+
klass = class_from_registration(e.element_name, ns)
|
173
|
+
end
|
136
174
|
|
137
175
|
if klass && klass != self
|
138
176
|
klass.import(node)
|
@@ -141,6 +179,11 @@ class Stanza
|
|
141
179
|
end
|
142
180
|
end
|
143
181
|
|
182
|
+
# Create a new Message stanza
|
183
|
+
#
|
184
|
+
# @param [#to_s] to the JID to send the message to
|
185
|
+
# @param [#to_s] body the body of the message
|
186
|
+
# @param [Symbol] type the message type. Must be one of VALID_TYPES
|
144
187
|
def self.new(to = nil, body = nil, type = :chat)
|
145
188
|
node = super :message
|
146
189
|
node.to = to
|
@@ -149,29 +192,141 @@ class Stanza
|
|
149
192
|
node
|
150
193
|
end
|
151
194
|
|
152
|
-
|
195
|
+
# Check if the Message is of type :chat
|
196
|
+
#
|
197
|
+
# @return [true, false]
|
198
|
+
def chat?
|
199
|
+
self.type == :chat
|
200
|
+
end
|
153
201
|
|
154
|
-
|
155
|
-
|
202
|
+
# Check if the Message is of type :error
|
203
|
+
#
|
204
|
+
# @return [true, false]
|
205
|
+
def error?
|
206
|
+
self.type == :error
|
207
|
+
end
|
208
|
+
|
209
|
+
# Check if the Message is of type :groupchat
|
210
|
+
#
|
211
|
+
# @return [true, false]
|
212
|
+
def groupchat?
|
213
|
+
self.type == :groupchat
|
214
|
+
end
|
215
|
+
|
216
|
+
# Check if the Message is of type :headline
|
217
|
+
#
|
218
|
+
# @return [true, false]
|
219
|
+
def headline?
|
220
|
+
self.type == :headline
|
221
|
+
end
|
222
|
+
|
223
|
+
# Check if the Message is of type :normal
|
224
|
+
#
|
225
|
+
# @return [true, false]
|
226
|
+
def normal?
|
227
|
+
self.type == :normal
|
228
|
+
end
|
229
|
+
|
230
|
+
# Ensures type is :get, :set, :result or :error
|
231
|
+
#
|
232
|
+
# @param [#to_sym] type the Message type. Must be one of VALID_TYPES
|
233
|
+
def type=(type)
|
234
|
+
if type && !VALID_TYPES.include?(type.to_sym)
|
235
|
+
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
|
236
|
+
end
|
156
237
|
super
|
157
238
|
end
|
158
239
|
|
159
|
-
|
160
|
-
|
240
|
+
# Get the message body
|
241
|
+
#
|
242
|
+
# @return [String]
|
243
|
+
def body
|
244
|
+
read_content :body
|
245
|
+
end
|
246
|
+
|
247
|
+
# Set the message body
|
248
|
+
#
|
249
|
+
# @param [#to_s] body the message body
|
250
|
+
def body=(body)
|
251
|
+
set_content_for :body, body
|
252
|
+
end
|
253
|
+
|
254
|
+
# Get the message xhtml node
|
255
|
+
# This will create the node if it doesn't exist
|
256
|
+
#
|
257
|
+
# @return [XML::Node]
|
258
|
+
def xhtml_node
|
259
|
+
unless h = find_first('ns:html', :ns => HTML_NS)
|
260
|
+
self << (h = XMPPNode.new('html', self.document))
|
261
|
+
h.namespace = HTML_NS
|
262
|
+
end
|
263
|
+
|
264
|
+
unless b = h.find_first('ns:body', :ns => HTML_BODY_NS)
|
265
|
+
h << (b = XMPPNode.new('body', self.document))
|
266
|
+
b.namespace = HTML_BODY_NS
|
267
|
+
end
|
268
|
+
|
269
|
+
b
|
270
|
+
end
|
271
|
+
|
272
|
+
# Get the message xhtml
|
273
|
+
#
|
274
|
+
# @return [String]
|
275
|
+
def xhtml
|
276
|
+
self.xhtml_node.content.strip
|
277
|
+
end
|
278
|
+
|
279
|
+
# Set the message xhtml
|
280
|
+
# This will use Nokogiri to ensure the xhtml is valid
|
281
|
+
#
|
282
|
+
# @param [#to_s] valid xhtml
|
283
|
+
def xhtml=(xhtml_body)
|
284
|
+
self.xhtml_node.content = Nokogiri::XML(xhtml_body).to_xhtml
|
285
|
+
end
|
161
286
|
|
162
|
-
|
287
|
+
# Get the message subject
|
288
|
+
#
|
289
|
+
# @return [String]
|
290
|
+
def subject
|
291
|
+
read_content :subject
|
292
|
+
end
|
293
|
+
|
294
|
+
# Set the message subject
|
295
|
+
#
|
296
|
+
# @param [#to_s] body the message subject
|
297
|
+
def subject=(subject)
|
298
|
+
set_content_for :subject, subject
|
299
|
+
end
|
300
|
+
|
301
|
+
# Get the message thread
|
302
|
+
#
|
303
|
+
# @return [String]
|
304
|
+
def thread
|
305
|
+
read_content :thread
|
306
|
+
end
|
163
307
|
|
164
|
-
|
308
|
+
# Get the parent thread
|
309
|
+
#
|
310
|
+
# @return [String, nil]
|
311
|
+
def parent_thread
|
165
312
|
n = find_first('thread')
|
166
313
|
n[:parent] if n
|
167
314
|
end
|
168
315
|
|
169
|
-
|
316
|
+
# Set the thread
|
317
|
+
#
|
318
|
+
# @overload thread=(hash)
|
319
|
+
# Set a thread with a parent
|
320
|
+
# @param [Hash<parent-id => thread-id>] thread
|
321
|
+
# @overload thread=(thread)
|
322
|
+
# Set a thread id
|
323
|
+
# @param [#to_s] thread the new thread id
|
324
|
+
def thread=(thread)
|
170
325
|
parent, thread = thread.to_a.flatten if thread.is_a?(Hash)
|
171
326
|
set_content_for :thread, thread
|
172
327
|
find_first('thread')[:parent] = parent
|
173
328
|
end
|
174
329
|
end
|
175
330
|
|
176
|
-
end
|
331
|
+
end
|
177
332
|
end
|