blather 0.4.8 → 0.4.10
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 +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
|