blather 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/LICENSE +1 -1
  2. data/README.rdoc +41 -12
  3. data/examples/echo.rb +1 -1
  4. data/examples/execute.rb +0 -5
  5. data/examples/pubsub/cli.rb +64 -0
  6. data/examples/pubsub/ping_pong.rb +18 -0
  7. data/examples/rosterprint.rb +14 -0
  8. data/examples/xmpp4r/echo.rb +35 -0
  9. data/lib/blather.rb +35 -12
  10. data/lib/blather/client.rb +1 -1
  11. data/lib/blather/client/client.rb +19 -13
  12. data/lib/blather/client/dsl.rb +16 -0
  13. data/lib/blather/client/dsl/pubsub.rb +133 -0
  14. data/lib/blather/core_ext/active_support.rb +1 -117
  15. data/lib/blather/core_ext/active_support/inheritable_attributes.rb +117 -0
  16. data/lib/blather/core_ext/nokogiri.rb +35 -0
  17. data/lib/blather/errors.rb +3 -20
  18. data/lib/blather/errors/sasl_error.rb +3 -1
  19. data/lib/blather/errors/stanza_error.rb +10 -17
  20. data/lib/blather/errors/stream_error.rb +11 -14
  21. data/lib/blather/jid.rb +1 -0
  22. data/lib/blather/roster.rb +9 -0
  23. data/lib/blather/roster_item.rb +6 -1
  24. data/lib/blather/stanza.rb +35 -18
  25. data/lib/blather/stanza/disco.rb +7 -1
  26. data/lib/blather/stanza/disco/disco_info.rb +45 -33
  27. data/lib/blather/stanza/disco/disco_items.rb +32 -21
  28. data/lib/blather/stanza/iq.rb +13 -8
  29. data/lib/blather/stanza/iq/query.rb +16 -8
  30. data/lib/blather/stanza/iq/roster.rb +33 -22
  31. data/lib/blather/stanza/message.rb +20 -31
  32. data/lib/blather/stanza/presence.rb +3 -5
  33. data/lib/blather/stanza/presence/status.rb +13 -21
  34. data/lib/blather/stanza/presence/subscription.rb +11 -16
  35. data/lib/blather/stanza/pubsub.rb +63 -0
  36. data/lib/blather/stanza/pubsub/affiliations.rb +50 -0
  37. data/lib/blather/stanza/pubsub/create.rb +43 -0
  38. data/lib/blather/stanza/pubsub/errors.rb +9 -0
  39. data/lib/blather/stanza/pubsub/event.rb +77 -0
  40. data/lib/blather/stanza/pubsub/items.rb +63 -0
  41. data/lib/blather/stanza/pubsub/publish.rb +58 -0
  42. data/lib/blather/stanza/pubsub/retract.rb +53 -0
  43. data/lib/blather/stanza/pubsub/subscribe.rb +42 -0
  44. data/lib/blather/stanza/pubsub/subscription.rb +66 -0
  45. data/lib/blather/stanza/pubsub/subscriptions.rb +55 -0
  46. data/lib/blather/stanza/pubsub/unsubscribe.rb +42 -0
  47. data/lib/blather/stanza/pubsub_owner.rb +41 -0
  48. data/lib/blather/stanza/pubsub_owner/delete.rb +34 -0
  49. data/lib/blather/stanza/pubsub_owner/purge.rb +34 -0
  50. data/lib/blather/stream.rb +76 -168
  51. data/lib/blather/stream/client.rb +1 -2
  52. data/lib/blather/stream/component.rb +9 -5
  53. data/lib/blather/stream/features.rb +53 -0
  54. data/lib/blather/stream/features/resource.rb +63 -0
  55. data/lib/blather/stream/{sasl.rb → features/sasl.rb} +53 -52
  56. data/lib/blather/stream/features/session.rb +44 -0
  57. data/lib/blather/stream/features/tls.rb +28 -0
  58. data/lib/blather/stream/parser.rb +70 -46
  59. data/lib/blather/xmpp_node.rb +113 -52
  60. data/spec/blather/client/client_spec.rb +44 -58
  61. data/spec/blather/client/dsl/pubsub_spec.rb +465 -0
  62. data/spec/blather/client/dsl_spec.rb +19 -6
  63. data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
  64. data/spec/blather/errors/sasl_error_spec.rb +8 -8
  65. data/spec/blather/errors/stanza_error_spec.rb +25 -33
  66. data/spec/blather/errors/stream_error_spec.rb +21 -16
  67. data/spec/blather/errors_spec.rb +4 -11
  68. data/spec/blather/jid_spec.rb +31 -30
  69. data/spec/blather/roster_item_spec.rb +34 -23
  70. data/spec/blather/roster_spec.rb +27 -12
  71. data/spec/blather/stanza/discos/disco_info_spec.rb +61 -42
  72. data/spec/blather/stanza/discos/disco_items_spec.rb +47 -35
  73. data/spec/blather/stanza/iq/query_spec.rb +34 -11
  74. data/spec/blather/stanza/iq/roster_spec.rb +47 -30
  75. data/spec/blather/stanza/iq_spec.rb +19 -14
  76. data/spec/blather/stanza/message_spec.rb +30 -17
  77. data/spec/blather/stanza/presence/status_spec.rb +43 -20
  78. data/spec/blather/stanza/presence/subscription_spec.rb +41 -21
  79. data/spec/blather/stanza/presence_spec.rb +34 -21
  80. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  81. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  82. data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
  83. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  84. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  85. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  86. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  87. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  88. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  89. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
  90. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  91. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  92. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  93. data/spec/blather/stanza/pubsub_spec.rb +62 -0
  94. data/spec/blather/stanza_spec.rb +53 -38
  95. data/spec/blather/stream/client_spec.rb +231 -88
  96. data/spec/blather/stream/component_spec.rb +14 -5
  97. data/spec/blather/stream/parser_spec.rb +145 -0
  98. data/spec/blather/xmpp_node_spec.rb +192 -96
  99. data/spec/fixtures/pubsub.rb +311 -0
  100. data/spec/spec_helper.rb +5 -4
  101. metadata +54 -18
  102. data/Rakefile +0 -139
  103. data/ext/extconf.rb +0 -65
  104. data/ext/push_parser.c +0 -209
  105. data/lib/blather/core_ext/libxml.rb +0 -28
  106. data/lib/blather/stream/resource.rb +0 -48
  107. data/lib/blather/stream/session.rb +0 -36
  108. data/lib/blather/stream/stream_handler.rb +0 -39
  109. data/lib/blather/stream/tls.rb +0 -33
  110. data/spec/blather/core_ext/libxml_spec.rb +0 -58
data/Rakefile DELETED
@@ -1,139 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = 'blather'
8
- gem.summary = 'Simpler XMPP'
9
- gem.description = 'An evented XMPP library written on EventMachine and libxml-ruby'
10
-
11
- gem.email = 'sprsquish@gmail.com'
12
- gem.homepage = 'http://github.com/sprsquish/blather'
13
- gem.authors = ['Jeff Smick']
14
-
15
- gem.rubyforge_project = 'squishtech'
16
-
17
- gem.extensions = ['Rakefile']
18
-
19
- gem.add_dependency 'eventmachine', '>= 0.12.6'
20
- gem.add_dependency 'libxml-ruby', '>= 1.1.2'
21
-
22
- gem.files = FileList['examples/**/*', 'lib/**/*', 'ext/*.{rb,c}'].to_a
23
-
24
- gem.test_files = FileList['spec/**/*.rb']
25
-
26
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
27
- end
28
- rescue LoadError
29
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
30
- end
31
-
32
- require 'rake/testtask'
33
- Rake::TestTask.new(:test) do |test|
34
- test.libs << 'lib' << 'spec'
35
- test.pattern = 'spec/**/*_spec.rb'
36
- test.verbose = true
37
- end
38
-
39
- begin
40
- require 'rcov/rcovtask'
41
- Rcov::RcovTask.new do |test|
42
- test.libs << 'spec'
43
- test.pattern = 'spec/**/*_spec.rb'
44
- test.rcov_opts += ['--exclude \/Library\/Ruby\/Gems,spec\/', '--xrefs']
45
- test.verbose = true
46
- end
47
- rescue LoadError
48
- task :rcov do
49
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
50
- end
51
- end
52
-
53
-
54
- begin
55
- require 'hanna/rdoctask'
56
-
57
- Rake::RDocTask.new do |rdoc|
58
- if File.exist?('VERSION.yml')
59
- config = YAML.load(File.read('VERSION.yml'))
60
- version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
61
- else
62
- version = ""
63
- end
64
-
65
- rdoc.rdoc_dir = 'rdoc'
66
- rdoc.title = "blather #{version}"
67
- rdoc.rdoc_files.include('README*')
68
- rdoc.rdoc_files.include('lib/**/*.rb')
69
- rdoc.options += %w[-S -T hanna --main README.rdoc --exclude autotest --exclude vendor]
70
- end
71
- rescue LoadError
72
- task :rdoc do
73
- abort "Hanna is not available. In order to use the Hanna, you must: sudo gem install mislav-hanna"
74
- end
75
- end
76
-
77
- begin
78
- require 'rake/contrib/sshpublisher'
79
- namespace :rubyforge do
80
-
81
- desc "Release gem and RDoc documentation to RubyForge"
82
- task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
83
-
84
- namespace :release do
85
- desc "Publish RDoc to RubyForge."
86
- task :docs => [:rdoc] do
87
- config = YAML.load(
88
- File.read(File.expand_path('~/.rubyforge/user-config.yml'))
89
- )
90
-
91
- host = "#{config['username']}@rubyforge.org"
92
- remote_dir = "/var/www/gforge-projects/squishtech/blather"
93
- local_dir = 'rdoc'
94
-
95
- Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
96
- end
97
- end
98
- end
99
- rescue LoadError
100
- puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
101
- end
102
-
103
- MAKE = ENV['MAKE'] || (RUBY_PLATFORM =~ /mswin/ ? 'nmake' : 'make')
104
-
105
- namespace :ext do
106
- ext_sources = FileList['ext/*.{rb,c}']
107
-
108
- desc 'Compile the makefile'
109
- file 'ext/Makefile' => ext_sources do
110
- chdir('ext') { ruby 'extconf.rb' }
111
- end
112
-
113
- desc "make extension"
114
- task :make => ext_sources + ['ext/Makefile'] do
115
- chdir('ext') { sh MAKE }
116
- end
117
-
118
- desc 'Build push parser'
119
- task :build => :make
120
-
121
- desc 'Clean extensions'
122
- task :clean do
123
- chdir 'ext' do
124
- sh "rm -f Makefile"
125
- sh "rm -f *.{o,so,bundle,log}"
126
- end
127
- end
128
- end
129
-
130
- # If running under rubygems...
131
- __DIR__ ||= File.expand_path(File.dirname(__FILE__))
132
- if Gem.path.any? {|path| %r(^#{Regexp.escape path}) =~ __DIR__}
133
- task :default => :gem_build
134
- else
135
- task :default => ['ext:build', :test]
136
- end
137
-
138
- desc ":default build when running under rubygems."
139
- task :gem_build => 'ext:build'
@@ -1,65 +0,0 @@
1
- require 'mkmf'
2
-
3
- flags = []
4
-
5
- case RUBY_PLATFORM.split('-',2)[1]
6
- when 'mswin32', 'mingw32', 'bccwin32'
7
- unless have_header('windows.h') and
8
- have_header('winsock.h') and
9
- have_library('kernel32') and
10
- have_library('rpcrt4') and
11
- have_library('gdi32')
12
- exit
13
- end
14
-
15
- flags << "-D OS_WIN32"
16
- flags << '-D BUILD_FOR_RUBY'
17
- flags << "-EHs"
18
- flags << "-GR"
19
-
20
- dir_config('xml2')
21
- exit unless have_library('xml2') && have_header('libxml/parser.h')
22
-
23
- when /solaris/
24
- unless have_library('pthread') and
25
- have_library('nsl') and
26
- have_library('socket')
27
- exit
28
- end
29
-
30
- flags << '-D OS_UNIX'
31
- flags << '-D OS_SOLARIS8'
32
- flags << '-D BUILD_FOR_RUBY'
33
-
34
- dir_config('xml2')
35
- exit unless have_library('xml2') && find_header('libxml/parser.h', '/usr/include/libxml2')
36
-
37
- # on Unix we need a g++ link, not gcc.
38
- #CONFIG['LDSHARED'] = "$(CXX) -shared"
39
-
40
- when /darwin/
41
- flags << '-DOS_UNIX'
42
- flags << '-DBUILD_FOR_RUBY'
43
-
44
- dir_config('xml2')
45
- exit unless have_library('xml2') && find_header('libxml/parser.h', '/usr/include/libxml2')
46
- # on Unix we need a g++ link, not gcc.
47
- #CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
48
-
49
- else
50
- unless have_library('pthread')
51
- exit
52
- end
53
-
54
- flags << '-DOS_UNIX'
55
- flags << '-DBUILD_FOR_RUBY'
56
-
57
- dir_config('xml2')
58
- exit unless have_library('xml2') && find_header('libxml/parser.h', '/usr/include/libxml2')
59
- # on Unix we need a g++ link, not gcc.
60
- #CONFIG['LDSHARED'] = "$(CXX) -shared"
61
- end
62
-
63
- $CFLAGS += ' ' + flags.join(' ')
64
-
65
- create_makefile "push_parser"
@@ -1,209 +0,0 @@
1
- /*
2
- * Most of this was ripped from libxml-ruby's SAX handler
3
- */
4
-
5
- #include <libxml/parser.h>
6
- #include <ruby.h>
7
-
8
- xmlSAXHandler saxHandler;
9
-
10
- static VALUE
11
- push_parser_raise_error (
12
- xmlErrorPtr error)
13
- {
14
- rb_raise(rb_eStandardError, "%s", rb_str_new2((const char*)error->message));
15
- return Qnil;
16
- }
17
-
18
- static VALUE
19
- push_parser_initialize (
20
- VALUE self,
21
- VALUE handler)
22
- {
23
- xmlParserCtxtPtr ctxt;
24
- ctxt = xmlCreatePushParserCtxt(&saxHandler, (void *)handler, NULL, 0, NULL);
25
-
26
- if (!ctxt)
27
- return push_parser_raise_error(&xmlLastError);
28
-
29
- ctxt->sax2 = 1;
30
-
31
- rb_iv_set(self, "@__libxml_push_parser", Data_Wrap_Struct(rb_cData, 0, xmlFreeParserCtxt, ctxt));
32
-
33
- return self;
34
- }
35
-
36
- static VALUE
37
- push_parser_close (
38
- VALUE self)
39
- {
40
- xmlParserCtxtPtr ctxt;
41
- Data_Get_Struct(rb_iv_get(self, "@__libxml_push_parser"), xmlParserCtxt, ctxt);
42
-
43
- if (xmlParseChunk(ctxt, "", 0, 1)) {
44
- return push_parser_raise_error(&xmlLastError);
45
- }
46
- else {
47
- return Qtrue;
48
- }
49
- }
50
-
51
- static VALUE
52
- push_parser_receive (
53
- VALUE self,
54
- VALUE chunk)
55
- {
56
- const char *data = StringValuePtr(chunk);
57
- int length = RSTRING_LEN(chunk);
58
-
59
- xmlParserCtxtPtr ctxt;
60
- Data_Get_Struct(rb_iv_get(self, "@__libxml_push_parser"), xmlParserCtxt, ctxt);
61
-
62
- int i;
63
- // Non-chunking version
64
- for (i = 0; i < length; i++) {
65
- xmlParseChunk (ctxt, data+i, 1, 0);
66
- }
67
-
68
- /* Chunk through in 4KB chunks so as not to overwhelm the buffers
69
- int chunkSize = length < 4096 ? length : 4096;
70
- for (i = 0; i < length; i += chunkSize) {
71
- xmlParseChunk(ctxt, data+i, chunkSize, 0);
72
- }
73
- if ((i -= length) > 0)
74
- xmlParseChunk(ctxt, data+(length-i), i, 0);
75
- */
76
-
77
- return self;
78
- }
79
-
80
- /*********
81
- CALLBACKS
82
- **********/
83
- static void
84
- push_parser_start_element_ns_callback (
85
- void * ctx,
86
- const xmlChar * localname,
87
- const xmlChar * prefix,
88
- const xmlChar * URI,
89
- int nb_namespaces,
90
- const xmlChar ** namespaces,
91
- int nb_attributes,
92
- int nb_defaulted,
93
- const xmlChar ** attributes)
94
- {
95
- VALUE handler = (VALUE) ctx;
96
- if (handler == Qnil)
97
- return;
98
-
99
- VALUE attrHash = rb_hash_new();
100
- VALUE nsHash = rb_hash_new();
101
-
102
- if (attributes)
103
- {
104
- /* Each attribute is an array of [localname, prefix, URI, value, end] */
105
- int i;
106
- for (i = 0; i < nb_attributes * 5; i += 5)
107
- {
108
- rb_hash_aset( attrHash,
109
- rb_str_new2((const char*)attributes[i+0]),
110
- rb_str_new((const char*)attributes[i+3], attributes[i+4] - attributes[i+3]));
111
- }
112
- }
113
-
114
- if (namespaces)
115
- {
116
- int i;
117
- for (i = 0; i < nb_namespaces * 2; i += 2)
118
- {
119
- rb_hash_aset( nsHash,
120
- namespaces[i+0] ? rb_str_new2((const char*)namespaces[i+0]) : Qnil,
121
- namespaces[i+1] ? rb_str_new2((const char*)namespaces[i+1]) : Qnil);
122
- }
123
- }
124
-
125
- rb_funcall(handler, rb_intern("on_start_element_ns"), 5,
126
- rb_str_new2((const char*)localname),
127
- attrHash,
128
- prefix ? rb_str_new2((const char*)prefix) : Qnil,
129
- URI ? rb_str_new2((const char*)URI) : Qnil,
130
- nsHash);
131
- }
132
-
133
- static void
134
- push_parser_end_element_ns_callback (
135
- void * ctx,
136
- const xmlChar * localname,
137
- const xmlChar * prefix,
138
- const xmlChar * URI)
139
- {
140
- VALUE handler = (VALUE) ctx;
141
- if (handler == Qnil)
142
- return;
143
-
144
- rb_funcall(handler, rb_intern("on_end_element_ns"), 3,
145
- rb_str_new2((const char*)localname),
146
- prefix ? rb_str_new2((const char*)prefix) : Qnil,
147
- URI ? rb_str_new2((const char*)URI) : Qnil);
148
- }
149
-
150
-
151
- static void
152
- push_parser_characters_callback (
153
- void *ctx,
154
- const char *chars,
155
- int len)
156
- {
157
- VALUE handler = (VALUE) ctx;
158
- if (handler != Qnil)
159
- rb_funcall (handler, rb_intern("on_characters"), 1, rb_str_new(chars, len));
160
- }
161
-
162
- xmlSAXHandler saxHandler = {
163
- 0, //internalSubset
164
- 0, //isStandalone
165
- 0, //hasInternalSubset
166
- 0, //hasExternalSubset
167
- 0, //resolveEntity
168
- 0, //getEntity
169
- 0, //entityDecl
170
- 0, //notationDecl
171
- 0, //attributeDecl
172
- 0, //elementDecl
173
- 0, //unparsedEntityDecl
174
- 0, //setDocumentLocator
175
- 0, //(startDocument)
176
- 0, //(endDocument)
177
- 0, //startElement
178
- 0, //endElement
179
- 0, //reference
180
- (charactersSAXFunc) push_parser_characters_callback,
181
- 0, //ignorableWhitespace
182
- 0, //processingInstruction
183
- 0, //comment
184
- 0, //warning
185
- 0, //error
186
- 0, //fatalError
187
- 0, //getParameterEntity
188
- 0, //cdataBlock
189
- 0, //externalSubset
190
- XML_SAX2_MAGIC,
191
- 0, //_private
192
- (startElementNsSAX2Func) push_parser_start_element_ns_callback,
193
- (endElementNsSAX2Func) push_parser_end_element_ns_callback,
194
- 0 // LibXML handles this globally
195
- };
196
-
197
- void
198
- Init_push_parser()
199
- {
200
- /* SaxPushParser */
201
- VALUE mLibXML = rb_define_module("LibXML");
202
- VALUE mXML = rb_define_module_under(mLibXML, "XML");
203
- VALUE cXMLSaxPushParser = rb_define_class_under(mXML, "SaxPushParser", rb_cObject);
204
-
205
- /* Instance Methods */
206
- rb_define_method(cXMLSaxPushParser, "initialize", push_parser_initialize, 1);
207
- rb_define_method(cXMLSaxPushParser, "receive", push_parser_receive, 1);
208
- rb_define_method(cXMLSaxPushParser, "close", push_parser_close, 0);
209
- }
@@ -1,28 +0,0 @@
1
- module LibXML # :nodoc:
2
- module XML # :nodoc:
3
-
4
- class Node
5
- alias_method :element_name, :name
6
- alias_method :element_name=, :name=
7
- end
8
-
9
- class Attributes
10
- # Helper method for removing attributes
11
- def remove(name)
12
- attribute = get_attribute(name.to_s)
13
- attribute.remove! if attribute
14
- end
15
-
16
- alias_method :old_hash_set, :[]= # :nodoc:
17
- def []=(name, val)
18
- val.nil? ? remove(name.to_s) : old_hash_set(name.to_s, val.to_s)
19
- end
20
-
21
- alias_method :old_hash_get, :[] # :nodoc:
22
- def [](name)
23
- old_hash_get name.to_s
24
- end
25
- end #Attributes
26
-
27
- end #XML
28
- end #LibXML
@@ -1,48 +0,0 @@
1
- module Blather # :nodoc:
2
- class Stream # :nodoc:
3
-
4
- class Resource < StreamHandler # :nodoc:
5
- def initialize(stream, jid)
6
- super stream
7
- @jid = jid
8
- end
9
-
10
- private
11
- ##
12
- # Respond to the bind request
13
- # If @jid has a resource set already request it from the server
14
- def bind
15
- response = Stanza::Iq.new :set
16
- @id = response.id
17
-
18
- binder = XMPPNode.new('bind')
19
- binder.namespace = 'urn:ietf:params:xml:ns:xmpp-bind'
20
-
21
- binder << XMPPNode.new('resource', @jid.resource) if @jid.resource
22
-
23
- response << binder
24
- @stream.send response
25
- end
26
-
27
- ##
28
- # Process the result from the server
29
- # Sets the sends the JID (now bound to a resource)
30
- # back to the stream
31
- def result
32
- LOG.debug "RESOURE NODE #{@node}"
33
- # ensure this is a response to our original request
34
- if @id == @node['id']
35
- @jid = JID.new @node.find_first('//bind_ns:bind/bind_ns:jid', :bind_ns => 'urn:ietf:params:xml:ns:xmpp-bind').content
36
- success @jid
37
- end
38
- end
39
-
40
- ##
41
- # Server returned an error
42
- def error
43
- failure StanzaError.import(@node)
44
- end
45
- end #Resource
46
-
47
- end #Stream
48
- end #Blather