cancer 0.1.0.a1
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/.autotest +3 -0
- data/.gitignore +5 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/cancer.gemspec +156 -0
- data/documentation/STREAM_INITIATION.markdown +18 -0
- data/examples/example.rb +80 -0
- data/examples/example_2.rb +20 -0
- data/examples/example_em.rb +11 -0
- data/examples/example_em_helper.rb +23 -0
- data/examples/example_im_roster.rb +26 -0
- data/examples/example_xep_0004.rb +124 -0
- data/examples/example_xep_0047.rb +35 -0
- data/examples/example_xep_0050.rb +78 -0
- data/examples/example_xep_0065.rb +66 -0
- data/examples/example_xep_0096.dup.rb +40 -0
- data/examples/example_xep_0096_xep_0047.rb +42 -0
- data/examples/example_xep_0096_xep_0065.rb +40 -0
- data/lib/cancer.rb +122 -0
- data/lib/cancer/adapter.rb +33 -0
- data/lib/cancer/adapters/em.rb +88 -0
- data/lib/cancer/adapters/socket.rb +122 -0
- data/lib/cancer/builder.rb +28 -0
- data/lib/cancer/controller.rb +32 -0
- data/lib/cancer/dependencies.rb +187 -0
- data/lib/cancer/events/abstract_event.rb +10 -0
- data/lib/cancer/events/base_matchers.rb +63 -0
- data/lib/cancer/events/binary_matchers.rb +30 -0
- data/lib/cancer/events/eventable.rb +92 -0
- data/lib/cancer/events/exception_events.rb +28 -0
- data/lib/cancer/events/handler.rb +33 -0
- data/lib/cancer/events/manager.rb +130 -0
- data/lib/cancer/events/named_events.rb +25 -0
- data/lib/cancer/events/proc_matcher.rb +16 -0
- data/lib/cancer/events/xml_events.rb +44 -0
- data/lib/cancer/exceptions.rb +53 -0
- data/lib/cancer/jid.rb +215 -0
- data/lib/cancer/lock.rb +32 -0
- data/lib/cancer/spec.rb +35 -0
- data/lib/cancer/spec/error.rb +18 -0
- data/lib/cancer/spec/extentions.rb +79 -0
- data/lib/cancer/spec/matcher.rb +30 -0
- data/lib/cancer/spec/mock_adapter.rb +139 -0
- data/lib/cancer/spec/mock_stream.rb +15 -0
- data/lib/cancer/spec/transcript.rb +107 -0
- data/lib/cancer/stream.rb +182 -0
- data/lib/cancer/stream/builder.rb +20 -0
- data/lib/cancer/stream/controller.rb +36 -0
- data/lib/cancer/stream/event_helper.rb +12 -0
- data/lib/cancer/stream/xep.rb +52 -0
- data/lib/cancer/stream_parser.rb +144 -0
- data/lib/cancer/support.rb +27 -0
- data/lib/cancer/support/hash.rb +32 -0
- data/lib/cancer/support/string.rb +22 -0
- data/lib/cancer/synchronized_stanza.rb +79 -0
- data/lib/cancer/thread_pool.rb +118 -0
- data/lib/cancer/xep.rb +43 -0
- data/lib/cancer/xeps/core.rb +109 -0
- data/lib/cancer/xeps/core/bind.rb +33 -0
- data/lib/cancer/xeps/core/sasl.rb +113 -0
- data/lib/cancer/xeps/core/session.rb +22 -0
- data/lib/cancer/xeps/core/stream.rb +18 -0
- data/lib/cancer/xeps/core/terminator.rb +21 -0
- data/lib/cancer/xeps/core/tls.rb +34 -0
- data/lib/cancer/xeps/im.rb +323 -0
- data/lib/cancer/xeps/xep_0004_x_data.rb +692 -0
- data/lib/cancer/xeps/xep_0020_feature_neg.rb +35 -0
- data/lib/cancer/xeps/xep_0030_disco.rb +167 -0
- data/lib/cancer/xeps/xep_0047_ibb.rb +322 -0
- data/lib/cancer/xeps/xep_0050_commands.rb +256 -0
- data/lib/cancer/xeps/xep_0065_bytestreams.rb +306 -0
- data/lib/cancer/xeps/xep_0066_oob.rb +69 -0
- data/lib/cancer/xeps/xep_0095_si.rb +211 -0
- data/lib/cancer/xeps/xep_0096_si_filetransfer.rb +173 -0
- data/lib/cancer/xeps/xep_0114_component.rb +73 -0
- data/lib/cancer/xeps/xep_0115_caps.rb +180 -0
- data/lib/cancer/xeps/xep_0138_compress.rb +134 -0
- data/lib/cancer/xeps/xep_0144_rosterx.rb +250 -0
- data/lib/cancer/xeps/xep_0184_receipts.rb +40 -0
- data/lib/cancer/xeps/xep_0199_ping.rb +41 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/stream/stanza_errors_spec.rb +47 -0
- data/spec/stream/stream_errors_spec.rb +38 -0
- data/spec/stream/stream_initialization_spec.rb +160 -0
- data/spec/xep_0050/local_spec.rb +165 -0
- data/spec/xep_0050/remote_spec.rb +44 -0
- metadata +200 -0
@@ -0,0 +1,692 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
# Data Forms
|
4
|
+
# http://xmpp.org/extensions/xep-0004.html
|
5
|
+
module XEP_0004
|
6
|
+
include Cancer::XEP
|
7
|
+
|
8
|
+
dependency 'core'
|
9
|
+
|
10
|
+
NS = 'jabber:x:data'
|
11
|
+
|
12
|
+
def self.enhance_stream(stream)
|
13
|
+
Cancer::Stream.extend Cancer::XEP_0004::SingletonStreamHelper
|
14
|
+
stream.extend_stream do
|
15
|
+
include Cancer::XEP_0004::StreamHelper
|
16
|
+
end
|
17
|
+
stream.extend_builder do
|
18
|
+
include Cancer::XEP_0004::BuilderHelper
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module StreamHelper
|
23
|
+
|
24
|
+
def define_form(name, &proc)
|
25
|
+
self.defined_forms[name.to_s] = FormDefinition.new(&proc)
|
26
|
+
end
|
27
|
+
|
28
|
+
def form(name)
|
29
|
+
self.defined_forms[name.to_s]
|
30
|
+
end
|
31
|
+
|
32
|
+
def defined_forms
|
33
|
+
@defined_forms ||= self.class.defined_forms.dup
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
module SingletonStreamHelper
|
39
|
+
|
40
|
+
def define_form(name, &proc)
|
41
|
+
self.defined_forms[name.to_s] = FormDefinition.new(&proc)
|
42
|
+
end
|
43
|
+
|
44
|
+
def defined_forms
|
45
|
+
@defined_forms ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
module BuilderHelper
|
51
|
+
|
52
|
+
def x(form=nil, options={}, &proc)
|
53
|
+
options, form = form, nil if Hash === form
|
54
|
+
form ||= Form.new(FormDefinition.new(&proc), options[:type])
|
55
|
+
form.build(self, options)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
module HasFieldValues
|
61
|
+
|
62
|
+
def [](var)
|
63
|
+
@values[var.to_sym]
|
64
|
+
end
|
65
|
+
|
66
|
+
def []=(var, value)
|
67
|
+
set_value(var, value)
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_value(var, value, validate=true)
|
71
|
+
@values[var.to_sym] = value
|
72
|
+
end
|
73
|
+
|
74
|
+
def each(&block)
|
75
|
+
@values.each(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete(var)
|
79
|
+
@values.delete(var.to_sym)
|
80
|
+
end
|
81
|
+
|
82
|
+
def value?(var)
|
83
|
+
@values.key?(var.to_sym)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
module HasDefinition
|
89
|
+
|
90
|
+
def field(value)
|
91
|
+
@definition.field(value)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
module HasFieldDefinitions
|
97
|
+
|
98
|
+
def define(var)
|
99
|
+
field = FieldDefinition.new(var)
|
100
|
+
yield field
|
101
|
+
add_field field
|
102
|
+
field
|
103
|
+
end
|
104
|
+
|
105
|
+
def field(var)
|
106
|
+
@fields_by_name[var.to_sym]
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_field(field)
|
110
|
+
@fields_by_name[field.var] = field unless field.type == :fixed
|
111
|
+
@fields << field
|
112
|
+
field
|
113
|
+
end
|
114
|
+
|
115
|
+
def field?(var)
|
116
|
+
@fields_by_name.key?(var.to_sym)
|
117
|
+
end
|
118
|
+
|
119
|
+
def each_field(&block)
|
120
|
+
@fields.each(&block)
|
121
|
+
end
|
122
|
+
|
123
|
+
def delete_field(var)
|
124
|
+
field = @fields_by_name[var.to_sym] if var.repond_to?(:to_sym)
|
125
|
+
@fields.delete(field)
|
126
|
+
@fields_by_name.delete(field.var)
|
127
|
+
field
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
class Form
|
133
|
+
|
134
|
+
include HasDefinition
|
135
|
+
include HasFieldValues
|
136
|
+
|
137
|
+
attr_reader :definition, :type
|
138
|
+
|
139
|
+
def items
|
140
|
+
@items ||= []
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.load(xml, definition=nil)
|
144
|
+
definition ||= FormDefinition.load(xml)
|
145
|
+
form = new(definition, (xml[:type] || :form).to_sym)
|
146
|
+
|
147
|
+
xml.xpath('x:field', 'x' => NS).each do |x|
|
148
|
+
next if x[:type] == 'fixed'
|
149
|
+
|
150
|
+
field_definition = definition.field(x[:var])
|
151
|
+
form.set_value(x[:var], field_definition.load(x), false)
|
152
|
+
end
|
153
|
+
|
154
|
+
xml.xpath('x:item', 'x' => NS).each do |x|
|
155
|
+
form.items << Item.load(x, definition.reported)
|
156
|
+
end
|
157
|
+
|
158
|
+
form
|
159
|
+
end
|
160
|
+
|
161
|
+
def initialize(definition, type = :form)
|
162
|
+
@definition, @type = definition, type
|
163
|
+
@values = {}
|
164
|
+
end
|
165
|
+
|
166
|
+
def to_xml(options={})
|
167
|
+
Nokogiri::XML::Builder.new do |builder|
|
168
|
+
build(builder, options)
|
169
|
+
end.doc.root.to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
def build(builder, options={})
|
173
|
+
options[:values] = self
|
174
|
+
options[:rows] = @items
|
175
|
+
options[:type] ||= @type
|
176
|
+
self.definition.build(builder, options)
|
177
|
+
end
|
178
|
+
|
179
|
+
def valid?
|
180
|
+
self.definition.each_field do |field|
|
181
|
+
unless (field.var and field.valid?(self[field.var]))
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
end
|
185
|
+
self.items.each { |item| return false unless item.valid? }
|
186
|
+
return true
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
class Item
|
192
|
+
|
193
|
+
include HasDefinition
|
194
|
+
include HasFieldValues
|
195
|
+
|
196
|
+
attr_reader :definition
|
197
|
+
|
198
|
+
def self.load(xml, definition)
|
199
|
+
item = new(definition)
|
200
|
+
|
201
|
+
xml.xpath('x:field', 'x' => NS).each do |x|
|
202
|
+
next if x[:type] == 'fixed'
|
203
|
+
|
204
|
+
field_definition = definition.field(x[:var])
|
205
|
+
item.set_value(x[:var], field_definition.load(x), false)
|
206
|
+
end
|
207
|
+
|
208
|
+
item
|
209
|
+
end
|
210
|
+
|
211
|
+
def initialize(definition)
|
212
|
+
@definition = definition
|
213
|
+
@values = {}
|
214
|
+
end
|
215
|
+
|
216
|
+
def build(builder, options={})
|
217
|
+
builder.item do
|
218
|
+
self.definition.each_field do |f|
|
219
|
+
f.build(builder, options.merge(:values => @values, :in_item => true))
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def valid?
|
225
|
+
self.definition.each_field do |field|
|
226
|
+
unless (field.var and field.valid?(self[field.var]))
|
227
|
+
return false
|
228
|
+
end
|
229
|
+
end
|
230
|
+
return true
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
class FormDefinition
|
236
|
+
|
237
|
+
include HasFieldDefinitions
|
238
|
+
|
239
|
+
attr_accessor :title
|
240
|
+
attr_reader :instructions
|
241
|
+
|
242
|
+
def self.load(xml)
|
243
|
+
new do |d|
|
244
|
+
|
245
|
+
xml.xpath('x:reported', 'x' => NS).each do |x|
|
246
|
+
d.add_reported Reported.load(x)
|
247
|
+
break
|
248
|
+
end
|
249
|
+
|
250
|
+
xml.xpath('x:title', 'x' => NS).each do |x|
|
251
|
+
d.title = x.text.to_s
|
252
|
+
break
|
253
|
+
end
|
254
|
+
|
255
|
+
xml.xpath('x:instructions', 'x' => NS).each do |x|
|
256
|
+
d.instructions << x.text.to_s
|
257
|
+
end
|
258
|
+
|
259
|
+
xml.xpath('x:field', 'x' => NS).each do |x|
|
260
|
+
d.add_field FieldDefinition.load(x)
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def initialize
|
267
|
+
@fields = []
|
268
|
+
@fields_by_name = {}
|
269
|
+
@instructions = []
|
270
|
+
yield(self) if block_given?
|
271
|
+
end
|
272
|
+
|
273
|
+
def reported(&proc)
|
274
|
+
@reported ||= Reported.new(&proc)
|
275
|
+
end
|
276
|
+
|
277
|
+
def load(xml)
|
278
|
+
Form.load(xml, self)
|
279
|
+
end
|
280
|
+
|
281
|
+
def add_reported(reported)
|
282
|
+
@reported = reported
|
283
|
+
end
|
284
|
+
|
285
|
+
def inspect
|
286
|
+
"(form fields:#{@fields.inspect})"
|
287
|
+
end
|
288
|
+
|
289
|
+
def to_xml(options={})
|
290
|
+
Nokogiri::XML::Builder.new do |builder|
|
291
|
+
build(builder, options)
|
292
|
+
end.doc.root.to_s
|
293
|
+
end
|
294
|
+
|
295
|
+
def build(builder, options={})
|
296
|
+
builder.x_(:type => (options[:type] || :form), :xmlns => NS) do
|
297
|
+
if options[:verbose]
|
298
|
+
builder.title(title) if title
|
299
|
+
instructions.each do |instruction|
|
300
|
+
builder.instructions(instruction)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
self.each_field do |f|
|
305
|
+
f.build(builder, options)
|
306
|
+
end
|
307
|
+
|
308
|
+
@reported.build(builder, options.merge(:values => nil)) if @reported
|
309
|
+
|
310
|
+
if options[:rows]
|
311
|
+
options[:rows].each do |r|
|
312
|
+
r.build(builder, options)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
class Reported
|
322
|
+
|
323
|
+
include HasFieldDefinitions
|
324
|
+
|
325
|
+
def self.load(xml)
|
326
|
+
new do |d|
|
327
|
+
|
328
|
+
xml.xpath('x:field', 'x' => NS).each do |x|
|
329
|
+
d.add_field FieldDefinition.load(x)
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def initialize
|
336
|
+
@fields = []
|
337
|
+
@fields_by_name = {}
|
338
|
+
yield(self) if block_given?
|
339
|
+
end
|
340
|
+
|
341
|
+
def build(builder, options={})
|
342
|
+
builder.reported do
|
343
|
+
self.each_field do |field|
|
344
|
+
field.build(builder, options)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
class FieldDefinition
|
352
|
+
|
353
|
+
attr_accessor :var, :type, :label, :description, :required, :options, :value
|
354
|
+
|
355
|
+
def self.load(xml)
|
356
|
+
new(xml[:var]) do |d|
|
357
|
+
|
358
|
+
d.type = (xml[:type] || 'text-single').to_sym
|
359
|
+
d.label = xml[:label].to_s if xml[:label]
|
360
|
+
d.required = !xml.xpath('x:required', 'x' => NS).empty?
|
361
|
+
|
362
|
+
if d.type == :fixed
|
363
|
+
d.value = xml.xpath('x:value/text()', 'x' => NS).to_s
|
364
|
+
end
|
365
|
+
|
366
|
+
d.description = xml.xpath('x:desc/text()', 'x' => NS).to_s
|
367
|
+
d.description = nil if d.description and d.description.empty?
|
368
|
+
|
369
|
+
xml.xpath('x:option', 'x' => NS).each do |x|
|
370
|
+
d.add_option Option.load(x)
|
371
|
+
end
|
372
|
+
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def initialize(var)
|
377
|
+
@var = var.to_sym if var
|
378
|
+
@options = {}
|
379
|
+
@option_values = []
|
380
|
+
@type = :'text-single'
|
381
|
+
@value = nil
|
382
|
+
yield(self) if block_given?
|
383
|
+
end
|
384
|
+
|
385
|
+
def add_option(option, label=nil)
|
386
|
+
option = Option.new(option, label) unless Option === option
|
387
|
+
@options[option.value] = option
|
388
|
+
@option_values << option.value
|
389
|
+
option
|
390
|
+
end
|
391
|
+
|
392
|
+
def option_values
|
393
|
+
@option_values
|
394
|
+
end
|
395
|
+
|
396
|
+
def load(xml)
|
397
|
+
case self.type
|
398
|
+
when :'boolean' then load_boolean(xml)
|
399
|
+
when :'fixed' then load_fixed(xml)
|
400
|
+
when :'hidden' then load_hidden(xml)
|
401
|
+
when :'jid-multi' then load_jid_multi(xml)
|
402
|
+
when :'jid-single' then load_jid_single(xml)
|
403
|
+
when :'list-multi' then load_list_multi(xml)
|
404
|
+
when :'list-single' then load_list_single(xml)
|
405
|
+
when :'text-multi' then load_text_multi(xml)
|
406
|
+
when :'text-private' then load_text_private(xml)
|
407
|
+
when :'text-single' then load_text_single(xml)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def valid?(value)
|
412
|
+
case self.type
|
413
|
+
when :'boolean' then valid_boolean(value)
|
414
|
+
when :'fixed' then valid_fixed(value)
|
415
|
+
when :'hidden' then valid_hidden(value)
|
416
|
+
when :'jid-multi' then valid_jid_multi(value)
|
417
|
+
when :'jid-single' then valid_jid_single(value)
|
418
|
+
when :'list-multi' then valid_list_multi(value)
|
419
|
+
when :'list-single' then valid_list_single(value)
|
420
|
+
when :'text-multi' then valid_text_multi(value)
|
421
|
+
when :'text-private' then valid_text_private(value)
|
422
|
+
when :'text-single' then valid_text_single(value)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
def inspect
|
427
|
+
"(field: #{var} type:#{type} options:#{options.inspect} required:#{required.inspect})"
|
428
|
+
end
|
429
|
+
|
430
|
+
def build(builder, options={})
|
431
|
+
attributes = {}
|
432
|
+
attributes[:var] = var if var
|
433
|
+
unless options[:in_item]
|
434
|
+
attributes[:type] = type if type != :'text-single'
|
435
|
+
attributes[:label] = label if label and options[:verbose]
|
436
|
+
end
|
437
|
+
builder.field(attributes) do
|
438
|
+
if options[:verbose] and !options[:in_item]
|
439
|
+
builder.required if required
|
440
|
+
builder.desc(description) if description
|
441
|
+
self.option_values.each do |key|
|
442
|
+
option = self.options[key]
|
443
|
+
option.build(builder, options)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
if options[:values]
|
447
|
+
build_value(builder, @value, options) if @value
|
448
|
+
if self.type != :fixed
|
449
|
+
value = options[:values][self.var] if self.var
|
450
|
+
build_value(builder, value, options)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
def remove_options!
|
457
|
+
@options = {}
|
458
|
+
@option_values = []
|
459
|
+
end
|
460
|
+
|
461
|
+
private
|
462
|
+
|
463
|
+
def build_value(builder, value, options={})
|
464
|
+
case self.type
|
465
|
+
when :'boolean' then build_boolean(builder, value, options)
|
466
|
+
when :'fixed' then build_fixed(builder, value, options)
|
467
|
+
when :'hidden' then build_hidden(builder, value, options)
|
468
|
+
when :'jid-multi' then build_jid_multi(builder, value, options)
|
469
|
+
when :'jid-single' then build_jid_single(builder, value, options)
|
470
|
+
when :'list-multi' then build_list_multi(builder, value, options)
|
471
|
+
when :'list-single' then build_list_single(builder, value, options)
|
472
|
+
when :'text-multi' then build_text_multi(builder, value, options)
|
473
|
+
when :'text-private' then build_text_private(builder, value, options)
|
474
|
+
when :'text-single' then build_text_single(builder, value, options)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def load_boolean(xml)
|
479
|
+
%w( true 1 ).include? load_first_value(xml)
|
480
|
+
end
|
481
|
+
|
482
|
+
## boolean
|
483
|
+
|
484
|
+
def build_boolean(builder, value, options={})
|
485
|
+
if value
|
486
|
+
builder.value('true')
|
487
|
+
else
|
488
|
+
builder.value('false')
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def valid_boolean(value)
|
493
|
+
true
|
494
|
+
end
|
495
|
+
|
496
|
+
## fixed
|
497
|
+
|
498
|
+
def load_fixed(xml)
|
499
|
+
nil
|
500
|
+
end
|
501
|
+
|
502
|
+
def build_fixed(builder, value, options={})
|
503
|
+
builder.value(value)
|
504
|
+
end
|
505
|
+
|
506
|
+
def valid_fixed(value)
|
507
|
+
true
|
508
|
+
end
|
509
|
+
|
510
|
+
## hidden
|
511
|
+
|
512
|
+
def load_hidden(xml)
|
513
|
+
load_first_value(xml)
|
514
|
+
end
|
515
|
+
|
516
|
+
def build_hidden(builder, value, options={})
|
517
|
+
builder.value(value.to_s) if value
|
518
|
+
end
|
519
|
+
|
520
|
+
def valid_hidden(value)
|
521
|
+
value.respond_to?(:to_s) and (required ? value : true)
|
522
|
+
end
|
523
|
+
|
524
|
+
## jid-multi
|
525
|
+
|
526
|
+
def load_jid_multi(xml)
|
527
|
+
load_all_values(xml).collect { |v| v.to_jid }
|
528
|
+
end
|
529
|
+
|
530
|
+
def build_jid_multi(builder, values, options={})
|
531
|
+
values.each do |value|
|
532
|
+
builder.value(value.to_s)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
def valid_jid_multi(values)
|
537
|
+
valid_multi(values) do |value|
|
538
|
+
valid_jid_single(value)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
## jid-single
|
543
|
+
|
544
|
+
def load_jid_single(xml)
|
545
|
+
value = load_first_value(xml)
|
546
|
+
value = value.to_jid if value
|
547
|
+
value
|
548
|
+
end
|
549
|
+
|
550
|
+
def build_jid_single(builder, value, options={})
|
551
|
+
builder.value(value.to_s) if value
|
552
|
+
end
|
553
|
+
|
554
|
+
def valid_jid_single(value)
|
555
|
+
value.respond_to?(:to_jid) and (required ? value : true) and (!!value.to_jid rescue false)
|
556
|
+
end
|
557
|
+
|
558
|
+
## list-multi
|
559
|
+
|
560
|
+
def load_list_multi(xml)
|
561
|
+
option_values = options.keys
|
562
|
+
load_all_values(xml) & option_values
|
563
|
+
end
|
564
|
+
|
565
|
+
def build_list_multi(builder, values, options={})
|
566
|
+
values.each do |value|
|
567
|
+
builder.value(value)
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
def valid_list_multi(values)
|
572
|
+
valid_multi(values) do |value|
|
573
|
+
valid_list_single(value)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
## list-single
|
578
|
+
|
579
|
+
def load_list_single(xml)
|
580
|
+
value = load_first_value(xml)
|
581
|
+
options.key?(value) ? value : nil
|
582
|
+
end
|
583
|
+
|
584
|
+
def build_list_single(builder, value, options={})
|
585
|
+
builder.value(value) if value
|
586
|
+
end
|
587
|
+
|
588
|
+
def valid_list_single(value)
|
589
|
+
value.respond_to?(:to_s) and (required ? value : true) and (value ? options.keys.include?(value.to_s) : true)
|
590
|
+
end
|
591
|
+
|
592
|
+
## text-multi
|
593
|
+
|
594
|
+
def load_text_multi(xml)
|
595
|
+
load_all_values(xml)
|
596
|
+
end
|
597
|
+
|
598
|
+
def build_text_multi(builder, values, options={})
|
599
|
+
values.each do |value|
|
600
|
+
builder.value(value)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
def valid_text_multi(values)
|
605
|
+
valid_multi(values) do |value|
|
606
|
+
valid_text_single(value)
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
## text-private
|
611
|
+
|
612
|
+
def load_text_private(xml)
|
613
|
+
load_first_value(xml)
|
614
|
+
end
|
615
|
+
|
616
|
+
def build_text_private(builder, value, options={})
|
617
|
+
builder.value(value) if value
|
618
|
+
end
|
619
|
+
|
620
|
+
def valid_text_private(value)
|
621
|
+
value.respond_to?(:to_s) and (required ? value : true)
|
622
|
+
end
|
623
|
+
|
624
|
+
## text-single
|
625
|
+
|
626
|
+
def load_text_single(xml)
|
627
|
+
load_first_value(xml)
|
628
|
+
end
|
629
|
+
|
630
|
+
def build_text_single(builder, value, options={})
|
631
|
+
builder.value(value) if value
|
632
|
+
end
|
633
|
+
|
634
|
+
def valid_text_single(value)
|
635
|
+
value.respond_to?(:to_s) and (required ? value : true)
|
636
|
+
end
|
637
|
+
|
638
|
+
|
639
|
+
def load_all_values(xml)
|
640
|
+
xml.xpath('x:value/text()', 'x' => NS).to_a.collect { |t| t.to_s }
|
641
|
+
end
|
642
|
+
|
643
|
+
def load_first_value(xml)
|
644
|
+
v = xml.xpath('x:value/text()', 'x' => NS).first
|
645
|
+
v ? v.to_s : v
|
646
|
+
end
|
647
|
+
|
648
|
+
def valid_multi(values, &proc)
|
649
|
+
(required ? values : true) and (values ? values.respond_to?(:to_a) : true) and (values ? values.to_a.all?(&proc) : true)
|
650
|
+
end
|
651
|
+
|
652
|
+
end
|
653
|
+
|
654
|
+
class Option
|
655
|
+
|
656
|
+
attr_accessor :label, :value
|
657
|
+
|
658
|
+
def self.load(xml)
|
659
|
+
new(nil) do |d|
|
660
|
+
|
661
|
+
d.label = xml[:label].to_s if xml[:label]
|
662
|
+
d.value = xml.xpath('x:value/text()', 'x' => NS).to_s
|
663
|
+
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
def initialize(value, label=nil)
|
668
|
+
@value, @label = value, label
|
669
|
+
yield(self) if block_given?
|
670
|
+
end
|
671
|
+
|
672
|
+
def inspect
|
673
|
+
if label
|
674
|
+
"(option: #{value.inspect} with label #{label.inspect})"
|
675
|
+
else
|
676
|
+
"(option: #{value.inspect})"
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
def build(builder, options={})
|
681
|
+
if options[:verbose]
|
682
|
+
builder.option(label ? { :label => label} : {}) do
|
683
|
+
builder.value(value)
|
684
|
+
end
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
end
|
689
|
+
|
690
|
+
|
691
|
+
end
|
692
|
+
end
|