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.
Files changed (89) hide show
  1. data/.autotest +3 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE.txt +22 -0
  4. data/Rakefile +46 -0
  5. data/VERSION +1 -0
  6. data/cancer.gemspec +156 -0
  7. data/documentation/STREAM_INITIATION.markdown +18 -0
  8. data/examples/example.rb +80 -0
  9. data/examples/example_2.rb +20 -0
  10. data/examples/example_em.rb +11 -0
  11. data/examples/example_em_helper.rb +23 -0
  12. data/examples/example_im_roster.rb +26 -0
  13. data/examples/example_xep_0004.rb +124 -0
  14. data/examples/example_xep_0047.rb +35 -0
  15. data/examples/example_xep_0050.rb +78 -0
  16. data/examples/example_xep_0065.rb +66 -0
  17. data/examples/example_xep_0096.dup.rb +40 -0
  18. data/examples/example_xep_0096_xep_0047.rb +42 -0
  19. data/examples/example_xep_0096_xep_0065.rb +40 -0
  20. data/lib/cancer.rb +122 -0
  21. data/lib/cancer/adapter.rb +33 -0
  22. data/lib/cancer/adapters/em.rb +88 -0
  23. data/lib/cancer/adapters/socket.rb +122 -0
  24. data/lib/cancer/builder.rb +28 -0
  25. data/lib/cancer/controller.rb +32 -0
  26. data/lib/cancer/dependencies.rb +187 -0
  27. data/lib/cancer/events/abstract_event.rb +10 -0
  28. data/lib/cancer/events/base_matchers.rb +63 -0
  29. data/lib/cancer/events/binary_matchers.rb +30 -0
  30. data/lib/cancer/events/eventable.rb +92 -0
  31. data/lib/cancer/events/exception_events.rb +28 -0
  32. data/lib/cancer/events/handler.rb +33 -0
  33. data/lib/cancer/events/manager.rb +130 -0
  34. data/lib/cancer/events/named_events.rb +25 -0
  35. data/lib/cancer/events/proc_matcher.rb +16 -0
  36. data/lib/cancer/events/xml_events.rb +44 -0
  37. data/lib/cancer/exceptions.rb +53 -0
  38. data/lib/cancer/jid.rb +215 -0
  39. data/lib/cancer/lock.rb +32 -0
  40. data/lib/cancer/spec.rb +35 -0
  41. data/lib/cancer/spec/error.rb +18 -0
  42. data/lib/cancer/spec/extentions.rb +79 -0
  43. data/lib/cancer/spec/matcher.rb +30 -0
  44. data/lib/cancer/spec/mock_adapter.rb +139 -0
  45. data/lib/cancer/spec/mock_stream.rb +15 -0
  46. data/lib/cancer/spec/transcript.rb +107 -0
  47. data/lib/cancer/stream.rb +182 -0
  48. data/lib/cancer/stream/builder.rb +20 -0
  49. data/lib/cancer/stream/controller.rb +36 -0
  50. data/lib/cancer/stream/event_helper.rb +12 -0
  51. data/lib/cancer/stream/xep.rb +52 -0
  52. data/lib/cancer/stream_parser.rb +144 -0
  53. data/lib/cancer/support.rb +27 -0
  54. data/lib/cancer/support/hash.rb +32 -0
  55. data/lib/cancer/support/string.rb +22 -0
  56. data/lib/cancer/synchronized_stanza.rb +79 -0
  57. data/lib/cancer/thread_pool.rb +118 -0
  58. data/lib/cancer/xep.rb +43 -0
  59. data/lib/cancer/xeps/core.rb +109 -0
  60. data/lib/cancer/xeps/core/bind.rb +33 -0
  61. data/lib/cancer/xeps/core/sasl.rb +113 -0
  62. data/lib/cancer/xeps/core/session.rb +22 -0
  63. data/lib/cancer/xeps/core/stream.rb +18 -0
  64. data/lib/cancer/xeps/core/terminator.rb +21 -0
  65. data/lib/cancer/xeps/core/tls.rb +34 -0
  66. data/lib/cancer/xeps/im.rb +323 -0
  67. data/lib/cancer/xeps/xep_0004_x_data.rb +692 -0
  68. data/lib/cancer/xeps/xep_0020_feature_neg.rb +35 -0
  69. data/lib/cancer/xeps/xep_0030_disco.rb +167 -0
  70. data/lib/cancer/xeps/xep_0047_ibb.rb +322 -0
  71. data/lib/cancer/xeps/xep_0050_commands.rb +256 -0
  72. data/lib/cancer/xeps/xep_0065_bytestreams.rb +306 -0
  73. data/lib/cancer/xeps/xep_0066_oob.rb +69 -0
  74. data/lib/cancer/xeps/xep_0095_si.rb +211 -0
  75. data/lib/cancer/xeps/xep_0096_si_filetransfer.rb +173 -0
  76. data/lib/cancer/xeps/xep_0114_component.rb +73 -0
  77. data/lib/cancer/xeps/xep_0115_caps.rb +180 -0
  78. data/lib/cancer/xeps/xep_0138_compress.rb +134 -0
  79. data/lib/cancer/xeps/xep_0144_rosterx.rb +250 -0
  80. data/lib/cancer/xeps/xep_0184_receipts.rb +40 -0
  81. data/lib/cancer/xeps/xep_0199_ping.rb +41 -0
  82. data/spec/spec.opts +2 -0
  83. data/spec/spec_helper.rb +14 -0
  84. data/spec/stream/stanza_errors_spec.rb +47 -0
  85. data/spec/stream/stream_errors_spec.rb +38 -0
  86. data/spec/stream/stream_initialization_spec.rb +160 -0
  87. data/spec/xep_0050/local_spec.rb +165 -0
  88. data/spec/xep_0050/remote_spec.rb +44 -0
  89. 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