sprsquish-blather 0.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/LICENSE +2 -0
  2. data/README.rdoc +100 -0
  3. data/Rakefile +110 -0
  4. data/examples/drb_client.rb +5 -0
  5. data/examples/echo.rb +18 -0
  6. data/ext/extconf.rb +65 -0
  7. data/ext/push_parser.c +231 -0
  8. data/lib/blather/client.rb +219 -44
  9. data/lib/blather/{core/sugar.rb → core_ext/active_support.rb} +25 -13
  10. data/lib/blather/core_ext/libxml.rb +28 -0
  11. data/lib/blather/errors/sasl_error.rb +87 -0
  12. data/lib/blather/errors/stanza_error.rb +262 -0
  13. data/lib/blather/errors/stream_error.rb +253 -0
  14. data/lib/blather/errors.rb +48 -0
  15. data/lib/blather/{core/jid.rb → jid.rb} +15 -26
  16. data/lib/blather/{core/roster.rb → roster.rb} +22 -0
  17. data/lib/blather/{core/roster_item.rb → roster_item.rb} +39 -8
  18. data/lib/blather/stanza/iq/disco.rb +11 -0
  19. data/lib/blather/stanza/iq/discos/disco_info.rb +86 -0
  20. data/lib/blather/stanza/iq/discos/disco_items.rb +61 -0
  21. data/lib/blather/stanza/iq/query.rb +51 -0
  22. data/lib/blather/stanza/iq/roster.rb +90 -0
  23. data/lib/blather/stanza/iq.rb +38 -0
  24. data/lib/blather/stanza/message.rb +58 -0
  25. data/lib/blather/stanza/presence/status.rb +78 -0
  26. data/lib/blather/stanza/presence/subscription.rb +72 -0
  27. data/lib/blather/stanza/presence.rb +45 -0
  28. data/lib/blather/stanza.rb +101 -0
  29. data/lib/blather/stream/client.rb +26 -0
  30. data/lib/blather/stream/component.rb +34 -0
  31. data/lib/blather/stream/parser.rb +70 -0
  32. data/lib/blather/stream/resource.rb +48 -0
  33. data/lib/blather/stream/sasl.rb +173 -0
  34. data/lib/blather/stream/session.rb +36 -0
  35. data/lib/blather/stream/stream_handler.rb +39 -0
  36. data/lib/blather/stream/tls.rb +33 -0
  37. data/lib/blather/stream.rb +249 -0
  38. data/lib/blather/xmpp_node.rb +199 -0
  39. data/lib/blather.rb +40 -41
  40. data/spec/blather/core_ext/libxml_spec.rb +58 -0
  41. data/spec/blather/errors/sasl_error_spec.rb +56 -0
  42. data/spec/blather/errors/stanza_error_spec.rb +148 -0
  43. data/spec/blather/errors/stream_error_spec.rb +114 -0
  44. data/spec/blather/errors_spec.rb +40 -0
  45. data/spec/blather/{core/jid_spec.rb → jid_spec.rb} +9 -1
  46. data/spec/blather/{core/roster_item_spec.rb → roster_item_spec.rb} +6 -1
  47. data/spec/blather/{core/roster_spec.rb → roster_spec.rb} +16 -6
  48. data/spec/blather/stanza/iq/discos/disco_info_spec.rb +207 -0
  49. data/spec/blather/stanza/iq/discos/disco_items_spec.rb +136 -0
  50. data/spec/blather/stanza/iq/query_spec.rb +34 -0
  51. data/spec/blather/stanza/iq/roster_spec.rb +123 -0
  52. data/spec/blather/stanza/iq_spec.rb +40 -0
  53. data/spec/blather/stanza/message_spec.rb +52 -0
  54. data/spec/blather/stanza/presence/status_spec.rb +102 -0
  55. data/spec/blather/stanza/presence/subscription_spec.rb +85 -0
  56. data/spec/blather/stanza/presence_spec.rb +53 -0
  57. data/spec/blather/{core/stanza_spec.rb → stanza_spec.rb} +14 -2
  58. data/spec/blather/stream/client_spec.rb +787 -0
  59. data/spec/blather/stream/component_spec.rb +86 -0
  60. data/spec/blather/{core/xmpp_node_spec.rb → xmpp_node_spec.rb} +76 -23
  61. data/spec/build_safe.rb +20 -0
  62. data/spec/spec_helper.rb +7 -17
  63. metadata +79 -59
  64. data/CHANGELOG +0 -1
  65. data/blather.gemspec +0 -73
  66. data/lib/blather/callback.rb +0 -24
  67. data/lib/blather/core/errors.rb +0 -24
  68. data/lib/blather/core/stanza.rb +0 -90
  69. data/lib/blather/core/stream.rb +0 -179
  70. data/lib/blather/core/xmpp_node.rb +0 -95
  71. data/lib/blather/extensions/last_activity.rb +0 -57
  72. data/lib/blather/extensions/version.rb +0 -85
  73. data/spec/blather/core/stream_spec.rb +0 -263
data/LICENSE CHANGED
@@ -1,3 +1,5 @@
1
+ Blather
2
+
1
3
  Copyright (c) 2008 Jeff Smick
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
data/README.rdoc CHANGED
@@ -1,3 +1,103 @@
1
1
  = Blather
2
2
 
3
3
  An evented XMPP library
4
+
5
+ == Features
6
+
7
+ * evented architecture
8
+ * uses libxml
9
+ * simplified starting point
10
+
11
+ == Project Pages
12
+
13
+ GitHub:: https://github.com/sprsquish/blather
14
+
15
+ RubyForge:: http://rubyforge.org/projects/squishtech/
16
+
17
+ == Author
18
+
19
+ Jeff Smick <sprsquish@gmail.com>
20
+
21
+ = Usage
22
+
23
+ == Installation
24
+
25
+ sudo gem install blather
26
+
27
+ == Example
28
+
29
+ See the /examples directory for more advanced examples.
30
+
31
+ This will auto-accept any subscription requests and echo back any chat messages.
32
+
33
+ require 'rubygems'
34
+ require 'blather/client'
35
+
36
+ setup 'echo@jabber.local', 'echo'
37
+
38
+ # Auto approve subscription requests
39
+ subscription :request? do |s|
40
+ write s.approve!
41
+ end
42
+
43
+ # Echo back what was said
44
+ message :chat?, :body do |m|
45
+ write m.reply
46
+ end
47
+
48
+ == Handlers
49
+
50
+ Setup handlers by calling their names as methods.
51
+
52
+ # Will only be called for messages where #chat? responds positively
53
+ # and #body == 'exit'
54
+ message :chat?, :body => 'exit'
55
+
56
+ === Handler Guards
57
+
58
+ Guards act like AND statements. Each condition must be met if the handler is to be used.
59
+
60
+ # Equivalent to saying (stanza.chat? && stanza.body)
61
+ message :chat?, :body
62
+
63
+ There are 5 different types of guards:
64
+
65
+ # Symbol
66
+ # Checks for a non-false reply to calling the symbol on the stanza
67
+ # Equivalent to stanza.chat?
68
+ message :chat?
69
+
70
+ # Hash with value (:body => 'exit')
71
+ # Calls the key on the stanza and checks for equality
72
+ # Equivalent to stanza.body == 'exit'
73
+ message :body => 'exit'
74
+
75
+ # Hash with regular expression (:body => /exit/)
76
+ # Calls the key on the stanza and checks for a match
77
+ # Equivalent to stanza.body.match /exit/
78
+ message :body => /exit/
79
+
80
+ # Proc
81
+ # Calls the proc passing in the stanza
82
+ # Checks that the ID is modulo 3
83
+ message proc { |m| m.id % 3 == 0 }
84
+
85
+ # Array
86
+ # Use arrays with the previous types effectively turns the guard into
87
+ # an OR statement.
88
+ # Equivalent to stanza.body == 'foo' || stanza.body == 'baz'
89
+ message [{:body => 'foo'}, {:body => 'baz'}]
90
+
91
+ = TODO
92
+
93
+ * Cleanup API
94
+ * Add lambda callback ability to Iq stanzas
95
+ * Better Documentation
96
+ * Service Discovery (XEP-0030: http://xmpp.org/extensions/xep-0030.html)
97
+ * PubSub (XEP-0060: http://xmpp.org/extensions/xep-0060.html)
98
+ * More examples (Re-write XMPP4R examples into Blather)
99
+
100
+ = License
101
+
102
+ Please see LICENSE
103
+ The LibXML-Ruby license can be found in its directory
data/Rakefile ADDED
@@ -0,0 +1,110 @@
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 = 'An evented XMPP library written on EventMachine and libxml-ruby'
9
+
10
+ gem.email = 'sprsquish@gmail.com'
11
+ gem.homepage = 'http://github.com/sprsquish/blather'
12
+ gem.authors = ['Jeff Smick']
13
+
14
+ gem.rubyforge_project = 'squishtech'
15
+
16
+ gem.extensions = ['Rakefile']
17
+
18
+ gem.add_dependency 'eventmachine', '>= 0.12.6'
19
+ gem.add_dependency 'libxml-ruby', '>= 1.1.3'
20
+
21
+ gem.files = FileList['examples/**/*', 'lib/**/*', 'ext/*'].to_a
22
+
23
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
24
+ end
25
+ rescue LoadError
26
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
27
+ end
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'spec'
32
+ test.pattern = 'spec/**/*_spec.rb'
33
+ test.verbose = true
34
+ end
35
+
36
+ begin
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'spec'
40
+ test.pattern = 'spec/**/*_spec.rb'
41
+ test.rcov_opts += ['--exclude \/Library\/Ruby\/Gems,spec\/', '--xrefs']
42
+ test.verbose = true
43
+ end
44
+ rescue LoadError
45
+ task :rcov do
46
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
47
+ end
48
+ end
49
+
50
+
51
+ begin
52
+ require 'hanna/rdoctask'
53
+
54
+ Rake::RDocTask.new do |rdoc|
55
+ if File.exist?('VERSION.yml')
56
+ config = YAML.load(File.read('VERSION.yml'))
57
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
58
+ else
59
+ version = ""
60
+ end
61
+
62
+ rdoc.rdoc_dir = 'rdoc'
63
+ rdoc.title = "blather #{version}"
64
+ rdoc.rdoc_files.include('README*')
65
+ rdoc.rdoc_files.include('lib/**/*.rb')
66
+ rdoc.options += %w[-S -T hanna --main README.rdoc --exclude autotest --exclude vendor]
67
+ end
68
+ rescue LoadError
69
+ task :rdoc do
70
+ abort "Hanna is not available. In order to use the Hanna, you must: sudo gem install mislav-hanna"
71
+ end
72
+ end
73
+
74
+ MAKE = ENV['MAKE'] || (RUBY_PLATFORM =~ /mswin/ ? 'nmake' : 'make')
75
+
76
+ namespace :ext do
77
+ ext_sources = FileList['ext/*.{rb,c}']
78
+
79
+ desc 'Compile the makefile'
80
+ file 'ext/Makefile' => ext_sources do
81
+ chdir('ext') { ruby 'extconf.rb' }
82
+ end
83
+
84
+ desc "make extension"
85
+ task :make => ext_sources + ['ext/Makefile'] do
86
+ chdir('ext') { sh MAKE }
87
+ end
88
+
89
+ desc 'Build push parser'
90
+ task :build => :make
91
+
92
+ desc 'Clean extensions'
93
+ task :clean do
94
+ chdir 'ext' do
95
+ sh "rm -f Makefile"
96
+ sh "rm -f *.{o,so,bundle,log}"
97
+ end
98
+ end
99
+ end
100
+
101
+ # If running under rubygems...
102
+ __DIR__ ||= File.expand_path(File.dirname(__FILE__))
103
+ if Gem.path.any? {|path| %r(^#{Regexp.escape path}) =~ __DIR__}
104
+ task :default => :gem_build
105
+ else
106
+ task :default => ['ext:build', :test]
107
+ end
108
+
109
+ desc ":default build when running under rubygems."
110
+ task :gem_build => 'ext:build'
@@ -0,0 +1,5 @@
1
+ %w[rubygems lib/blather/client drb/drb].each { |r| require r }
2
+
3
+ setup 'drb_client@jabber.local', 'drb_client'
4
+
5
+ when_ready { DRb.start_service 'druby://localhost:99843', self }
data/examples/echo.rb ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'blather/client'
4
+
5
+ when_ready { puts "Connected ! send messages to #{jid.stripped}." }
6
+
7
+ subscription :request? do |s|
8
+ write s.approve!
9
+ end
10
+
11
+ message :chat?, :body => 'exit' do |m|
12
+ say m.from, 'Exiting ...'
13
+ shutdown
14
+ end
15
+
16
+ message :chat?, :body do |m|
17
+ say m.from, "You sent: #{m.body}"
18
+ end
data/ext/extconf.rb ADDED
@@ -0,0 +1,65 @@
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"
data/ext/push_parser.c ADDED
@@ -0,0 +1,231 @@
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
+ // Chunk through in 4KB chunks so as not to overwhelm the buffers
63
+ int i;
64
+ int chunkSize = length < 4096 ? length : 4096;
65
+ for (i = 0; i < length; i += chunkSize) {
66
+ xmlParseChunk(ctxt, data+i, chunkSize, 0);
67
+ }
68
+ if ((i -= length) > 0)
69
+ xmlParseChunk(ctxt, data+(length-i), i, 0);
70
+
71
+ return self;
72
+ }
73
+
74
+ /*********
75
+ CALLBACKS
76
+ **********/
77
+ static void
78
+ push_parser_start_document_callback (
79
+ void *ctx)
80
+ {
81
+ VALUE handler = (VALUE) ctx;
82
+ if (handler != Qnil)
83
+ rb_funcall (handler, rb_intern("on_start_document"), 0);
84
+ }
85
+
86
+ static void
87
+ push_parser_end_document_callback (
88
+ void *ctx)
89
+ {
90
+ VALUE handler = (VALUE) ctx;
91
+ if (handler != Qnil)
92
+ rb_funcall (handler, rb_intern("on_end_document"), 0);
93
+ }
94
+
95
+ static void
96
+ push_parser_start_element_ns_callback (
97
+ void * ctx,
98
+ const xmlChar * localname,
99
+ const xmlChar * prefix,
100
+ const xmlChar * URI,
101
+ int nb_namespaces,
102
+ const xmlChar ** namespaces,
103
+ int nb_attributes,
104
+ int nb_defaulted,
105
+ const xmlChar ** attributes)
106
+ {
107
+ VALUE handler = (VALUE) ctx;
108
+ if (handler == Qnil)
109
+ return;
110
+
111
+ VALUE attrHash = rb_hash_new();
112
+ VALUE nsHash = rb_hash_new();
113
+
114
+ if (attributes)
115
+ {
116
+ /* Each attribute is an array of [localname, prefix, URI, value, end] */
117
+ int i;
118
+ for (i = 0; i < nb_attributes * 5; i += 5)
119
+ {
120
+ rb_hash_aset( attrHash,
121
+ rb_str_new2((const char*)attributes[i+0]),
122
+ rb_str_new((const char*)attributes[i+3], attributes[i+4] - attributes[i+3]));
123
+ }
124
+ }
125
+
126
+ if (namespaces)
127
+ {
128
+ int i;
129
+ for (i = 0; i < nb_namespaces * 2; i += 2)
130
+ {
131
+ rb_hash_aset( nsHash,
132
+ namespaces[i+0] ? rb_str_new2((const char*)namespaces[i+0]) : Qnil,
133
+ namespaces[i+1] ? rb_str_new2((const char*)namespaces[i+1]) : Qnil);
134
+ }
135
+ }
136
+
137
+ rb_funcall(handler, rb_intern("on_start_element_ns"), 5,
138
+ rb_str_new2((const char*)localname),
139
+ attrHash,
140
+ prefix ? rb_str_new2((const char*)prefix) : Qnil,
141
+ URI ? rb_str_new2((const char*)URI) : Qnil,
142
+ nsHash);
143
+ }
144
+
145
+ static void
146
+ push_parser_end_element_ns_callback (
147
+ void * ctx,
148
+ const xmlChar * localname,
149
+ const xmlChar * prefix,
150
+ const xmlChar * URI)
151
+ {
152
+ VALUE handler = (VALUE) ctx;
153
+ if (handler == Qnil)
154
+ return;
155
+
156
+ rb_funcall(handler, rb_intern("on_end_element_ns"), 3,
157
+ rb_str_new2((const char*)localname),
158
+ prefix ? rb_str_new2((const char*)prefix) : Qnil,
159
+ URI ? rb_str_new2((const char*)URI) : Qnil);
160
+ }
161
+
162
+
163
+ static void
164
+ push_parser_characters_callback (
165
+ void *ctx,
166
+ const char *chars,
167
+ int len)
168
+ {
169
+ VALUE handler = (VALUE) ctx;
170
+ if (handler != Qnil)
171
+ rb_funcall (handler, rb_intern("on_characters"), 1, rb_str_new(chars, len));
172
+ }
173
+
174
+ static void
175
+ push_parser_structured_error_callback (
176
+ void *ctx,
177
+ xmlErrorPtr error)
178
+ {
179
+ VALUE handler = (VALUE) ctx;
180
+ if (handler != Qnil)
181
+ rb_funcall (handler, rb_intern("on_error"), 1, rb_str_new2((const char*)error->message));
182
+ }
183
+
184
+ xmlSAXHandler saxHandler = {
185
+ 0, //internalSubset
186
+ 0, //isStandalone
187
+ 0, //hasInternalSubset
188
+ 0, //hasExternalSubset
189
+ 0, //resolveEntity
190
+ 0, //getEntity
191
+ 0, //entityDecl
192
+ 0, //notationDecl
193
+ 0, //attributeDecl
194
+ 0, //elementDecl
195
+ 0, //unparsedEntityDecl
196
+ 0, //setDocumentLocator
197
+ (startDocumentSAXFunc) push_parser_start_document_callback,
198
+ (endDocumentSAXFunc) push_parser_end_document_callback,
199
+ 0, //startElement
200
+ 0, //endElement
201
+ 0, //reference
202
+ (charactersSAXFunc) push_parser_characters_callback,
203
+ 0, //ignorableWhitespace
204
+ 0, //processingInstruction
205
+ 0, //comment
206
+ 0, //warning
207
+ (errorSAXFunc) push_parser_structured_error_callback,
208
+ 0, //fatalError
209
+ 0, //getParameterEntity
210
+ 0, //cdataBlock
211
+ 0, //externalSubset
212
+ XML_SAX2_MAGIC,
213
+ 0, //_private
214
+ (startElementNsSAX2Func) push_parser_start_element_ns_callback,
215
+ (endElementNsSAX2Func) push_parser_end_element_ns_callback,
216
+ (xmlStructuredErrorFunc) push_parser_structured_error_callback
217
+ };
218
+
219
+ void
220
+ Init_push_parser()
221
+ {
222
+ /* SaxPushParser */
223
+ VALUE mLibXML = rb_define_module("LibXML");
224
+ VALUE mXML = rb_define_module_under(mLibXML, "XML");
225
+ VALUE cXMLSaxPushParser = rb_define_class_under(mXML, "SaxPushParser", rb_cObject);
226
+
227
+ /* Instance Methods */
228
+ rb_define_method(cXMLSaxPushParser, "initialize", push_parser_initialize, 1);
229
+ rb_define_method(cXMLSaxPushParser, "receive", push_parser_receive, 1);
230
+ rb_define_method(cXMLSaxPushParser, "close", push_parser_close, 0);
231
+ }