jubjub 0.0.6 → 0.0.7

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.mdown CHANGED
@@ -181,11 +181,57 @@ Examples
181
181
  u.pubsub.destroy('old_node', 'pubsub.jabber.ru', 'new_new')
182
182
  => true
183
183
 
184
+ Dealing with errors
185
+ ===================
186
+
187
+ Every returned object from every action is really a Jubjub::Response::Proxy, which delegates to the Jubjub::Response and if the method doesn't exist there to the actual result. As Jubjub::Response only has a few methods it will usually just behaves like the actual result (say a Jubjub::Pubsub::Item). However, you can check what has really happened like so:
188
+
189
+ # Attempt to destroy a node that does not exist
190
+ u.pubsub.destroy('made up')
191
+ => false
192
+
193
+ # False tells us destroy didn't succeed in this case, but it's not terribly helpful
194
+ # So we can use methods provided by the Jubjub::Response as well
195
+
196
+ response = u.pubsub.destroy('made up')
197
+ response.success?
198
+ => false
199
+
200
+ response.stanza.to_s
201
+ => "<?xml version=\"1.0\"?>\n<iq type=\"error\" id=\"blather0011\" from=\"pubsub.theo-template.local\" to=\"theozaurus@theo-template.local/42607076141308656900975361\" lang=\"en\">\n <pubsub xmlns=\"http://jabber.org/protocol/pubsub#owner\">\n <delete node=\"made up\"/>\n </pubsub>\n<error code=\"404\" type=\"cancel\"><item-not-found xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/></error></iq>\n"
202
+
203
+ # We can get the real result with no proxy sat in front
204
+ response.result
205
+ => false
206
+ response.result.success?
207
+ NoMethodError: undefined method `success?' for false:FalseClass
208
+ from (irb):14
209
+
210
+ # We can also get an enhanced error object, Jubjub::Response::Error
211
+ response.error
212
+ => #<Jubjub::Response::Error:0x101ef51f8 @stanza=#<Nokogiri::XML::Element:0x80f7a7bc name="error" attributes=[#<Nokogiri::XML::Attr:0x80f7a654 name="code" value="404">, #<Nokogiri::XML::Attr:0x80f7a640 name="type" value="cancel">] children=[#<Nokogiri::XML::Element:0x80f7a03c name="item-not-found" namespace=#<Nokogiri::XML::Namespace:0x80f79fb0 href="urn:ietf:params:xml:ns:xmpp-stanzas">>]>>
213
+
214
+ response.error.stanza.to_s
215
+ => "<error code=\"404\" type=\"cancel\">\n <item-not-found xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>\n</error>"
216
+
217
+ response.error.condition
218
+ => 'item-not-found'
219
+
220
+ response.error.type
221
+ => 'cancel'
222
+
223
+ # If there is any error text this can also be extracted
224
+ response.error.text
225
+ => 'Item not found'
226
+
227
+
228
+ The error `type` and error `condition` are the important factors. The `type` is defined in the [RFC 6121](http://xmpp.org/rfcs/rfc6120.html#stanzas-error-syntax), and helps decide whether the action should be attempted again. The `condition` provides a bit more detail, and will always be part of the [urn:ietf:params:xml:ns:xmpp-stanzas namespace](http://xmpp.org/schemas/stanzaerror.xsd).
229
+
184
230
  TODO
185
231
  ====
186
232
 
187
- - Error handling
188
233
  - MUC user role and affiliation control
234
+ - Better exception handling
189
235
  - Service discovery
190
236
  - Operations that are not IQ based, such as rosters and two way messaging
191
237
  - Other backends (for servers that are evented)
@@ -46,14 +46,10 @@ module Jubjub
46
46
 
47
47
  presence full_jid
48
48
 
49
- success = write(
50
- # Open room
51
- request.to_xml
52
- ).xpath(
53
- # Check for valid response
54
- '//iq[@type="result"]'
55
- ).any?
56
- Jubjub::Muc.new room_jid, nil, @connection if success
49
+ Jubjub::Response.new( write request.to_xml ){|stanza|
50
+ success = stanza.xpath( '/iq[@type="result"]' ).any?
51
+ Jubjub::Muc.new room_jid, nil, @connection if success
52
+ }.proxy_result
57
53
  end
58
54
 
59
55
  # http://xmpp.org/extensions/xep-0045.html#createroom-reserved
@@ -212,16 +208,14 @@ module Jubjub
212
208
  end
213
209
 
214
210
  presence full_jid
215
- response = write(
216
- # Request room configuration
217
- request.to_xml
218
- ).xpath(
219
- # Get fields
220
- "//iq[@type='result']/muc_owner:query/x_data:x[@type='form']",
221
- namespaces
222
- )
223
211
 
224
- Jubjub::Muc::Configuration.new response if response
212
+ Jubjub::Response.new( write request.to_xml ){|stanza|
213
+ config = stanza.xpath(
214
+ "/iq[@type='result']/muc_owner:query/x_data:x[@type='form']",
215
+ namespaces
216
+ )
217
+ Jubjub::Muc::Configuration.new config if config
218
+ }.proxy_result
225
219
  end
226
220
 
227
221
  # http://xmpp.org/extensions/xep-0045.html#destroyroom
@@ -250,13 +244,9 @@ module Jubjub
250
244
  }
251
245
  end
252
246
 
253
- write(
254
- # Generate stanza
255
- request.to_xml
256
- ).xpath(
257
- # Check for valid response
258
- '//iq[@type="result"]'
259
- ).any?
247
+ Jubjub::Response.new( write request.to_xml ){|stanza|
248
+ stanza.xpath( '/iq[@type="result"]' ).any?
249
+ }.proxy_result
260
250
  end
261
251
 
262
252
  # http://xmpp.org/extensions/xep-0045.html#disco-rooms
@@ -289,18 +279,16 @@ module Jubjub
289
279
  xml.query_('xmlns' => 'http://jabber.org/protocol/disco#items')
290
280
  }
291
281
  end
292
-
293
- write(
294
- # Generate stanza
295
- request.to_xml
296
- ).xpath(
297
- # Pull out required parts
298
- '//iq[@type="result"]/disco_items:query/disco_items:item',
299
- namespaces
300
- ).map{|item|
301
- # Convert to Jubjub object
302
- Jubjub::Muc.new item.attr('jid'), item.attr('name'), @connection
303
- }
282
+
283
+ Jubjub::Response.new( write request.to_xml ){|stanza|
284
+ stanza.xpath(
285
+ '/iq[@type="result"]/disco_items:query/disco_items:item',
286
+ namespaces
287
+ ).map{|item|
288
+ # Convert to Jubjub object
289
+ Jubjub::Muc.new item.attr('jid'), item.attr('name'), @connection
290
+ }
291
+ }.proxy_result
304
292
  end
305
293
 
306
294
  # http://xmpp.org/extensions/xep-0045.html#exit
@@ -40,19 +40,17 @@ module Jubjub
40
40
  xml.query_('xmlns' => namespaces['disco_items'])
41
41
  }
42
42
  end
43
-
44
- write(
45
- # Generate stanza
46
- request.to_xml
47
- ).xpath(
48
- # Pull out required parts
49
- '//iq[@type="result"]/disco_items:query/disco_items:item',
50
- namespaces
51
- ).map{|item|
52
- jid = item.attr('jid')
53
- node = item.attr('node')
54
- Jubjub::Pubsub.new jid, node, @connection
55
- }
43
+
44
+ Jubjub::Response.new( write request.to_xml ){|stanza|
45
+ stanza.xpath(
46
+ '/iq[@type="result"]/disco_items:query/disco_items:item',
47
+ namespaces
48
+ ).map{|item|
49
+ jid = item.attr('jid')
50
+ node = item.attr('node')
51
+ Jubjub::Pubsub.new jid, node, @connection
52
+ }
53
+ }.proxy_result
56
54
  end
57
55
 
58
56
  # http://xmpp.org/extensions/xep-0060.html#owner-create-and-configure
@@ -93,14 +91,13 @@ module Jubjub
93
91
  }
94
92
  end
95
93
 
96
- success = write(
97
- # Generate stanza
98
- request.to_xml
99
- ).xpath(
100
- # Pull out required parts
101
- '//iq[@type="result"]'
102
- ).any?
103
- Jubjub::Pubsub.new jid, node, @connection if success
94
+ Jubjub::Response.new( write request.to_xml ){|stanza|
95
+ success = stanza.xpath(
96
+ # Pull out required parts
97
+ '/iq[@type="result"]'
98
+ ).any?
99
+ Jubjub::Pubsub.new jid, node, @connection if success
100
+ }.proxy_result
104
101
  end
105
102
 
106
103
  # http://xmpp.org/extensions/xep-0060.html#owner-configure
@@ -144,16 +141,14 @@ module Jubjub
144
141
  }
145
142
  end
146
143
 
147
- response = write(
148
- # Request default node configuration
149
- request.to_xml
150
- ).xpath(
151
- # Get fields
152
- "//iq[@type='result']/pubsub_owner:pubsub/pubsub_owner:default/x_data:x[@type='form']",
153
- namespaces
154
- )
155
-
156
- Jubjub::Pubsub::Configuration.new response if response
144
+ Jubjub::Response.new( write request.to_xml ){|stanza|
145
+ config = stanza.xpath(
146
+ # Pull out required parts
147
+ "/iq[@type='result']/pubsub_owner:pubsub/pubsub_owner:default/x_data:x[@type='form']",
148
+ namespaces
149
+ )
150
+ Jubjub::Pubsub::Configuration.new config if config
151
+ }.proxy_result
157
152
  end
158
153
 
159
154
  # http://xmpp.org/extensions/xep-0060.html#owner-delete
@@ -183,13 +178,9 @@ module Jubjub
183
178
  }
184
179
  end
185
180
 
186
- success = write(
187
- # Generate stanza
188
- request.to_xml
189
- ).xpath(
190
- # Pull out required parts
191
- '//iq[@type="result"]'
192
- ).any?
181
+ Jubjub::Response.new( write request.to_xml ){|stanza|
182
+ stanza.xpath( '/iq[@type="result"]' ).any?
183
+ }.proxy_result
193
184
  end
194
185
 
195
186
  # http://xmpp.org/extensions/xep-0060.html#owner-purge
@@ -215,13 +206,9 @@ module Jubjub
215
206
  }
216
207
  end
217
208
 
218
- success = write(
219
- # Generate stanza
220
- request.to_xml
221
- ).xpath(
222
- # Pull out required parts
223
- '//iq[@type="result"]'
224
- ).any?
209
+ Jubjub::Response.new( write request.to_xml ){|stanza|
210
+ stanza.xpath( '/iq[@type="result"]' ).any?
211
+ }.proxy_result
225
212
  end
226
213
 
227
214
  # http://xmpp.org/extensions/xep-0060.html#subscriber-subscribe
@@ -257,21 +244,19 @@ module Jubjub
257
244
  }
258
245
  }
259
246
  end
260
-
261
- result = write(
262
- # Generate stanza
263
- request.to_xml
264
- ).xpath(
265
- # Pull out required parts
266
- '//iq[@type="result"]/pubsub:pubsub/pubsub:subscription',
267
- namespaces
268
- )
269
- if result.any?
270
- subscriber = Jubjub::Jid.new(result.first.attr('jid'))
271
- subid = result.first.attr('subid')
272
- subscription = result.first.attr('subscription')
273
- Jubjub::Pubsub::Subscription.new jid, node, subscriber, subid, subscription, @connection
274
- end
247
+
248
+ Jubjub::Response.new( write request.to_xml ){|stanza|
249
+ result = stanza.xpath(
250
+ '/iq[@type="result"]/pubsub:pubsub/pubsub:subscription',
251
+ namespaces
252
+ )
253
+ if result.any?
254
+ subscriber = Jubjub::Jid.new(result.first.attr('jid'))
255
+ subid = result.first.attr('subid')
256
+ subscription = result.first.attr('subscription')
257
+ Jubjub::Pubsub::Subscription.new jid, node, subscriber, subid, subscription, @connection
258
+ end
259
+ }.proxy_result
275
260
  end
276
261
 
277
262
  # http://xmpp.org/extensions/xep-0060.html#subscriber-unsubscribe
@@ -303,13 +288,9 @@ module Jubjub
303
288
  }
304
289
  end
305
290
 
306
- write(
307
- # Generate stanza
308
- request.to_xml
309
- ).xpath(
310
- # Pull out required parts
311
- '//iq[@type="result"]'
312
- ).any?
291
+ Jubjub::Response.new( write request.to_xml ){|stanza|
292
+ stanza.xpath( '/iq[@type="result"]' ).any?
293
+ }.proxy_result
313
294
  end
314
295
 
315
296
  # http://xmpp.org/extensions/xep-0060.html#publisher-publish
@@ -357,19 +338,17 @@ module Jubjub
357
338
  }
358
339
  end
359
340
 
360
- result = write(
361
- # Generate stanza
362
- request.to_xml
363
- ).xpath(
364
- # Pull out required parts
365
- '//iq[@type="result"]/pubsub:pubsub/pubsub:publish/pubsub:item',
366
- namespaces
367
- )
368
- if result.any?
369
- item_id = result.first.attr('id')
370
- data = request.doc.xpath("//pubsub:item/*", namespaces).to_s
371
- Jubjub::Pubsub::Item.new jid, node, item_id, data, @connection
372
- end
341
+ Jubjub::Response.new( write request.to_xml ){|stanza|
342
+ result = stanza.xpath(
343
+ '/iq[@type="result"]/pubsub:pubsub/pubsub:publish/pubsub:item',
344
+ namespaces
345
+ )
346
+ if result.any?
347
+ item_id = result.first.attr('id')
348
+ data = request.doc.xpath("//pubsub:item/*", namespaces).to_s
349
+ Jubjub::Pubsub::Item.new jid, node, item_id, data, @connection
350
+ end
351
+ }.proxy_result
373
352
  end
374
353
 
375
354
  # http://xmpp.org/extensions/xep-0060.html#publisher-delete
@@ -400,10 +379,9 @@ module Jubjub
400
379
  }
401
380
  end
402
381
 
403
- write(
404
- # Generate stanza
405
- request.to_xml
406
- ).xpath('//iq[@type="result"]').any?
382
+ Jubjub::Response.new( write request.to_xml ){|stanza|
383
+ stanza.xpath( '/iq[@type="result"]' ).any?
384
+ }.proxy_result
407
385
  end
408
386
 
409
387
  # http://xmpp.org/extensions/xep-0060.html#subscriber-retrieve
@@ -449,19 +427,17 @@ module Jubjub
449
427
  }
450
428
  }
451
429
  end
452
-
453
- write(
454
- # Generate stanza
455
- request.to_xml
456
- ).xpath(
457
- # Pull out required parts
458
- '//iq[@type="result"]/pubsub:pubsub/pubsub:items/pubsub:item',
459
- namespaces
460
- ).map{|item|
461
- item_id = item.attr('id')
462
- data = item.xpath('./*').to_xml
463
- Jubjub::Pubsub::Item.new jid, node, item_id, data, @connection
464
- }
430
+
431
+ Jubjub::Response.new( write request.to_xml ){|stanza|
432
+ stanza.xpath(
433
+ '/iq[@type="result"]/pubsub:pubsub/pubsub:items/pubsub:item',
434
+ namespaces
435
+ ).map{|item|
436
+ item_id = item.attr('id')
437
+ data = item.xpath('./*').to_xml
438
+ Jubjub::Pubsub::Item.new jid, node, item_id, data, @connection
439
+ }
440
+ }.proxy_result
465
441
  end
466
442
 
467
443
 
@@ -496,18 +472,16 @@ module Jubjub
496
472
  }
497
473
  end
498
474
 
499
- write(
500
- # Generate stanza
501
- request.to_xml
502
- ).xpath(
503
- # Pull out required parts
504
- '//iq[@type="result"]/pubsub_owner:pubsub/pubsub_owner:affiliations/pubsub_owner:affiliation',
505
- namespaces
506
- ).map{|affiliation|
507
- jid = Jubjub::Jid.new affiliation.attr('jid')
508
- affiliation = affiliation.attr('affiliation')
509
- Jubjub::Pubsub::Affiliation.new pubsub_jid, pubsub_node, jid, affiliation, @connection
510
- }
475
+ Jubjub::Response.new( write request.to_xml ){|stanza|
476
+ stanza.xpath(
477
+ '/iq[@type="result"]/pubsub_owner:pubsub/pubsub_owner:affiliations/pubsub_owner:affiliation',
478
+ namespaces
479
+ ).map{|item|
480
+ jid = Jubjub::Jid.new item.attr('jid')
481
+ affiliation = item.attr('affiliation')
482
+ Jubjub::Pubsub::Affiliation.new pubsub_jid, pubsub_node, jid, affiliation, @connection
483
+ }
484
+ }.proxy_result
511
485
  end
512
486
 
513
487
  # http://xmpp.org/extensions/xep-0060.html#owner-affiliations-modify
@@ -541,13 +515,9 @@ module Jubjub
541
515
  }
542
516
  end
543
517
 
544
- write(
545
- # Generate stanza
546
- request.to_xml
547
- ).xpath(
548
- # Pull out required parts
549
- '//iq[@type="result"]'
550
- ).any?
518
+ Jubjub::Response.new( write request.to_xml ){|stanza|
519
+ stanza.xpath( '/iq[@type="result"]' ).any?
520
+ }.proxy_result
551
521
  end
552
522
 
553
523
  private
@@ -0,0 +1,33 @@
1
+ module Jubjub
2
+ class Response
3
+ class Error
4
+
5
+ attr_reader :stanza
6
+
7
+ def initialize(xml)
8
+ @stanza = xml
9
+ end
10
+
11
+ def condition
12
+ @condition ||= begin
13
+ condition = @stanza.xpath('//error/ietf:*','ietf' => 'urn:ietf:params:xml:ns:xmpp-stanzas').first
14
+ condition.name if condition
15
+ end
16
+ end
17
+
18
+ def type
19
+ @error_type ||= stanza.xpath('//error').first.attr('type')
20
+ end
21
+
22
+ def text(language=nil)
23
+ if language
24
+ xpath = "//error/text[@xml:lang='#{language}']"
25
+ else
26
+ xpath = "//error/text"
27
+ end
28
+ stanza.xpath(xpath).text
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ module Jubjub
2
+ class Response
3
+ class Proxy
4
+
5
+ # Turn proxy class into a 'BasicObject'
6
+ instance_methods.each do |m|
7
+ undef_method(m) if m.to_s !~ /(?:^__|^nil?$|^send$|^object_id$|^should$)/
8
+ end
9
+
10
+ attr_reader :proxy_primary, :proxy_secondary
11
+
12
+ def initialize(primary, secondary, primary_method)
13
+ @proxy_primary = primary
14
+ @proxy_secondary = secondary
15
+ @primary_method = primary_method
16
+ end
17
+
18
+ # We really want to show the secondary object
19
+ # as the primary is just a thin layer on top
20
+ def inspect
21
+ proxy_secondary.inspect
22
+ end
23
+
24
+ # Used to give away this is really a proxy
25
+ def proxy_class
26
+ Jubjub::Response::Proxy
27
+ end
28
+
29
+ private
30
+
31
+ def method_missing(name, *args, &block)
32
+ # If the method exists on the primary use that
33
+ # unless it's the method name called to create the proxy from the primary, or is a standard object method
34
+ if name.to_s != @primary_method && (proxy_primary.public_methods - Object.public_methods).include?( name.to_s )
35
+ proxy_primary.send(name, *args, &block)
36
+ else
37
+ # Else use a method on the secondary
38
+ proxy_secondary.send(name, *args, &block)
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ module Jubjub
2
+
3
+ class Response
4
+
5
+ attr_reader :stanza, :result
6
+
7
+ def initialize(xml,&block)
8
+ @stanza = xml
9
+
10
+ @result = nil
11
+ @result = yield stanza if block_given?
12
+ end
13
+
14
+ def proxy_result
15
+ # Allows some syntax sugar. We can chain from the result,
16
+ # but still get access to the response via the proxy
17
+ @proxy_result ||= Proxy.new self, result, 'proxy_result'
18
+ end
19
+
20
+ def success?
21
+ @success ||= stanza.xpath('/iq[@type="result"]|/iq[@type="set"]|/iq[@type="get"]').any?
22
+ end
23
+
24
+ def error
25
+ @errors ||= begin
26
+ s = stanza.at_xpath('/iq[@type="error"]/error')
27
+ Error.new s if s
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,3 @@
1
+ require "jubjub/response/response"
2
+ require "jubjub/response/proxy"
3
+ require "jubjub/response/error"
data/lib/jubjub/user.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'jubjub/errors'
2
+ require 'jubjub/response'
2
3
  require 'jubjub/jid'
3
4
  require 'jubjub/connection/xmpp_gateway'
4
5
  require 'jubjub/pubsub'
@@ -14,7 +14,7 @@ describe Jubjub::Connection::XmppGateway do
14
14
 
15
15
  it "return a Jubjub::Muc" do
16
16
  @room = @connection.muc.create( Jubjub::Jid.new 'room@conference.theo-template.local/nick' )
17
- @room.should be_a_kind_of Jubjub::Muc
17
+ @room.should be_a_kind_of_response_proxied Jubjub::Muc
18
18
  @room.jid.should == Jubjub::Jid.new( 'room@conference.theo-template.local' )
19
19
  end
20
20
 
@@ -32,7 +32,7 @@ describe Jubjub::Connection::XmppGateway do
32
32
  @config = Jubjub::Muc::Configuration.new("allow_query_users" => { :type => "boolean", :value => "1", :label => "Allow users to query other users" })
33
33
 
34
34
  @room = @connection.muc.create( Jubjub::Jid.new( 'room@conference.theo-template.local/nick' ), @config )
35
- @room.should be_a_kind_of Jubjub::Muc
35
+ @room.should be_a_kind_of_response_proxied Jubjub::Muc
36
36
  @room.jid.should == Jubjub::Jid.new( 'room@conference.theo-template.local' )
37
37
  end
38
38
 
@@ -104,7 +104,7 @@ describe Jubjub::Connection::XmppGateway do
104
104
 
105
105
  it "return an array of Jubjub::Muc" do
106
106
  list = @connection.muc.list( Jubjub::Jid.new 'conference.theo-template.local' )
107
- list.should be_a_kind_of Array
107
+ list.should be_a_kind_of_response_proxied Array
108
108
 
109
109
  list.size.should eql(2)
110
110
  list[0].should be_a_kind_of Jubjub::Muc
@@ -15,7 +15,7 @@ describe Jubjub::Connection::XmppGateway do
15
15
  it "return a Jubjub::Pubsub" do
16
16
  @pubsub = @connection.pubsub.create 'pubsub.theo-template.local', 'node_1'
17
17
 
18
- @pubsub.should be_a_kind_of Jubjub::Pubsub
18
+ @pubsub.should be_a_kind_of_response_proxied Jubjub::Pubsub
19
19
  @pubsub.jid.should == Jubjub::Jid.new('pubsub.theo-template.local')
20
20
  @pubsub.node.should == 'node_1'
21
21
  end
@@ -82,7 +82,7 @@ describe Jubjub::Connection::XmppGateway do
82
82
 
83
83
  config = @connection.pubsub.default_configuration 'pubsub.theo-template.local'
84
84
 
85
- config.should be_a_kind_of Jubjub::Pubsub::Configuration
85
+ config.should be_a_kind_of_response_proxied Jubjub::Pubsub::Configuration
86
86
  config.should == Jubjub::Pubsub::Configuration.new( expected_config )
87
87
  end
88
88
  end
@@ -130,7 +130,7 @@ describe Jubjub::Connection::XmppGateway do
130
130
 
131
131
  it "return an array of Jubjub::Muc" do
132
132
  list = @connection.pubsub.list 'pubsub.theo-template.local'
133
- list.should be_a_kind_of Array
133
+ list.should be_a_kind_of_response_proxied Array
134
134
 
135
135
  list.size.should eql(2)
136
136
  list[0].should be_a_kind_of Jubjub::Pubsub
@@ -173,7 +173,7 @@ describe Jubjub::Connection::XmppGateway do
173
173
  it "return a Jubjub::Pubsub::Subscription" do
174
174
  @subscription = @connection.pubsub.subscribe( 'pubsub.theo-template.local', 'node_1' )
175
175
 
176
- @subscription.should be_a_kind_of Jubjub::Pubsub::Subscription
176
+ @subscription.should be_a_kind_of_response_proxied Jubjub::Pubsub::Subscription
177
177
  @subscription.jid.should == Jubjub::Jid.new('pubsub.theo-template.local')
178
178
  @subscription.node.should == 'node_1'
179
179
  @subscription.subscriber.should == @jid
@@ -239,7 +239,7 @@ describe Jubjub::Connection::XmppGateway do
239
239
 
240
240
  it "should return a Jubjub::Pubsub::Item" do
241
241
  i = @connection.pubsub.publish 'pubsub.theo-template.local', 'node_1', Jubjub::DataForm.new, '123'
242
- i.should be_a_kind_of Jubjub::Pubsub::Item
242
+ i.should be_a_kind_of_response_proxied Jubjub::Pubsub::Item
243
243
  i.item_id.should == '123'
244
244
  i.data.should == "<x xmlns=\"jabber:x:data\" type=\"submit\"/>"
245
245
  end
@@ -252,7 +252,7 @@ describe Jubjub::Connection::XmppGateway do
252
252
  it "should return a Jubjub::Pubsub::Item" do
253
253
  item = "<x xmlns=\"jabber:x:data\" type=\"submit\"><field var=\"foo\"><value>true</value></field></x>"
254
254
  i = @connection.pubsub.publish 'pubsub.theo-template.local', 'node_1', item
255
- i.should be_a_kind_of Jubjub::Pubsub::Item
255
+ i.should be_a_kind_of_response_proxied Jubjub::Pubsub::Item
256
256
  i.item_id.should be_a_kind_of String
257
257
  i.data.should == "<x xmlns=\"jabber:x:data\" type=\"submit\">\n <field var=\"foo\">\n <value>true</value>\n </field>\n</x>"
258
258
  end
@@ -264,7 +264,7 @@ describe Jubjub::Connection::XmppGateway do
264
264
 
265
265
  it "should return a Jubjub::Pubsub::Item" do
266
266
  i = @connection.pubsub.publish 'pubsub.theo-template.local', 'node_1', Jubjub::DataForm.new({ :foo => {:type => "boolean", :value => true }})
267
- i.should be_a_kind_of Jubjub::Pubsub::Item
267
+ i.should be_a_kind_of_response_proxied Jubjub::Pubsub::Item
268
268
  i.item_id.should be_a_kind_of String
269
269
  i.data.should == "<x xmlns=\"jabber:x:data\" type=\"submit\">\n <field var=\"foo\">\n <value>true</value>\n </field>\n</x>"
270
270
  end
@@ -292,7 +292,7 @@ describe Jubjub::Connection::XmppGateway do
292
292
  end
293
293
 
294
294
  it "should return false when not successful" do
295
- @connection.pubsub.retract( 'pubsub.theo-template.local', 'node_pubsub_retract', "wibble" ).should be_false
295
+ @connection.pubsub.retract( 'pubsub.theo-template.local', 'node_pubsub_retract', "wibble" ).should equal(false)
296
296
  end
297
297
 
298
298
  after do
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jubjub::Response::Error do
4
+
5
+ describe 'creating' do
6
+
7
+ it 'should take a stanza' do
8
+ xml = Nokogiri::XML::Document.parse "<error type='cancel'><feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>"
9
+
10
+ response = Jubjub::Response::Error.new xml
11
+
12
+ response.stanza.should == xml
13
+ end
14
+
15
+ end
16
+
17
+ describe 'instance method' do
18
+
19
+ describe 'condition' do
20
+ it 'should return a "urn:ietf:params:xml:ns:xmpp-stanzas" error condition' do
21
+ xml = Nokogiri::XML::Document.parse "<error><bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'></error>"
22
+ Jubjub::Response::Error.new( xml ).condition.should == "bad-request"
23
+ end
24
+
25
+ it 'should return nil if unknown' do
26
+ xml = Nokogiri::XML::Document.parse "<error><text>Something went wrong</text><error/>"
27
+ Jubjub::Response::Error.new( xml ).condition.should equal(nil)
28
+ end
29
+ end
30
+
31
+ describe 'type' do
32
+ it 'should return nil if no error type available' do
33
+ xml = Nokogiri::XML::Document.parse "<error/>"
34
+ Jubjub::Response::Error.new( xml ).type.should equal(nil)
35
+ end
36
+
37
+ it 'should return value errors present' do
38
+ xml = Nokogiri::XML::Document.parse "<error type='cancel'/>"
39
+ Jubjub::Response::Error.new( xml ).type.should == 'cancel'
40
+ end
41
+ end
42
+
43
+ describe 'text' do
44
+ it 'should return empty string if no text is available' do
45
+ xml = Nokogiri::XML::Document.parse "<error type='cancel'/>"
46
+ Jubjub::Response::Error.new( xml ).text.should == ""
47
+ end
48
+
49
+ it 'should return the text if available' do
50
+ xml = Nokogiri::XML::Document.parse "<error type='cancel'><text>Hello!</text></error>"
51
+ Jubjub::Response::Error.new( xml ).text.should == "Hello!"
52
+ end
53
+
54
+ it 'should return all text if available if multiple locales available' do
55
+ xml = Nokogiri::XML::Document.parse "<error type='cancel'><text xml:lang='en'>Hello!</text><text xml:lang='fr'>Bonjour!</text></error>"
56
+ Jubjub::Response::Error.new( xml ).text.should == "Hello!Bonjour!"
57
+ end
58
+
59
+ it 'should return the text in the correct locale if language specified' do
60
+ xml = Nokogiri::XML::Document.parse "<error type='cancel'><text xml:lang='en'>Hello!</text><text xml:lang='fr'>Bonjour!</text></error>"
61
+ Jubjub::Response::Error.new( xml ).text('fr').should == "Bonjour!"
62
+ Jubjub::Response::Error.new( xml ).text('en').should == "Hello!"
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+ end
@@ -0,0 +1,143 @@
1
+ describe Jubjub::Response::Proxy do
2
+
3
+ describe "creating" do
4
+
5
+ it "should require proxy_primary, proxy_secondary and public_method" do
6
+ primary = "String"
7
+ secondary = ["Array"]
8
+
9
+ p = Jubjub::Response::Proxy.new primary, secondary, "to_s"
10
+
11
+ p.proxy_primary.should equal(primary)
12
+ p.proxy_secondary.should equal(secondary)
13
+ end
14
+
15
+ end
16
+
17
+ describe "proxied methods" do
18
+
19
+ before do
20
+ @primary_class = Class.new do
21
+ def abc() end
22
+ def efg() end
23
+ def xyz() end
24
+ end
25
+
26
+ @secondary_class = Class.new do
27
+ def abc() end # Same as primary
28
+ def efg() end # Same as primary
29
+ def cba() end # Does not exist on primary
30
+ end
31
+
32
+ @primary = @primary_class.new
33
+ @secondary = @secondary_class.new
34
+ end
35
+
36
+ subject { Jubjub::Response::Proxy.new @primary, @secondary, 'efg' }
37
+
38
+ describe "that exist on proxy_primary" do
39
+
40
+ it "should not defer the primary_method" do
41
+ @primary.should_receive(:efg).never
42
+
43
+ subject.efg
44
+ end
45
+
46
+ it "should not defer methods that belong to Object.public_methods" do
47
+ @primary.should_receive(:class).never
48
+
49
+ subject.class
50
+ end
51
+
52
+ it "should defer to the proxy_primary" do
53
+ @primary.should_receive(:abc)
54
+
55
+ subject.abc
56
+ end
57
+
58
+ end
59
+
60
+ describe "that don't defer to proxy_primary" do
61
+
62
+ it "should defer to proxy_secondary" do
63
+ @secondary.should_receive(:cba)
64
+
65
+ subject.cba
66
+ end
67
+
68
+ it "should defer Object.public_methods to proxy_secondary" do
69
+ @secondary.should_receive(:class)
70
+
71
+ subject.class
72
+ end
73
+
74
+ it "should defer to proxy_secondary if primary_method" do
75
+ @secondary.should_receive(:efg)
76
+
77
+ subject.efg
78
+ end
79
+
80
+ it "should blow up if it doesn't exist on proxy_secondary either" do
81
+ expect {
82
+ subject.explode
83
+ }.to raise_error(NoMethodError,"undefined method `explode' for #{@secondary.inspect}")
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+
90
+ describe "instance method" do
91
+
92
+ describe "inspect" do
93
+
94
+ it "should really inspect proxy_secondary" do
95
+ # The proxy_primary is just a thin layer over the top of the proxy_secondary
96
+ # So make sure inspect shows us the proxy_secondary
97
+ primary = "String"
98
+ secondary = ["Array"]
99
+
100
+ p = Jubjub::Response::Proxy.new primary, secondary, "to_s"
101
+
102
+ p.inspect.should == secondary.inspect
103
+ end
104
+
105
+ end
106
+
107
+ describe "proxy_primary" do
108
+
109
+ it "should return the proxy_primary object" do
110
+ primary = "p"
111
+
112
+ proxy = Jubjub::Response::Proxy.new primary, "s", "to_s"
113
+
114
+ proxy.proxy_primary.should equal(primary)
115
+ end
116
+
117
+ end
118
+
119
+ describe "proxy_secondary" do
120
+
121
+ it "should return the proxy_secondary object" do
122
+ secondary = "p"
123
+
124
+ proxy = Jubjub::Response::Proxy.new "p", secondary, "to_s"
125
+
126
+ proxy.proxy_secondary.should equal(secondary)
127
+ end
128
+
129
+ end
130
+
131
+ describe "proxy_class" do
132
+
133
+ it "should return Jubjub::Response::Proxy" do
134
+ p = Jubjub::Response::Proxy.new "p", "s", "to_s"
135
+
136
+ p.proxy_class.should equal(Jubjub::Response::Proxy)
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+
143
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jubjub::Response do
4
+
5
+ describe 'creating' do
6
+
7
+ it 'should take a stanza' do
8
+ stanza = xml_fixture 'dataform_1'
9
+
10
+ response = Jubjub::Response.new stanza
11
+
12
+ response.stanza.should == stanza
13
+ response.result.should == nil
14
+ end
15
+
16
+ it 'should take a block to define result' do
17
+ stanza = xml_fixture 'dataform_1'
18
+
19
+ namespaces = {
20
+ 'commands' => 'http://jabber.org/protocol/commands',
21
+ 'x_data' => 'jabber:x:data'
22
+ }
23
+
24
+ response = Jubjub::Response.new( stanza ) do |s|
25
+ s.xpath(
26
+ "//iq[@type='result']/commands:command/x_data:x[@type='form']/x_data:instructions",
27
+ namespaces
28
+ ).text
29
+ end
30
+
31
+ response.result.should == 'Fill out this form to configure your new bot!'
32
+ end
33
+
34
+ end
35
+
36
+ describe 'instance method' do
37
+
38
+ describe 'result' do
39
+
40
+ before { @stanza = xml_fixture 'dataform_1' }
41
+
42
+ it 'should return result of block used in initialization if specified' do
43
+ Jubjub::Response.new( @stanza ){ "Result" }.result.should == "Result"
44
+ end
45
+
46
+ it 'should return nil if no block provided' do
47
+ Jubjub::Response.new( @stanza ).result.should == nil
48
+ end
49
+
50
+ end
51
+
52
+ describe 'proxy_result' do
53
+
54
+ before { @stanza = xml_fixture 'dataform_1' }
55
+
56
+ it 'should return proxy of result' do
57
+ result = "Result"
58
+ Jubjub::Response.new( @stanza ){ result }.proxy_result.should be_a_kind_of_response_proxied result.class
59
+ end
60
+
61
+ end
62
+
63
+ describe 'success?' do
64
+ it 'should return true when stanza is iq[@type="get"]' do
65
+ xml = Nokogiri::XML::Document.parse "<iq type='get'/>"
66
+ Jubjub::Response.new( xml ).success?.should equal(true)
67
+ end
68
+
69
+ it 'should return true when stanza is iq[@type="result"]' do
70
+ xml = Nokogiri::XML::Document.parse "<iq type='result'/>"
71
+ Jubjub::Response.new( xml ).success?.should equal(true)
72
+ end
73
+
74
+ it 'should return true when stanza is iq[@type="set"]' do
75
+ xml = Nokogiri::XML::Document.parse "<iq type='set'/>"
76
+ Jubjub::Response.new( xml ).success?.should equal(true)
77
+ end
78
+
79
+ it 'should return false when stanza is iq[@type="error"]' do
80
+ xml = Nokogiri::XML::Document.parse "<iq type='error'/>"
81
+ Jubjub::Response.new( xml ).success?.should equal(false)
82
+ end
83
+
84
+ it 'should not look at child iq elements' do
85
+ xml = Nokogiri::XML::Document.parse "<totally><wrong><iq type='result'/></wrong></totally>"
86
+ Jubjub::Response.new( xml ).success?.should equal(false)
87
+ end
88
+
89
+ it 'should return false when stanza is nonsensical' do
90
+ xml = Nokogiri::XML::Document.parse "<presence/>"
91
+ Jubjub::Response.new( xml ).success?.should equal(false)
92
+ end
93
+ end
94
+
95
+ describe 'error' do
96
+
97
+ it 'should return Jubjub::Response:Error object if error present' do
98
+ xml = Nokogiri::XML::Document.parse "<iq type='error'><error><policy-violation xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'></error></iq>"
99
+ r = Jubjub::Response.new( xml )
100
+ r.error.should be_a Jubjub::Response::Error
101
+ r.error.condition.should == "policy-violation"
102
+ end
103
+
104
+ it 'should return nil if error is not present' do
105
+ xml = Nokogiri::XML::Document.parse "<iq type='set'/>"
106
+ Jubjub::Response.new( xml ).error.should be_nil
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+
113
+ end
@@ -0,0 +1,7 @@
1
+ RSpec::Matchers.define :be_a_kind_of_response_proxied do |secondary_class|
2
+ match do |proxy|
3
+ proxy.proxy_class == Jubjub::Response::Proxy &&
4
+ proxy.proxy_primary.class == Jubjub::Response &&
5
+ proxy.proxy_secondary.class == secondary_class
6
+ end
7
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jubjub
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 6
10
- version: 0.0.6
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Theo Cushion
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-16 00:00:00 +01:00
18
+ date: 2011-06-21 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -119,6 +119,10 @@ files:
119
119
  - lib/jubjub/pubsub/pubsub.rb
120
120
  - lib/jubjub/pubsub/subscription.rb
121
121
  - lib/jubjub/pubsub.rb
122
+ - lib/jubjub/response/error.rb
123
+ - lib/jubjub/response/proxy.rb
124
+ - lib/jubjub/response/response.rb
125
+ - lib/jubjub/response.rb
122
126
  - lib/jubjub/user.rb
123
127
  - lib/jubjub.rb
124
128
  - LICENSE
@@ -162,8 +166,12 @@ files:
162
166
  - spec/models/pubsub_item_spec.rb
163
167
  - spec/models/pubsub_spec.rb
164
168
  - spec/models/pubsub_subscription_spec.rb
169
+ - spec/models/response_error_spec.rb
170
+ - spec/models/response_proxy_spec.rb
171
+ - spec/models/response_spec.rb
165
172
  - spec/spec_helper.rb
166
173
  - spec/support/helpers.rb
174
+ - spec/support/matchers.rb
167
175
  - spec/support/shared_examples.rb
168
176
  - .rspec
169
177
  - .infinity_test
@@ -243,8 +251,12 @@ test_files:
243
251
  - spec/models/pubsub_item_spec.rb
244
252
  - spec/models/pubsub_spec.rb
245
253
  - spec/models/pubsub_subscription_spec.rb
254
+ - spec/models/response_error_spec.rb
255
+ - spec/models/response_proxy_spec.rb
256
+ - spec/models/response_spec.rb
246
257
  - spec/spec_helper.rb
247
258
  - spec/support/helpers.rb
259
+ - spec/support/matchers.rb
248
260
  - spec/support/shared_examples.rb
249
261
  - .rspec
250
262
  - .infinity_test