blather 0.4.8 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +14 -1
- data/lib/blather.rb +4 -0
- data/lib/blather/core_ext/nokogiri.rb +2 -0
- data/lib/blather/stanza/iq/command.rb +305 -0
- data/lib/blather/stanza/x.rb +351 -0
- data/spec/blather/stanza/iq/command_spec.rb +159 -0
- data/spec/blather/stanza/pubsub/retract_spec.rb +1 -1
- data/spec/blather/stanza/x_spec.rb +228 -0
- data/spec/blather/stream/client_spec.rb +1 -1
- data/spec/blather/xmpp_node_spec.rb +4 -3
- metadata +84 -13
data/README.md
CHANGED
@@ -148,6 +148,17 @@ Command line options:
|
|
148
148
|
-h, --help Show this message
|
149
149
|
-v, --version Show version
|
150
150
|
|
151
|
+
## Health warning:
|
152
|
+
|
153
|
+
Some parts of Blather will allow you to do stupid things that don't conform to XMPP
|
154
|
+
spec. You should exercise caution and read the relevant specifications (indicated in
|
155
|
+
the preamble to most relevant classes).
|
156
|
+
|
157
|
+
# Contributions
|
158
|
+
|
159
|
+
All contributions are welcome, even encouraged. However, contributions must be
|
160
|
+
well tested. If you send me a branch name to merge that'll get my attention faster
|
161
|
+
than a change set made directly on master.
|
151
162
|
|
152
163
|
# Author
|
153
164
|
|
@@ -155,7 +166,9 @@ Command line options:
|
|
155
166
|
|
156
167
|
### Contributors
|
157
168
|
|
158
|
-
[Nolan Darilek](http://github.com/thewordnerd)
|
169
|
+
* [Nolan Darilek](http://github.com/thewordnerd)
|
170
|
+
* [Tim Carey-Smith](http://github.com/halorgium)
|
171
|
+
* [Ben Langfeld](http://github.com/benlangfeld)
|
159
172
|
|
160
173
|
# Copyright
|
161
174
|
|
data/lib/blather.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
eventmachine
|
5
5
|
nokogiri
|
6
6
|
digest/md5
|
7
|
+
digest/sha1
|
7
8
|
logger
|
8
9
|
|
9
10
|
blather/core_ext/active_support
|
@@ -21,6 +22,7 @@
|
|
21
22
|
blather/stanza
|
22
23
|
blather/stanza/iq
|
23
24
|
blather/stanza/iq/query
|
25
|
+
blather/stanza/iq/command
|
24
26
|
blather/stanza/iq/roster
|
25
27
|
blather/stanza/disco
|
26
28
|
blather/stanza/disco/disco_info
|
@@ -46,6 +48,8 @@
|
|
46
48
|
blather/stanza/pubsub_owner/delete
|
47
49
|
blather/stanza/pubsub_owner/purge
|
48
50
|
|
51
|
+
blather/stanza/x
|
52
|
+
|
49
53
|
blather/stream
|
50
54
|
blather/stream/client
|
51
55
|
blather/stream/component
|
@@ -22,9 +22,11 @@ module XML
|
|
22
22
|
# and namespace designation
|
23
23
|
def xpath(*paths)
|
24
24
|
paths[0] = paths[0].to_s
|
25
|
+
|
25
26
|
if paths.size > 1 && (namespaces = paths.pop).is_a?(Hash)
|
26
27
|
paths << namespaces.inject({}) { |h,v| h[v[0].to_s] = v[1]; h }
|
27
28
|
end
|
29
|
+
|
28
30
|
nokogiri_xpath *paths
|
29
31
|
end
|
30
32
|
alias_method :find, :xpath
|
@@ -0,0 +1,305 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
class Iq
|
4
|
+
|
5
|
+
# # Command Stanza
|
6
|
+
#
|
7
|
+
# [XEP-0050 Ad-Hoc Commands](http://xmpp.org/extensions/xep-0050.html)
|
8
|
+
#
|
9
|
+
# This is a base class for any command based Iq stanzas. It provides a base set
|
10
|
+
# of methods for working with command stanzas
|
11
|
+
#
|
12
|
+
# @handler :command
|
13
|
+
class Command < Iq
|
14
|
+
VALID_ACTIONS = [:cancel, :execute, :complete, :next, :prev].freeze
|
15
|
+
VALID_STATUS = [:executing, :completed, :canceled].freeze
|
16
|
+
VALID_NOTE_TYPES = [:info, :warn, :error].freeze
|
17
|
+
|
18
|
+
register :command, :command, 'http://jabber.org/protocol/commands'
|
19
|
+
|
20
|
+
# Overrides the parent method to ensure a command node is created
|
21
|
+
#
|
22
|
+
# @param [:get, :set, :result, :error, nil] type the IQ type
|
23
|
+
# @param [String] node the name of the node
|
24
|
+
# @param [:cancel, :execute, :complete, :next, :prev, nil] action the command's action
|
25
|
+
# @return [Command] a new Command stanza
|
26
|
+
def self.new(type = :set, node = nil, action = :execute)
|
27
|
+
new_node = super type
|
28
|
+
new_node.command
|
29
|
+
new_node.node = node
|
30
|
+
new_node.action = action
|
31
|
+
new_node.new_sessionid!
|
32
|
+
new_node
|
33
|
+
end
|
34
|
+
|
35
|
+
# Overrides the parent method to ensure the current command node is destroyed
|
36
|
+
#
|
37
|
+
# @see Blather::Stanza::Iq#inherit
|
38
|
+
def inherit(node)
|
39
|
+
command.remove
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
# Command node accessor
|
44
|
+
# If a command node exists it will be returned.
|
45
|
+
# Otherwise a new node will be created and returned
|
46
|
+
#
|
47
|
+
# @return [Blather::XMPPNode]
|
48
|
+
def command
|
49
|
+
c = if self.class.registered_ns
|
50
|
+
find_first('command_ns:command', :command_ns => self.class.registered_ns)
|
51
|
+
else
|
52
|
+
find_first('command')
|
53
|
+
end
|
54
|
+
|
55
|
+
unless c
|
56
|
+
(self << (c = XMPPNode.new('command', self.document)))
|
57
|
+
c.namespace = self.class.registered_ns
|
58
|
+
end
|
59
|
+
c
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get the name of the node
|
63
|
+
#
|
64
|
+
# @return [String, nil]
|
65
|
+
def node
|
66
|
+
command[:node]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Set the name of the node
|
70
|
+
#
|
71
|
+
# @param [String, nil] node the new node name
|
72
|
+
def node=(node)
|
73
|
+
command[:node] = node
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get the sessionid of the command
|
77
|
+
#
|
78
|
+
# @return [String, nil]
|
79
|
+
def sessionid
|
80
|
+
command[:sessionid]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Set the sessionid of the command
|
84
|
+
#
|
85
|
+
# @param [String, nil] sessionid the new sessionid
|
86
|
+
def sessionid=(sessionid)
|
87
|
+
command[:sessionid] = Digest::SHA1.hexdigest(sessionid)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Generate a new session ID (SHA-1 hash)
|
91
|
+
def new_sessionid!
|
92
|
+
self.sessionid = "commandsession-"+id
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get the action of the command
|
96
|
+
#
|
97
|
+
# @return [Symbol, nil]
|
98
|
+
def action
|
99
|
+
command.read_attr :action, :to_sym
|
100
|
+
end
|
101
|
+
|
102
|
+
# Check if the command action is :cancel
|
103
|
+
#
|
104
|
+
# @return [true, false]
|
105
|
+
def cancel?
|
106
|
+
self.action == :cancel
|
107
|
+
end
|
108
|
+
|
109
|
+
# Check if the command action is :execute
|
110
|
+
#
|
111
|
+
# @return [true, false]
|
112
|
+
def execute?
|
113
|
+
self.action == :execute
|
114
|
+
end
|
115
|
+
|
116
|
+
# Check if the command action is :complete
|
117
|
+
#
|
118
|
+
# @return [true, false]
|
119
|
+
def complete?
|
120
|
+
self.action == :complete
|
121
|
+
end
|
122
|
+
|
123
|
+
# Check if the command action is :next
|
124
|
+
#
|
125
|
+
# @return [true, false]
|
126
|
+
def next?
|
127
|
+
self.action == :next
|
128
|
+
end
|
129
|
+
|
130
|
+
# Check if the command action is :prev
|
131
|
+
#
|
132
|
+
# @return [true, false]
|
133
|
+
def prev?
|
134
|
+
self.action == :prev
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set the action of the command
|
138
|
+
#
|
139
|
+
# @param [:cancel, :execute, :complete, :next, :prev] action the new action
|
140
|
+
def action=(action)
|
141
|
+
if action && !VALID_ACTIONS.include?(action.to_sym)
|
142
|
+
raise ArgumentError, "Invalid Action (#{action}), use: #{VALID_ACTIONS*' '}"
|
143
|
+
end
|
144
|
+
command[:action] = action
|
145
|
+
end
|
146
|
+
|
147
|
+
# Get the status of the command
|
148
|
+
#
|
149
|
+
# @return [Symbol, nil]
|
150
|
+
def status
|
151
|
+
command.read_attr :status, :to_sym
|
152
|
+
end
|
153
|
+
|
154
|
+
# Check if the command status is :executing
|
155
|
+
#
|
156
|
+
# @return [true, false]
|
157
|
+
def executing?
|
158
|
+
self.status == :executing
|
159
|
+
end
|
160
|
+
|
161
|
+
# Check if the command status is :completed
|
162
|
+
#
|
163
|
+
# @return [true, false]
|
164
|
+
def completed?
|
165
|
+
self.status == :completed
|
166
|
+
end
|
167
|
+
|
168
|
+
# Check if the command status is :canceled
|
169
|
+
#
|
170
|
+
# @return [true, false]
|
171
|
+
def canceled?
|
172
|
+
self.status == :canceled
|
173
|
+
end
|
174
|
+
|
175
|
+
# Set the status of the command
|
176
|
+
#
|
177
|
+
# @param [:executing, :completed, :canceled] status the new status
|
178
|
+
def status=(status)
|
179
|
+
if status && !VALID_STATUS.include?(status.to_sym)
|
180
|
+
raise ArgumentError, "Invalid Action (#{statusn}), use: #{VALID_STATUS*' '}"
|
181
|
+
end
|
182
|
+
command[:status] = status
|
183
|
+
end
|
184
|
+
|
185
|
+
# Command actions accessor
|
186
|
+
# If a command actions element exists it will be returned.
|
187
|
+
# Otherwise a new actions element will be created and returned
|
188
|
+
#
|
189
|
+
# @return [Blather::XMPPNode]
|
190
|
+
def actions
|
191
|
+
a = find_first('actions')
|
192
|
+
|
193
|
+
unless a
|
194
|
+
(self << (a = XMPPNode.new('actions', self.document)))
|
195
|
+
end
|
196
|
+
a
|
197
|
+
end
|
198
|
+
|
199
|
+
# Get the command's allowed actions
|
200
|
+
#
|
201
|
+
# @return [[Symbol]]
|
202
|
+
def allowed_actions
|
203
|
+
a = []
|
204
|
+
a << :execute
|
205
|
+
actions.children.each do |action|
|
206
|
+
a << action.name.to_sym
|
207
|
+
end
|
208
|
+
a
|
209
|
+
end
|
210
|
+
|
211
|
+
# Add allowed actions to the command
|
212
|
+
#
|
213
|
+
# @param [[:prev, :next, :complete]] allowed_actions the new allowed actions
|
214
|
+
def add_allowed_actions(allowed_actions)
|
215
|
+
[allowed_actions].flatten.each do |action|
|
216
|
+
if action && !VALID_ACTIONS.include?(action.to_sym)
|
217
|
+
raise ArgumentError, "Invalid Action (#{action}), use: #{VALID_ACTIONS*' '}"
|
218
|
+
end
|
219
|
+
actions << "<#{action.to_s}/>"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Remove allowed actions from the command
|
224
|
+
#
|
225
|
+
# @param [[:prev, :next, :complete]] disallowed_actions the allowed actions to remove
|
226
|
+
def remove_allowed_actions(disallowed_actions)
|
227
|
+
[disallowed_actions].flatten.each do |action|
|
228
|
+
actions.remove_children action.to_sym
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Command note accessor
|
233
|
+
# If a command note exists it will be returned.
|
234
|
+
# Otherwise a new note will be created and returned
|
235
|
+
#
|
236
|
+
# @return [Blather::XMPPNode]
|
237
|
+
def note
|
238
|
+
n = find_first('note')
|
239
|
+
|
240
|
+
unless n
|
241
|
+
(self << (n = XMPPNode.new('note', self.document)))
|
242
|
+
end
|
243
|
+
n
|
244
|
+
end
|
245
|
+
|
246
|
+
# Get the note_type of the command
|
247
|
+
#
|
248
|
+
# @return [Symbol, nil]
|
249
|
+
def note_type
|
250
|
+
note.read_attr :type, :to_sym
|
251
|
+
end
|
252
|
+
|
253
|
+
# Check if the command status is :info
|
254
|
+
#
|
255
|
+
# @return [true, false]
|
256
|
+
def info?
|
257
|
+
self.note_type == :info
|
258
|
+
end
|
259
|
+
|
260
|
+
# Check if the command status is :warn
|
261
|
+
#
|
262
|
+
# @return [true, false]
|
263
|
+
def warn?
|
264
|
+
self.status == :warn
|
265
|
+
end
|
266
|
+
|
267
|
+
# Check if the command status is :error
|
268
|
+
#
|
269
|
+
# @return [true, false]
|
270
|
+
def error?
|
271
|
+
self.status == :error
|
272
|
+
end
|
273
|
+
|
274
|
+
# Set the note_type of the command
|
275
|
+
#
|
276
|
+
# @param [:executing, :completed, :canceled] note_type the new note_type
|
277
|
+
def note_type=(note_type)
|
278
|
+
if note_type && !VALID_NOTE_TYPES.include?(note_type.to_sym)
|
279
|
+
raise ArgumentError, "Invalid Action (#{note_type}), use: #{VALID_NOTE_TYPES*' '}"
|
280
|
+
end
|
281
|
+
note[:type] = note_type
|
282
|
+
end
|
283
|
+
|
284
|
+
# Get the text of the command's note
|
285
|
+
def note_text
|
286
|
+
content_from "note"
|
287
|
+
end
|
288
|
+
|
289
|
+
# Set the command's note text
|
290
|
+
#
|
291
|
+
# @param [String] note_text the command's new note text
|
292
|
+
def note_text=(note_text)
|
293
|
+
set_content_for "note", note_text
|
294
|
+
end
|
295
|
+
|
296
|
+
# Returns the command's x:data form child
|
297
|
+
def form
|
298
|
+
X.new command.find_first('//ns:x', :ns => X.registered_ns)
|
299
|
+
end
|
300
|
+
|
301
|
+
end #Command
|
302
|
+
|
303
|
+
end #Iq
|
304
|
+
end #Stanza
|
305
|
+
end
|
@@ -0,0 +1,351 @@
|
|
1
|
+
module Blather
|
2
|
+
class Stanza
|
3
|
+
# # X Stanza
|
4
|
+
#
|
5
|
+
# [XEP-0004 Data Forms](http://xmpp.org/extensions/xep-0004.html)
|
6
|
+
#
|
7
|
+
# Data Form node that allows for semi-structured data exchange
|
8
|
+
#
|
9
|
+
# @handler :x
|
10
|
+
class X < XMPPNode
|
11
|
+
register :x, 'jabber:x:data'
|
12
|
+
|
13
|
+
VALID_TYPES = [:cancel, :form, :result, :submit].freeze
|
14
|
+
|
15
|
+
# Create a new X node
|
16
|
+
# @param [:cancel, :form, :result, :submit, nil] type the x:form type
|
17
|
+
# @param [Array<Array, X::Field>, nil] fields a list of fields.
|
18
|
+
# These are passed directly to X::Field.new
|
19
|
+
# @return [X] a new X stanza
|
20
|
+
def self.new(type = nil, fields = [])
|
21
|
+
new_node = super :x
|
22
|
+
|
23
|
+
case type
|
24
|
+
when Nokogiri::XML::Node
|
25
|
+
new_node.inherit type
|
26
|
+
when Hash
|
27
|
+
new_node.type = type[:type]
|
28
|
+
new_node.add_fields([type[:fields]])
|
29
|
+
else
|
30
|
+
new_node.type = type
|
31
|
+
new_node.add_fields([fields])
|
32
|
+
end
|
33
|
+
new_node
|
34
|
+
end
|
35
|
+
|
36
|
+
# The Form's type
|
37
|
+
# @return [Symbol]
|
38
|
+
def type
|
39
|
+
read_attr :type, :to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
# Set the Form's type
|
43
|
+
# @param [:cancel, :form, :result, :submit] type the new type for the form
|
44
|
+
def type=(type)
|
45
|
+
if type && !VALID_TYPES.include?(type.to_sym)
|
46
|
+
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
|
47
|
+
end
|
48
|
+
write_attr :type, type
|
49
|
+
end
|
50
|
+
|
51
|
+
# List of field objects
|
52
|
+
# @return [Blather::Stanza::X::Field]
|
53
|
+
def fields
|
54
|
+
self.find('ns:field', :ns => self.class.registered_ns).map do |f|
|
55
|
+
Field.new f
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Add an array of fields to form
|
60
|
+
# @param fields the array of fields, passed directly to Field.new
|
61
|
+
def add_fields(fields = [])
|
62
|
+
[fields].flatten.each { |f| self << Field.new(f) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Check if the x is of type :cancel
|
66
|
+
#
|
67
|
+
# @return [true, false]
|
68
|
+
def cancel?
|
69
|
+
self.type == :cancel
|
70
|
+
end
|
71
|
+
|
72
|
+
# Check if the x is of type :form
|
73
|
+
#
|
74
|
+
# @return [true, false]
|
75
|
+
def form?
|
76
|
+
self.type == :form
|
77
|
+
end
|
78
|
+
|
79
|
+
# Check if the x is of type :result
|
80
|
+
#
|
81
|
+
# @return [true, false]
|
82
|
+
def result?
|
83
|
+
self.type == :result
|
84
|
+
end
|
85
|
+
|
86
|
+
# Check if the x is of type :submit
|
87
|
+
#
|
88
|
+
# @return [true, false]
|
89
|
+
def submit?
|
90
|
+
self.type == :submit
|
91
|
+
end
|
92
|
+
|
93
|
+
# Retrieve the form's instructions
|
94
|
+
#
|
95
|
+
# @return [String]
|
96
|
+
def instructions
|
97
|
+
if i = self.find_first('ns:instructions', :ns => self.class.registered_ns)
|
98
|
+
i.children.inner_text
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Set the form's instructions
|
103
|
+
#
|
104
|
+
# @param [String] instructions the form's instructions
|
105
|
+
def instructions=(instructions)
|
106
|
+
self.remove_children :instructions
|
107
|
+
self << "<instructions>#{instructions}</instructions>"
|
108
|
+
end
|
109
|
+
|
110
|
+
# Retrieve the form's title
|
111
|
+
#
|
112
|
+
# @return [String]
|
113
|
+
def title
|
114
|
+
if t = self.find_first('ns:title', :ns => self.class.registered_ns)
|
115
|
+
t.children.inner_text
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Set the form's title
|
120
|
+
#
|
121
|
+
# @param [String] title the form's title
|
122
|
+
def title=(title)
|
123
|
+
self.remove_children :title
|
124
|
+
self << "<title>#{title}</title>"
|
125
|
+
end
|
126
|
+
|
127
|
+
class Field < XMPPNode
|
128
|
+
VALID_TYPES = [:boolean, :fixed, :hidden, :"jid-multi", :"jid-single", :"list-multi", :"list-single", :"text-multi", :"text-private", :"text-single"].freeze
|
129
|
+
|
130
|
+
# Create a new X Field
|
131
|
+
# @overload new(node)
|
132
|
+
# Imports the XML::Node to create a Field object
|
133
|
+
# @param [XML::Node] node the node object to import
|
134
|
+
# @overload new(opts = {})
|
135
|
+
# Creates a new Field using a hash of options
|
136
|
+
# @param [Hash] opts a hash of options
|
137
|
+
# @option opts [:boolean, :fixed, :hidden, :"jid-multi", :"jid-single", :"list-multi", :"list-single", :"text-multi", :"text-private", :"text-single"] :type the type of the field
|
138
|
+
# @option opts [String] :var the variable for the field
|
139
|
+
# @option opts [String] :label the label for the field
|
140
|
+
# @overload new(type, var = nil, label = nil)
|
141
|
+
# Create a new Field by name
|
142
|
+
# @param [:boolean, :fixed, :hidden, :"jid-multi", :"jid-single", :"list-multi", :"list-single", :"text-multi", :"text-private", :"text-single"] type the type of the field
|
143
|
+
# @param [String, nil] var the variable for the field
|
144
|
+
# @param [String, nil] label the label for the field
|
145
|
+
def self.new(type, var = nil, label = nil)
|
146
|
+
new_node = super :field
|
147
|
+
|
148
|
+
case type
|
149
|
+
when Nokogiri::XML::Node
|
150
|
+
new_node.inherit type
|
151
|
+
when Hash
|
152
|
+
new_node.type = type[:type]
|
153
|
+
new_node.var = type[:var]
|
154
|
+
new_node.label = type[:label]
|
155
|
+
else
|
156
|
+
new_node.type = type
|
157
|
+
new_node.var = var
|
158
|
+
new_node.label = label
|
159
|
+
end
|
160
|
+
new_node
|
161
|
+
end
|
162
|
+
|
163
|
+
# The Field's type
|
164
|
+
# @return [String]
|
165
|
+
def type
|
166
|
+
read_attr :type
|
167
|
+
end
|
168
|
+
|
169
|
+
# Set the Field's type
|
170
|
+
# @param [#to_sym] type the new type for the field
|
171
|
+
def type=(type)
|
172
|
+
if type && !VALID_TYPES.include?(type.to_sym)
|
173
|
+
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
|
174
|
+
end
|
175
|
+
write_attr :type, type
|
176
|
+
end
|
177
|
+
|
178
|
+
# The Field's var
|
179
|
+
# @return [String]
|
180
|
+
def var
|
181
|
+
read_attr :var
|
182
|
+
end
|
183
|
+
|
184
|
+
# Set the Field's var
|
185
|
+
# @param [String] var the new var for the field
|
186
|
+
def var=(var)
|
187
|
+
write_attr :var, var
|
188
|
+
end
|
189
|
+
|
190
|
+
# The Field's label
|
191
|
+
# @return [String]
|
192
|
+
def label
|
193
|
+
read_attr :label
|
194
|
+
end
|
195
|
+
|
196
|
+
# Set the Field's label
|
197
|
+
# @param [String] label the new label for the field
|
198
|
+
def label=(label)
|
199
|
+
write_attr :label, label
|
200
|
+
end
|
201
|
+
|
202
|
+
# Get the field's value
|
203
|
+
#
|
204
|
+
# @param [String]
|
205
|
+
def value
|
206
|
+
if v = self.find_first('value')
|
207
|
+
v.children.inner_text
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Set the field's value
|
212
|
+
#
|
213
|
+
# @param [String] value the field's value
|
214
|
+
def value=(value)
|
215
|
+
self.remove_children :value
|
216
|
+
self << "<value>#{value}</value>"
|
217
|
+
end
|
218
|
+
|
219
|
+
# Get the field's description
|
220
|
+
#
|
221
|
+
# @param [String]
|
222
|
+
def desc
|
223
|
+
if d = self.find_first('desc')
|
224
|
+
d.children.inner_text
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Set the field's description
|
229
|
+
#
|
230
|
+
# @param [String] description the field's description
|
231
|
+
def desc=(description)
|
232
|
+
self.remove_children :desc
|
233
|
+
self << "<desc>#{description}</desc>"
|
234
|
+
end
|
235
|
+
|
236
|
+
# Get the field's required flag
|
237
|
+
#
|
238
|
+
# @param [true, false]
|
239
|
+
def required?
|
240
|
+
self.find_first('required') ? true : false
|
241
|
+
end
|
242
|
+
|
243
|
+
# Set the field's required flag
|
244
|
+
#
|
245
|
+
# @param [true, false] required the field's required flag
|
246
|
+
def required!(required = true)
|
247
|
+
if self.find_first('required')
|
248
|
+
if required==false
|
249
|
+
self.remove_children :required
|
250
|
+
end
|
251
|
+
else
|
252
|
+
if required==true
|
253
|
+
self << "<required/>"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Extract list of option objects
|
259
|
+
#
|
260
|
+
# @return [Blather::Stanza::X::Field::Option]
|
261
|
+
def options
|
262
|
+
self.find('option').map do |f|
|
263
|
+
Option.new f
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Add an array of options to field
|
268
|
+
# @param options the array of options, passed directly to Option.new
|
269
|
+
def add_options(options = [])
|
270
|
+
[options].flatten.each { |o| self << Option.new(o) }
|
271
|
+
end
|
272
|
+
|
273
|
+
# Compare two Field objects by type, var and label
|
274
|
+
# @param [X::Field] o the Field object to compare against
|
275
|
+
# @return [true, false]
|
276
|
+
def eql?(o)
|
277
|
+
unless o.is_a?(self.class)
|
278
|
+
raise "Cannot compare #{self.class} with #{o.class}"
|
279
|
+
end
|
280
|
+
|
281
|
+
o.type == self.type &&
|
282
|
+
o.var == self.var &&
|
283
|
+
o.label == self.label &&
|
284
|
+
o.desc == self.desc &&
|
285
|
+
o.required? == self.required? &&
|
286
|
+
o.value == self.value
|
287
|
+
end
|
288
|
+
alias_method :==, :eql?
|
289
|
+
|
290
|
+
class Option < XMPPNode
|
291
|
+
# Create a new X Field Option
|
292
|
+
# @overload new(node)
|
293
|
+
# Imports the XML::Node to create a Field option object
|
294
|
+
# @param [XML::Node] node the node object to import
|
295
|
+
# @overload new(opts = {})
|
296
|
+
# Creates a new Field option using a hash of options
|
297
|
+
# @param [Hash] opts a hash of options
|
298
|
+
# @option opts [String] :value the value of the field option
|
299
|
+
# @option opts [String] :label the human readable label for the field option
|
300
|
+
# @overload new(value, label = nil)
|
301
|
+
# Create a new Field option by name
|
302
|
+
# @param [String] value the value of the field option
|
303
|
+
# @param [String, nil] label the human readable label for the field option
|
304
|
+
def self.new(value, label = nil)
|
305
|
+
new_node = super :option
|
306
|
+
|
307
|
+
case value
|
308
|
+
when Nokogiri::XML::Node
|
309
|
+
new_node.inherit value
|
310
|
+
when Hash
|
311
|
+
new_node.value = value[:value]
|
312
|
+
new_node.label = value[:label]
|
313
|
+
else
|
314
|
+
new_node.value = value
|
315
|
+
new_node.label = label
|
316
|
+
end
|
317
|
+
new_node
|
318
|
+
end
|
319
|
+
|
320
|
+
# The Field Option's value
|
321
|
+
# @return [String]
|
322
|
+
def value
|
323
|
+
if v = self.find_first('value')
|
324
|
+
v.children.inner_text
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Set the Field Option's value
|
329
|
+
# @param [String] value the new value for the field option
|
330
|
+
def value=(v)
|
331
|
+
self.remove_children :value
|
332
|
+
self << "<value>#{v}</value>"
|
333
|
+
end
|
334
|
+
|
335
|
+
# The Field Option's label
|
336
|
+
# @return [String]
|
337
|
+
def label
|
338
|
+
read_attr :label
|
339
|
+
end
|
340
|
+
|
341
|
+
# Set the Field Option's label
|
342
|
+
# @param [String] label the new label for the field option
|
343
|
+
def label=(label)
|
344
|
+
write_attr :label, label
|
345
|
+
end
|
346
|
+
end # Option
|
347
|
+
end # Field
|
348
|
+
end # X
|
349
|
+
|
350
|
+
end #Stanza
|
351
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. .. spec_helper])
|
2
|
+
|
3
|
+
def command_xml
|
4
|
+
<<-XML
|
5
|
+
<iq type='result'
|
6
|
+
from='catalog.shakespeare.lit'
|
7
|
+
to='romeo@montague.net/orchard'
|
8
|
+
id='form2'>
|
9
|
+
<command xmlns='http://jabber.org/protocol/commands'
|
10
|
+
node='node1'
|
11
|
+
action='execute'
|
12
|
+
sessionid='dqjiodmqlmakm'>
|
13
|
+
<x xmlns='jabber:x:data' type='form'>
|
14
|
+
<field var='field-name' type='text-single' label='description' />
|
15
|
+
</x>
|
16
|
+
</command>
|
17
|
+
</iq>
|
18
|
+
XML
|
19
|
+
end
|
20
|
+
|
21
|
+
describe Blather::Stanza::Iq::Command do
|
22
|
+
it 'registers itself' do
|
23
|
+
Blather::XMPPNode.class_from_registration(:command, 'http://jabber.org/protocol/commands').must_equal Blather::Stanza::Iq::Command
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'must be importable' do
|
27
|
+
Blather::XMPPNode.import(parse_stanza(command_xml).root).must_be_instance_of Blather::Stanza::Iq::Command
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'ensures a command node is present on create' do
|
31
|
+
c = Blather::Stanza::Iq::Command.new
|
32
|
+
c.xpath('xmlns:command', :xmlns => Blather::Stanza::Iq::Command.registered_ns).wont_be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'ensures a command node exists when calling #command' do
|
36
|
+
c = Blather::Stanza::Iq::Command.new
|
37
|
+
c.remove_children :command
|
38
|
+
c.xpath('ns:command', :ns => Blather::Stanza::Iq::Command.registered_ns).must_be_empty
|
39
|
+
|
40
|
+
c.command.wont_be_nil
|
41
|
+
c.xpath('ns:command', :ns => Blather::Stanza::Iq::Command.registered_ns).wont_be_empty
|
42
|
+
end
|
43
|
+
|
44
|
+
Blather::Stanza::Iq::Command::VALID_ACTIONS.each do |valid_action|
|
45
|
+
it "provides a helper (#{valid_action}?) for action #{valid_action}" do
|
46
|
+
Blather::Stanza::Iq::Command.new.must_respond_to :"#{valid_action}?"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Blather::Stanza::Iq::Command::VALID_STATUS.each do |valid_status|
|
51
|
+
it "provides a helper (#{valid_status}?) for status #{valid_status}" do
|
52
|
+
Blather::Stanza::Iq::Command.new.must_respond_to :"#{valid_status}?"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Blather::Stanza::Iq::Command::VALID_NOTE_TYPES.each do |valid_note_type|
|
57
|
+
it "provides a helper (#{valid_note_type}?) for note_type #{valid_note_type}" do
|
58
|
+
Blather::Stanza::Iq::Command.new.must_respond_to :"#{valid_note_type}?"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
[:cancel, :execute, :complete, :next, :prev].each do |action|
|
63
|
+
it "action can be set as \"#{action}\"" do
|
64
|
+
c = Blather::Stanza::Iq::Command.new nil, nil, action
|
65
|
+
c.action.must_equal action
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
[:get, :set, :result, :error].each do |type|
|
70
|
+
it "can be set as \"#{type}\"" do
|
71
|
+
c = Blather::Stanza::Iq::Command.new type
|
72
|
+
c.type.must_equal type
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'sets type to "result" on reply' do
|
77
|
+
c = Blather::Stanza::Iq::Command.new
|
78
|
+
c.type.must_equal :set
|
79
|
+
reply = c.reply.type.must_equal :result
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'sets type to "result" on reply!' do
|
83
|
+
c = Blather::Stanza::Iq::Command.new
|
84
|
+
c.type.must_equal :set
|
85
|
+
c.reply!
|
86
|
+
c.type.must_equal :result
|
87
|
+
end
|
88
|
+
|
89
|
+
# TODO: Deal with #reply/#reply! better?
|
90
|
+
|
91
|
+
it 'can be registered under a namespace' do
|
92
|
+
class CommandNs < Blather::Stanza::Iq::Command; register :command_ns, nil, 'command:ns'; end
|
93
|
+
Blather::XMPPNode.class_from_registration(:command, 'command:ns').must_equal CommandNs
|
94
|
+
c_ns = CommandNs.new
|
95
|
+
c_ns.xpath('command').must_be_empty
|
96
|
+
c_ns.xpath('ns:command', :ns => 'command:ns').size.must_equal 1
|
97
|
+
|
98
|
+
c_ns.command
|
99
|
+
c_ns.command
|
100
|
+
c_ns.xpath('ns:command', :ns => 'command:ns').size.must_equal 1
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'is constructed properly' do
|
104
|
+
n = Blather::Stanza::Iq::Command.new :set, "node", :execute
|
105
|
+
n.to = 'to@jid.com'
|
106
|
+
n.find("/iq[@to='to@jid.com' and @type='set' and @id='#{n.id}']/ns:command[@node='node' and @action='execute']", :ns => Blather::Stanza::Iq::Command.registered_ns).wont_be_empty
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'has an action attribute' do
|
110
|
+
n = Blather::Stanza::Iq::Command.new
|
111
|
+
n.action.must_equal :execute
|
112
|
+
n.action = :cancel
|
113
|
+
n.action.must_equal :cancel
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'has a status attribute' do
|
117
|
+
n = Blather::Stanza::Iq::Command.new
|
118
|
+
n.status.must_equal nil
|
119
|
+
n.status = :executing
|
120
|
+
n.status.must_equal :executing
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'has a sessionid attribute' do
|
124
|
+
n = Blather::Stanza::Iq::Command.new
|
125
|
+
n.sessionid.wont_be_nil
|
126
|
+
n.sessionid = "somerandomstring"
|
127
|
+
n.sessionid.must_equal Digest::SHA1.hexdigest("somerandomstring")
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'has an allowed_actions attribute' do
|
131
|
+
n = Blather::Stanza::Iq::Command.new
|
132
|
+
n.allowed_actions.must_equal [:execute]
|
133
|
+
n.add_allowed_actions [:prev, :next]
|
134
|
+
n.allowed_actions.must_equal [:execute, :prev, :next]
|
135
|
+
n.remove_allowed_actions :prev
|
136
|
+
n.allowed_actions.must_equal [:execute, :next]
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'has a note_type attribute' do
|
140
|
+
n = Blather::Stanza::Iq::Command.new
|
141
|
+
n.note_type.must_equal nil
|
142
|
+
n.note_type = :info
|
143
|
+
n.note_type.must_equal :info
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'has a note_text attribute' do
|
147
|
+
n = Blather::Stanza::Iq::Command.new
|
148
|
+
n.note_text.must_equal nil
|
149
|
+
n.note_text = "Some text"
|
150
|
+
n.note_text.must_equal "Some text"
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'makes a form child available' do
|
154
|
+
n = Blather::XMPPNode.import(parse_stanza(command_xml).root)
|
155
|
+
n.form.fields.size.must_equal 1
|
156
|
+
n.form.fields.map { |f| f.class }.uniq.must_equal [Blather::Stanza::X::Field]
|
157
|
+
n.form.must_be_instance_of Blather::Stanza::X
|
158
|
+
end
|
159
|
+
end
|
@@ -69,7 +69,7 @@ describe Blather::Stanza::PubSub::Retract do
|
|
69
69
|
|
70
70
|
it 'will iterate over each retraction' do
|
71
71
|
Blather::XMPPNode.import(parse_stanza(retract_xml).root).each do |i|
|
72
|
-
i.must_include
|
72
|
+
i.must_include "ae890ac52d0df67ed7cfdf51b644e901"
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
|
2
|
+
|
3
|
+
def x_xml
|
4
|
+
<<-XML
|
5
|
+
<x xmlns='jabber:x:data'
|
6
|
+
type='form'>
|
7
|
+
<title/>
|
8
|
+
<instructions/>
|
9
|
+
<field var='field-name'
|
10
|
+
type='text-single'
|
11
|
+
label='description' />
|
12
|
+
<field var='field-name2'
|
13
|
+
type='text-single'
|
14
|
+
label='description' />
|
15
|
+
<field var='field-name3'
|
16
|
+
type='text-single'
|
17
|
+
label='description' />
|
18
|
+
<field var='field-name'
|
19
|
+
type='{field-type}'
|
20
|
+
label='description'>
|
21
|
+
<desc/>
|
22
|
+
<required/>
|
23
|
+
<value>field-value</value>
|
24
|
+
<option label='option-label'><value>option-value</value></option>
|
25
|
+
<option label='option-label'><value>option-value</value></option>
|
26
|
+
</field>
|
27
|
+
</x>
|
28
|
+
XML
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Blather::Stanza::X do
|
32
|
+
|
33
|
+
it 'can be created from an XML string' do
|
34
|
+
x = Blather::Stanza::X.new parse_stanza(x_xml).root
|
35
|
+
x.type.must_equal :form
|
36
|
+
x.must_be_instance_of Blather::Stanza::X
|
37
|
+
end
|
38
|
+
|
39
|
+
[:cancel, :form, :result, :submit].each do |type|
|
40
|
+
it "type can be set as \"#{type}\"" do
|
41
|
+
x = Blather::Stanza::X.new type
|
42
|
+
x.type.must_equal type
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'is constructed properly' do
|
47
|
+
n = Blather::Stanza::X.new :form
|
48
|
+
n.find("/ns:x[@type='form']", :ns => Blather::Stanza::X.registered_ns).wont_be_empty
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'has an action attribute' do
|
52
|
+
n = Blather::Stanza::X.new :form
|
53
|
+
n.type.must_equal :form
|
54
|
+
n.type = :submit
|
55
|
+
n.type.must_equal :submit
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'has a title attribute' do
|
59
|
+
n = Blather::Stanza::X.new :form
|
60
|
+
n.title.must_equal nil
|
61
|
+
n.title = "Hello World!"
|
62
|
+
n.title.must_equal "Hello World!"
|
63
|
+
n.title = "goodbye"
|
64
|
+
n.title.must_equal "goodbye"
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'has an instructions attribute' do
|
68
|
+
n = Blather::Stanza::X.new :form
|
69
|
+
n.instructions.must_equal nil
|
70
|
+
n.instructions = "Please fill in this form"
|
71
|
+
n.instructions.must_equal "Please fill in this form"
|
72
|
+
n.instructions = "goodbye"
|
73
|
+
n.instructions.must_equal "goodbye"
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'inherits a list of fields' do
|
77
|
+
n = Blather::Stanza::Iq::Command.new
|
78
|
+
n.command << parse_stanza(x_xml).root
|
79
|
+
r = Blather::Stanza::X.new.inherit n.form
|
80
|
+
r.fields.size.must_equal 4
|
81
|
+
r.fields.map { |f| f.class }.uniq.must_equal [Blather::Stanza::X::Field]
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'takes a list of hashes for fields' do
|
85
|
+
fields = [
|
86
|
+
{:label => 'label', :type => 'text-single', :var => 'var'},
|
87
|
+
{:label => 'label1', :type => 'text-single', :var => 'var1'},
|
88
|
+
]
|
89
|
+
|
90
|
+
control = [ Blather::Stanza::X::Field.new(*%w[text-single var label]),
|
91
|
+
Blather::Stanza::X::Field.new(*%w[text-single var1 label1])]
|
92
|
+
|
93
|
+
di = Blather::Stanza::X.new nil, fields
|
94
|
+
di.fields.size.must_equal 2
|
95
|
+
di.fields.each { |f| control.include?(f).must_equal true }
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'takes a list of Field objects as fields' do
|
99
|
+
control = [ Blather::Stanza::X::Field.new(*%w[text-single var label1]),
|
100
|
+
Blather::Stanza::X::Field.new(*%w[text-single var1 label1])]
|
101
|
+
|
102
|
+
di = Blather::Stanza::X.new nil, control
|
103
|
+
di.fields.size.must_equal 2
|
104
|
+
di.fields.each { |f| control.include?(f).must_equal true }
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'takes a mix of hashes and field objects as fields' do
|
108
|
+
fields = [
|
109
|
+
{:label => 'label', :type => 'text-single', :var => 'var'},
|
110
|
+
Blather::Stanza::X::Field.new(*%w[text-single var1 label1]),
|
111
|
+
]
|
112
|
+
|
113
|
+
control = [ Blather::Stanza::X::Field.new(*%w[text-single var label]),
|
114
|
+
Blather::Stanza::X::Field.new(*%w[text-single var1 label1])]
|
115
|
+
|
116
|
+
di = Blather::Stanza::X.new nil, fields
|
117
|
+
di.fields.size.must_equal 2
|
118
|
+
di.fields.each { |f| control.include?(f).must_equal true }
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'allows adding of fields' do
|
122
|
+
di = Blather::Stanza::X.new nil
|
123
|
+
di.fields.size.must_equal 0
|
124
|
+
di.add_fields [{:label => 'label', :type => 'text-single', :var => 'var'}]
|
125
|
+
di.fields.size.must_equal 1
|
126
|
+
di.add_fields [Blather::Stanza::X::Field.new(*%w[text-single var1 label1])]
|
127
|
+
di.fields.size.must_equal 2
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
describe Blather::Stanza::X::Field do
|
133
|
+
it 'will auto-inherit nodes' do
|
134
|
+
n = parse_stanza "<field type='text-single' var='music' label='Music from the time of Shakespeare' />"
|
135
|
+
i = Blather::Stanza::X::Field.new n.root
|
136
|
+
i.type.must_equal 'text-single'
|
137
|
+
i.var.must_equal 'music'
|
138
|
+
i.label.must_equal 'Music from the time of Shakespeare'
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'has a type attribute' do
|
142
|
+
n = Blather::Stanza::X::Field.new 'text-single'
|
143
|
+
n.type.must_equal 'text-single'
|
144
|
+
n.type = 'hidden'
|
145
|
+
n.type.must_equal 'hidden'
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'has a var attribute' do
|
149
|
+
n = Blather::Stanza::X::Field.new 'text-single', 'name'
|
150
|
+
n.var.must_equal 'name'
|
151
|
+
n.var = 'email'
|
152
|
+
n.var.must_equal 'email'
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'has a label attribute' do
|
156
|
+
n = Blather::Stanza::X::Field.new 'text-single', 'subject', 'Music from the time of Shakespeare'
|
157
|
+
n.label.must_equal 'Music from the time of Shakespeare'
|
158
|
+
n.label = 'Books by and about Shakespeare'
|
159
|
+
n.label.must_equal 'Books by and about Shakespeare'
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'has a desc attribute' do
|
163
|
+
n = Blather::Stanza::X::Field.new 'text-single', 'subject', 'Music from the time of Shakespeare'
|
164
|
+
n.desc.must_equal nil
|
165
|
+
n.desc = 'Books by and about Shakespeare'
|
166
|
+
n.desc.must_equal 'Books by and about Shakespeare'
|
167
|
+
n.desc = 'goodbye'
|
168
|
+
n.desc.must_equal 'goodbye'
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'has a required? attribute' do
|
172
|
+
n = Blather::Stanza::X::Field.new 'text-single', 'subject', 'Music from the time of Shakespeare'
|
173
|
+
n.required?.must_equal false
|
174
|
+
n.required!
|
175
|
+
n.required?.must_equal true
|
176
|
+
n.required! false
|
177
|
+
n.required?.must_equal false
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'has a value attribute' do
|
181
|
+
n = Blather::Stanza::X::Field.new 'text-single', 'subject', 'Music from the time of Shakespeare'
|
182
|
+
n.value.must_equal nil
|
183
|
+
n.value = 'book1'
|
184
|
+
n.value.must_equal 'book1'
|
185
|
+
n.value = 'book2'
|
186
|
+
n.value.must_equal 'book2'
|
187
|
+
end
|
188
|
+
|
189
|
+
# Option child elements
|
190
|
+
it 'allows adding of options' do
|
191
|
+
di = Blather::Stanza::X::Field.new nil
|
192
|
+
di.options.size.must_equal 0
|
193
|
+
di.add_options [{:label => 'Person', :value => 'person'}]
|
194
|
+
di.options.size.must_equal 1
|
195
|
+
di.add_options [Blather::Stanza::X::Field::Option.new(*%w[person1 Person1])]
|
196
|
+
di.options.size.must_equal 2
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'raises an error when compared against a non X::Field' do
|
200
|
+
a = Blather::Stanza::X::Field.new('hidden', 'secret_message')
|
201
|
+
lambda { a == 'test' }.must_raise RuntimeError
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'can determine equality' do
|
205
|
+
a = Blather::Stanza::X::Field.new('text-single', 'subject')
|
206
|
+
a.must_equal Blather::Stanza::X::Field.new('text-single', 'subject')
|
207
|
+
a.wont_equal Blather::Stanza::X::Field.new('text-single', 'subject1')
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe Blather::Stanza::X::Field::Option do
|
212
|
+
|
213
|
+
it 'has a value attribute' do
|
214
|
+
n = Blather::Stanza::X::Field::Option.new 'person1', 'Person 1'
|
215
|
+
n.value.must_equal 'person1'
|
216
|
+
n.value = 'book1'
|
217
|
+
n.value.must_equal 'book1'
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'has a label attribute' do
|
221
|
+
n = Blather::Stanza::X::Field::Option.new 'person1', 'Person 1'
|
222
|
+
n.label.must_equal 'Person 1'
|
223
|
+
n.label = 'Book 1'
|
224
|
+
n.label.must_equal 'Book 1'
|
225
|
+
n.label = 'Book 2'
|
226
|
+
n.label.must_equal 'Book 2'
|
227
|
+
end
|
228
|
+
end
|
@@ -45,7 +45,7 @@ describe Blather::Stream::Client do
|
|
45
45
|
|
46
46
|
it 'attempts to find the SRV record if a host is not provided' do
|
47
47
|
dns = mock(:sort! => nil, :empty? => false)
|
48
|
-
dns.expects(:
|
48
|
+
dns.expects(:detect).yields(mock({
|
49
49
|
:target => 'd',
|
50
50
|
:port => 5222
|
51
51
|
}))
|
@@ -135,10 +135,11 @@ describe Blather::XMPPNode do
|
|
135
135
|
c.namespace = 'foo:bar'
|
136
136
|
n << c
|
137
137
|
|
138
|
-
n.find(:bar).size.must_equal 2
|
139
|
-
n.remove_child 'bar', 'foo:bar'
|
140
138
|
n.find(:bar).size.must_equal 1
|
141
|
-
n.find(:bar).
|
139
|
+
n.find('//xmlns:bar', :xmlns => 'foo:bar').size.must_equal 1
|
140
|
+
n.remove_child '//xmlns:bar', :xmlns => 'foo:bar'
|
141
|
+
n.find(:bar).size.must_equal 1
|
142
|
+
n.find('//xmlns:bar', :xmlns => 'foo:bar').size.must_equal 0
|
142
143
|
end
|
143
144
|
|
144
145
|
it 'will remove all child elements' do
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blather
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
- 10
|
10
|
+
version: 0.4.10
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Jeff Smick
|
@@ -9,29 +15,41 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2010-
|
18
|
+
date: 2010-07-19 00:00:00 -07:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: eventmachine
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
20
26
|
requirements:
|
21
27
|
- - ">="
|
22
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 35
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 12
|
33
|
+
- 6
|
23
34
|
version: 0.12.6
|
24
|
-
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
25
37
|
- !ruby/object:Gem::Dependency
|
26
38
|
name: nokogiri
|
27
|
-
|
28
|
-
|
29
|
-
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
30
42
|
requirements:
|
31
43
|
- - ">="
|
32
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 7
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 4
|
49
|
+
- 0
|
33
50
|
version: 1.4.0
|
34
|
-
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
35
53
|
description: An XMPP DSL for Ruby written on top of EventMachine and Nokogiri
|
36
54
|
email: sprsquish@gmail.com
|
37
55
|
executables: []
|
@@ -69,6 +87,7 @@ files:
|
|
69
87
|
- lib/blather/stanza/disco/disco_info.rb
|
70
88
|
- lib/blather/stanza/disco/disco_items.rb
|
71
89
|
- lib/blather/stanza/iq.rb
|
90
|
+
- lib/blather/stanza/iq/command.rb
|
72
91
|
- lib/blather/stanza/iq/query.rb
|
73
92
|
- lib/blather/stanza/iq/roster.rb
|
74
93
|
- lib/blather/stanza/message.rb
|
@@ -90,6 +109,7 @@ files:
|
|
90
109
|
- lib/blather/stanza/pubsub_owner.rb
|
91
110
|
- lib/blather/stanza/pubsub_owner/delete.rb
|
92
111
|
- lib/blather/stanza/pubsub_owner/purge.rb
|
112
|
+
- lib/blather/stanza/x.rb
|
93
113
|
- lib/blather/stream.rb
|
94
114
|
- lib/blather/stream/client.rb
|
95
115
|
- lib/blather/stream/component.rb
|
@@ -102,6 +122,49 @@ files:
|
|
102
122
|
- lib/blather/xmpp_node.rb
|
103
123
|
- LICENSE
|
104
124
|
- README.md
|
125
|
+
- spec/blather/client/client_spec.rb
|
126
|
+
- spec/blather/client/dsl/pubsub_spec.rb
|
127
|
+
- spec/blather/client/dsl_spec.rb
|
128
|
+
- spec/blather/core_ext/nokogiri_spec.rb
|
129
|
+
- spec/blather/errors/sasl_error_spec.rb
|
130
|
+
- spec/blather/errors/stanza_error_spec.rb
|
131
|
+
- spec/blather/errors/stream_error_spec.rb
|
132
|
+
- spec/blather/errors_spec.rb
|
133
|
+
- spec/blather/jid_spec.rb
|
134
|
+
- spec/blather/roster_item_spec.rb
|
135
|
+
- spec/blather/roster_spec.rb
|
136
|
+
- spec/blather/stanza/discos/disco_info_spec.rb
|
137
|
+
- spec/blather/stanza/discos/disco_items_spec.rb
|
138
|
+
- spec/blather/stanza/iq/command_spec.rb
|
139
|
+
- spec/blather/stanza/iq/query_spec.rb
|
140
|
+
- spec/blather/stanza/iq/roster_spec.rb
|
141
|
+
- spec/blather/stanza/iq_spec.rb
|
142
|
+
- spec/blather/stanza/message_spec.rb
|
143
|
+
- spec/blather/stanza/presence/status_spec.rb
|
144
|
+
- spec/blather/stanza/presence/subscription_spec.rb
|
145
|
+
- spec/blather/stanza/presence_spec.rb
|
146
|
+
- spec/blather/stanza/pubsub/affiliations_spec.rb
|
147
|
+
- spec/blather/stanza/pubsub/create_spec.rb
|
148
|
+
- spec/blather/stanza/pubsub/event_spec.rb
|
149
|
+
- spec/blather/stanza/pubsub/items_spec.rb
|
150
|
+
- spec/blather/stanza/pubsub/publish_spec.rb
|
151
|
+
- spec/blather/stanza/pubsub/retract_spec.rb
|
152
|
+
- spec/blather/stanza/pubsub/subscribe_spec.rb
|
153
|
+
- spec/blather/stanza/pubsub/subscription_spec.rb
|
154
|
+
- spec/blather/stanza/pubsub/subscriptions_spec.rb
|
155
|
+
- spec/blather/stanza/pubsub/unsubscribe_spec.rb
|
156
|
+
- spec/blather/stanza/pubsub_owner/delete_spec.rb
|
157
|
+
- spec/blather/stanza/pubsub_owner/purge_spec.rb
|
158
|
+
- spec/blather/stanza/pubsub_owner_spec.rb
|
159
|
+
- spec/blather/stanza/pubsub_spec.rb
|
160
|
+
- spec/blather/stanza/x_spec.rb
|
161
|
+
- spec/blather/stanza_spec.rb
|
162
|
+
- spec/blather/stream/client_spec.rb
|
163
|
+
- spec/blather/stream/component_spec.rb
|
164
|
+
- spec/blather/stream/parser_spec.rb
|
165
|
+
- spec/blather/xmpp_node_spec.rb
|
166
|
+
- spec/fixtures/pubsub.rb
|
167
|
+
- spec/spec_helper.rb
|
105
168
|
has_rdoc: true
|
106
169
|
homepage: http://github.com/sprsquish/blather
|
107
170
|
licenses: []
|
@@ -112,21 +175,27 @@ rdoc_options:
|
|
112
175
|
require_paths:
|
113
176
|
- lib
|
114
177
|
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
none: false
|
115
179
|
requirements:
|
116
180
|
- - ">="
|
117
181
|
- !ruby/object:Gem::Version
|
182
|
+
hash: 3
|
183
|
+
segments:
|
184
|
+
- 0
|
118
185
|
version: "0"
|
119
|
-
version:
|
120
186
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
121
188
|
requirements:
|
122
189
|
- - ">="
|
123
190
|
- !ruby/object:Gem::Version
|
191
|
+
hash: 3
|
192
|
+
segments:
|
193
|
+
- 0
|
124
194
|
version: "0"
|
125
|
-
version:
|
126
195
|
requirements: []
|
127
196
|
|
128
197
|
rubyforge_project: squishtech
|
129
|
-
rubygems_version: 1.3.
|
198
|
+
rubygems_version: 1.3.7
|
130
199
|
signing_key:
|
131
200
|
specification_version: 3
|
132
201
|
summary: Simpler XMPP built for speed
|
@@ -144,6 +213,7 @@ test_files:
|
|
144
213
|
- spec/blather/roster_spec.rb
|
145
214
|
- spec/blather/stanza/discos/disco_info_spec.rb
|
146
215
|
- spec/blather/stanza/discos/disco_items_spec.rb
|
216
|
+
- spec/blather/stanza/iq/command_spec.rb
|
147
217
|
- spec/blather/stanza/iq/query_spec.rb
|
148
218
|
- spec/blather/stanza/iq/roster_spec.rb
|
149
219
|
- spec/blather/stanza/iq_spec.rb
|
@@ -165,6 +235,7 @@ test_files:
|
|
165
235
|
- spec/blather/stanza/pubsub_owner/purge_spec.rb
|
166
236
|
- spec/blather/stanza/pubsub_owner_spec.rb
|
167
237
|
- spec/blather/stanza/pubsub_spec.rb
|
238
|
+
- spec/blather/stanza/x_spec.rb
|
168
239
|
- spec/blather/stanza_spec.rb
|
169
240
|
- spec/blather/stream/client_spec.rb
|
170
241
|
- spec/blather/stream/component_spec.rb
|