nokogiri 1.4.3.1-java → 1.4.4-java

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (63) hide show
  1. data/CHANGELOG.ja.rdoc +26 -0
  2. data/CHANGELOG.rdoc +26 -0
  3. data/Manifest.txt +3 -0
  4. data/README.ja.rdoc +0 -4
  5. data/README.rdoc +0 -4
  6. data/Rakefile +1 -0
  7. data/bin/nokogiri +6 -1
  8. data/ext/nokogiri/depend +358 -32
  9. data/ext/nokogiri/extconf.rb +1 -3
  10. data/ext/nokogiri/nokogiri.c +2 -0
  11. data/ext/nokogiri/nokogiri.h +7 -0
  12. data/ext/nokogiri/xml_dtd.c +2 -2
  13. data/ext/nokogiri/xml_io.c +2 -2
  14. data/ext/nokogiri/xml_node.c +31 -6
  15. data/ext/nokogiri/xml_node_set.c +1 -1
  16. data/ext/nokogiri/xml_sax_parser.c +1 -1
  17. data/ext/nokogiri/xml_sax_parser_context.c +40 -0
  18. data/ext/nokogiri/xml_xpath_context.c +33 -2
  19. data/ext/nokogiri/xslt_stylesheet.c +116 -4
  20. data/lib/nokogiri/css/generated_tokenizer.rb +1 -2
  21. data/lib/nokogiri/css/xpath_visitor.rb +15 -7
  22. data/lib/nokogiri/decorators/slop.rb +5 -3
  23. data/lib/nokogiri/ffi/libxml.rb +9 -0
  24. data/lib/nokogiri/ffi/structs/xml_parser_context.rb +2 -1
  25. data/lib/nokogiri/ffi/structs/xml_parser_input.rb +19 -0
  26. data/lib/nokogiri/ffi/xml/dtd.rb +2 -2
  27. data/lib/nokogiri/ffi/xml/node.rb +9 -4
  28. data/lib/nokogiri/ffi/xml/sax/parser_context.rb +12 -0
  29. data/lib/nokogiri/ffi/xml/xpath_context.rb +5 -0
  30. data/lib/nokogiri/ffi/xslt/stylesheet.rb +21 -1
  31. data/lib/nokogiri/html/document.rb +3 -3
  32. data/lib/nokogiri/html/document_fragment.rb +19 -17
  33. data/lib/nokogiri/version.rb +1 -1
  34. data/lib/nokogiri/xml/document.rb +26 -1
  35. data/lib/nokogiri/xml/document_fragment.rb +2 -2
  36. data/lib/nokogiri/xml/dtd.rb +11 -0
  37. data/lib/nokogiri/xml/node.rb +156 -45
  38. data/lib/nokogiri/xml/node_set.rb +2 -2
  39. data/lib/nokogiri/xml/reader.rb +36 -0
  40. data/lib/nokogiri/xml/sax/document.rb +4 -2
  41. data/lib/nokogiri/xslt.rb +9 -5
  42. data/tasks/cross_compile.rb +24 -2
  43. data/test/css/test_parser.rb +29 -18
  44. data/test/decorators/test_slop.rb +16 -0
  45. data/test/html/test_document_fragment.rb +46 -3
  46. data/test/html/test_node.rb +9 -0
  47. data/test/xml/sax/test_parser.rb +11 -3
  48. data/test/xml/sax/test_parser_context.rb +50 -0
  49. data/test/xml/sax/test_push_parser.rb +18 -1
  50. data/test/xml/test_document_fragment.rb +14 -2
  51. data/test/xml/test_dtd.rb +15 -0
  52. data/test/xml/test_node.rb +31 -2
  53. data/test/xml/test_node_reparenting.rb +59 -31
  54. data/test/xml/test_node_set.rb +13 -0
  55. data/test/xml/test_xpath.rb +32 -0
  56. data/test/xslt/test_custom_functions.rb +94 -0
  57. metadata +495 -516
  58. data/ext/nokogiri/libcharset-1.dll +0 -0
  59. data/ext/nokogiri/libexslt.dll +0 -0
  60. data/ext/nokogiri/libiconv-2.dll +0 -0
  61. data/ext/nokogiri/libxml2.dll +0 -0
  62. data/ext/nokogiri/libxslt.dll +0 -0
  63. data/ext/nokogiri/zlib1.dll +0 -0
@@ -91,14 +91,12 @@ def asplode(lib)
91
91
  abort "-----\n#{lib} is missing. please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.\n-----"
92
92
  end
93
93
 
94
- # Use this with cross compiling
95
- # PKG_CONFIG_PATH=/Users/apatterson/git/nokogiri/tmp/cross/lib/pkgconfig/ \
96
- # rake cross compile RUBY_CC_VERSION=1.9.1
97
94
  pkg_config('libxslt') if RUBY_PLATFORM =~ /mingw/
98
95
 
99
96
  asplode "libxml2" unless find_header('libxml/parser.h')
100
97
  asplode "libxslt" unless find_header('libxslt/xslt.h')
101
98
  asplode "libexslt" unless find_header('libexslt/exslt.h')
99
+ asplode "libiconv" unless have_func('iconv_open', 'iconv.h') or have_library('iconv', 'iconv_open', 'iconv.h')
102
100
  asplode "libxml2" unless find_library("#{lib_prefix}xml2", 'xmlParseDoc')
103
101
  asplode "libxslt" unless find_library("#{lib_prefix}xslt", 'xsltParseStylesheetDoc')
104
102
  asplode "libexslt" unless find_library("#{lib_prefix}exslt", 'exsltFuncRegister')
@@ -30,12 +30,14 @@ int is_2_6_16(void)
30
30
 
31
31
  void Init_nokogiri()
32
32
  {
33
+ #ifndef __MACRUBY__
33
34
  xmlMemSetup(
34
35
  (xmlFreeFunc)ruby_xfree,
35
36
  (xmlMallocFunc)ruby_xmalloc,
36
37
  (xmlReallocFunc)ruby_xrealloc,
37
38
  strdup
38
39
  );
40
+ #endif
39
41
 
40
42
  mNokogiri = rb_define_module("Nokogiri");
41
43
  mNokogiriXml = rb_define_module_under(mNokogiri, "XML");
@@ -27,6 +27,7 @@ int vasprintf (char **strp, const char *fmt, va_list ap);
27
27
  #include <libxml/HTMLparser.h>
28
28
  #include <libxml/HTMLtree.h>
29
29
  #include <libxml/relaxng.h>
30
+ #include <libxslt/extensions.h>
30
31
  #include <ruby.h>
31
32
 
32
33
  #ifdef HAVE_RUBY_ENCODING_H
@@ -134,6 +135,8 @@ extern VALUE mNokogiriXslt ;
134
135
  #define NOKOGIRI_DEBUG_START(p)
135
136
  #define NOKOGIRI_DEBUG_END(p)
136
137
 
138
+ #endif
139
+
137
140
  #ifndef RSTRING_PTR
138
141
  #define RSTRING_PTR(s) (RSTRING(s)->ptr)
139
142
  #endif
@@ -150,6 +153,10 @@ extern VALUE mNokogiriXslt ;
150
153
  #define RARRAY_LEN(a) RARRAY(a)->len
151
154
  #endif
152
155
 
156
+ #ifndef __builtin_expect
157
+ # if defined(__GNUC__)
158
+ # define __builtin_expect(expr, c) __builtin_expect((long)(expr), (long)(c))
159
+ # endif
153
160
  #endif
154
161
 
155
162
  #endif
@@ -84,10 +84,10 @@ static VALUE attributes(VALUE self)
84
84
 
85
85
  Data_Get_Struct(self, xmlDtd, dtd);
86
86
 
87
- if(!dtd->attributes) return Qnil;
88
-
89
87
  hash = rb_hash_new();
90
88
 
89
+ if(!dtd->attributes) return hash;
90
+
91
91
  xmlHashScan((xmlHashTablePtr)dtd->attributes, element_copier, (void *)hash);
92
92
 
93
93
  return hash;
@@ -8,14 +8,14 @@ int io_read_callback(void * ctx, char * buffer, int len) {
8
8
 
9
9
  if(NIL_P(string)) return 0;
10
10
 
11
- memcpy(buffer, StringValuePtr(string), (unsigned int)RSTRING_LEN(string));
11
+ memcpy(buffer, StringValuePtr(string), (size_t)RSTRING_LEN(string));
12
12
 
13
13
  return (int)RSTRING_LEN(string);
14
14
  }
15
15
 
16
16
  int io_write_callback(void * ctx, char * buffer, int len) {
17
17
  VALUE io = (VALUE)ctx;
18
- VALUE string = rb_str_new(buffer, len);
18
+ VALUE string = rb_str_new(buffer, (long)len);
19
19
 
20
20
  rb_funcall(io, id_write, 1, string);
21
21
  return len;
@@ -251,7 +251,13 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
251
251
  * call-seq:
252
252
  * create_internal_subset(name, external_id, system_id)
253
253
  *
254
- * Create an internal subset
254
+ * Create the internal subset of a document.
255
+ *
256
+ * doc.create_internal_subset("chapter", "-//OASIS//DTD DocBook XML//EN", "chapter.dtd")
257
+ * # => <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML//EN" "chapter.dtd">
258
+ *
259
+ * doc.create_internal_subset("chapter", nil, "chapter.dtd")
260
+ * # => <!DOCTYPE chapter SYSTEM "chapter.dtd">
255
261
  */
256
262
  static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
257
263
  {
@@ -377,6 +383,8 @@ static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
377
383
  dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
378
384
  if(dup == NULL) return Qnil;
379
385
 
386
+ NOKOGIRI_ROOT_NODE(dup);
387
+
380
388
  return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
381
389
  }
382
390
 
@@ -774,7 +782,8 @@ static VALUE attribute_nodes(VALUE self)
774
782
  * call-seq:
775
783
  * namespace()
776
784
  *
777
- * returns the Nokogiri::XML::Namespace for the node, if one exists.
785
+ * returns the default namespace set on this node (as with an "xmlns="
786
+ * attribute), as a Namespace object.
778
787
  */
779
788
  static VALUE namespace(VALUE self)
780
789
  {
@@ -791,7 +800,7 @@ static VALUE namespace(VALUE self)
791
800
  * call-seq:
792
801
  * namespace_definitions()
793
802
  *
794
- * returns a list of Namespace nodes defined on _self_
803
+ * returns namespaces defined on self element directly, as an array of Namespace objects. Includes both a default namespace (as in"xmlns="), and prefixed namespaces (as in "xmlns:prefix=").
795
804
  */
796
805
  static VALUE namespace_definitions(VALUE self)
797
806
  {
@@ -820,8 +829,10 @@ static VALUE namespace_definitions(VALUE self)
820
829
  * call-seq:
821
830
  * namespace_scopes()
822
831
  *
823
- * returns a list of Namespace nodes in scope for _self_. this is all
824
- * namespaces defined in the node, or in any ancestor node.
832
+ * returns namespaces in scope for self -- those defined on self element
833
+ * directly or any ancestor node -- as an array of Namespace objects. Default
834
+ * namespaces ("xmlns=" style) for self are included in this array; Default
835
+ * namespaces for ancestors, however, are not. See also #namespaces
825
836
  */
826
837
  static VALUE namespace_scopes(VALUE self)
827
838
  {
@@ -1045,7 +1056,12 @@ static VALUE line(VALUE self)
1045
1056
  * call-seq:
1046
1057
  * add_namespace_definition(prefix, href)
1047
1058
  *
1048
- * Adds a namespace definition with +prefix+ using +href+
1059
+ * Adds a namespace definition with +prefix+ using +href+ value. The result is
1060
+ * as if parsed XML for this node had included an attribute
1061
+ * 'xmlns:prefix=value'. A default namespace for this node ("xmlns=") can be
1062
+ * added by passing 'nil' for prefix. Namespaces added this way will not
1063
+ * show up in #attributes, but they will be included as an xmlns attribute
1064
+ * when the node is serialized to XML.
1049
1065
  */
1050
1066
  static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
1051
1067
  {
@@ -1155,6 +1171,7 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1155
1171
  {
1156
1172
  xmlNodePtr node;
1157
1173
  xmlNodePtr list;
1174
+ xmlNodePtr child_iter;
1158
1175
  xmlNodeSetPtr set;
1159
1176
  xmlParserErrors error;
1160
1177
  VALUE doc, err;
@@ -1180,6 +1197,14 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1180
1197
  (int)NUM2INT(_options),
1181
1198
  &list);
1182
1199
 
1200
+ /* make sure parent/child pointers are coherent so an unlink will work properly (#331) */
1201
+ child_iter = node->doc->children ;
1202
+ while (child_iter) {
1203
+ if (child_iter->parent != (xmlNodePtr)node->doc)
1204
+ child_iter->parent = (xmlNodePtr)node->doc ;
1205
+ child_iter = child_iter->next ;
1206
+ }
1207
+
1183
1208
  #ifndef HTML_PARSE_NOIMPLIED
1184
1209
  htmlHandleOmittedElem(1);
1185
1210
  #endif
@@ -257,7 +257,7 @@ static VALUE slice(int argc, VALUE *argv, VALUE self)
257
257
  }
258
258
 
259
259
  /* if arg is Range */
260
- switch (rb_range_beg_len(arg, &beg, &len, node_set->nodeNr, 0)) {
260
+ switch (rb_range_beg_len(arg, &beg, &len, (long)node_set->nodeNr, 0)) {
261
261
  case Qfalse:
262
262
  break;
263
263
  case Qnil:
@@ -238,7 +238,7 @@ static void deallocate(xmlSAXHandlerPtr handler)
238
238
 
239
239
  static VALUE allocate(VALUE klass)
240
240
  {
241
- xmlSAXHandlerPtr handler = calloc(1, sizeof(xmlSAXHandler));
241
+ xmlSAXHandlerPtr handler = calloc((size_t)1, sizeof(xmlSAXHandler));
242
242
 
243
243
  xmlSetStructuredErrorFunc(NULL, NULL);
244
244
 
@@ -140,6 +140,44 @@ static VALUE get_replace_entities(VALUE self)
140
140
  return Qtrue;
141
141
  }
142
142
 
143
+ /*
144
+ * call-seq: line
145
+ *
146
+ * Get the current line the parser context is processing.
147
+ */
148
+ static VALUE line(VALUE self)
149
+ {
150
+ xmlParserCtxtPtr ctxt;
151
+ xmlParserInputPtr io;
152
+
153
+ Data_Get_Struct(self, xmlParserCtxt, ctxt);
154
+
155
+ io = ctxt->input;
156
+ if(io)
157
+ return INT2NUM(io->line);
158
+
159
+ return Qnil;
160
+ }
161
+
162
+ /*
163
+ * call-seq: column
164
+ *
165
+ * Get the current column the parser context is processing.
166
+ */
167
+ static VALUE column(VALUE self)
168
+ {
169
+ xmlParserCtxtPtr ctxt;
170
+ xmlParserInputPtr io;
171
+
172
+ Data_Get_Struct(self, xmlParserCtxt, ctxt);
173
+
174
+ io = ctxt->input;
175
+ if(io)
176
+ return INT2NUM(io->col);
177
+
178
+ return Qnil;
179
+ }
180
+
143
181
  void init_xml_sax_parser_context()
144
182
  {
145
183
  VALUE nokogiri = rb_define_module("Nokogiri");
@@ -156,4 +194,6 @@ void init_xml_sax_parser_context()
156
194
  rb_define_method(klass, "parse_with", parse_with, 1);
157
195
  rb_define_method(klass, "replace_entities=", set_replace_entities, 1);
158
196
  rb_define_method(klass, "replace_entities", get_replace_entities, 0);
197
+ rb_define_method(klass, "line", line, 0);
198
+ rb_define_method(klass, "column", column, 0);
159
199
  }
@@ -27,6 +27,28 @@ static VALUE register_ns(VALUE self, VALUE prefix, VALUE uri)
27
27
  return self;
28
28
  }
29
29
 
30
+ /*
31
+ * call-seq:
32
+ * register_variable(name, value)
33
+ *
34
+ * Register the variable +name+ with +value+.
35
+ */
36
+ static VALUE register_variable(VALUE self, VALUE name, VALUE value)
37
+ {
38
+ xmlXPathContextPtr ctx;
39
+ xmlXPathObjectPtr xmlValue;
40
+ Data_Get_Struct(self, xmlXPathContext, ctx);
41
+
42
+ xmlValue = xmlXPathNewCString(StringValuePtr(value));
43
+
44
+ xmlXPathRegisterVariable( ctx,
45
+ (const xmlChar *)StringValuePtr(name),
46
+ xmlValue
47
+ );
48
+
49
+ return self;
50
+ }
51
+
30
52
  static void ruby_funcall(xmlXPathParserContextPtr ctx, int nargs)
31
53
  {
32
54
  VALUE xpath_handler = Qnil;
@@ -46,7 +68,11 @@ static void ruby_funcall(xmlXPathParserContextPtr ctx, int nargs)
46
68
 
47
69
  xpath_handler = (VALUE)(ctx->context->userData);
48
70
 
49
- argv = (VALUE *)calloc((unsigned int)nargs, sizeof(VALUE));
71
+ argv = (VALUE *)calloc((size_t)nargs, sizeof(VALUE));
72
+ for (i = 0 ; i < nargs ; ++i) {
73
+ rb_gc_register_address(&argv[i]);
74
+ }
75
+
50
76
  doc = DOC_RUBY_OBJECT(ctx->context->doc);
51
77
 
52
78
  i = nargs - 1;
@@ -77,6 +103,10 @@ static void ruby_funcall(xmlXPathParserContextPtr ctx, int nargs)
77
103
  nargs,
78
104
  argv
79
105
  );
106
+
107
+ for (i = 0 ; i < nargs ; ++i) {
108
+ rb_gc_unregister_address(&argv[i]);
109
+ }
80
110
  free(argv);
81
111
 
82
112
  switch(TYPE(result)) {
@@ -156,7 +186,7 @@ static void xpath_generic_exception_handler(void * ctx, const char *msg, ...)
156
186
 
157
187
  /*
158
188
  * call-seq:
159
- * evaluate(search_path)
189
+ * evaluate(search_path, handler = nil)
160
190
  *
161
191
  * Evaluate the +search_path+ returning an XML::XPath object.
162
192
  */
@@ -274,5 +304,6 @@ void init_xml_xpath_context(void)
274
304
 
275
305
  rb_define_singleton_method(klass, "new", new, 1);
276
306
  rb_define_method(klass, "evaluate", evaluate, -1);
307
+ rb_define_method(klass, "register_variable", register_variable, 2);
277
308
  rb_define_method(klass, "register_ns", register_ns, 2);
278
309
  }
@@ -5,6 +5,8 @@
5
5
  #include <libxslt/transform.h>
6
6
  #include <libexslt/exslt.h>
7
7
 
8
+ VALUE xslt;
9
+
8
10
  int vasprintf (char **strp, const char *fmt, va_list ap);
9
11
 
10
12
  static void dealloc(xsltStylesheetPtr doc)
@@ -24,7 +26,7 @@ static void xslt_generic_error_handler(void * ctx, const char *msg, ...)
24
26
  vasprintf(&message, msg, args);
25
27
  va_end(args);
26
28
 
27
- rb_raise(rb_eRuntimeError, message);
29
+ rb_exc_raise(rb_exc_new2(rb_eRuntimeError, message));
28
30
  }
29
31
 
30
32
  /*
@@ -97,7 +99,7 @@ static VALUE transform(int argc, VALUE* argv, VALUE self)
97
99
  long param_len, j ;
98
100
 
99
101
  rb_scan_args(argc, argv, "11", &xmldoc, &paramobj);
100
- if (NIL_P(paramobj)) { paramobj = rb_ary_new2(0) ; }
102
+ if (NIL_P(paramobj)) { paramobj = rb_ary_new2(0L) ; }
101
103
 
102
104
  /* handle hashes as arguments. */
103
105
  if(T_HASH == TYPE(paramobj)) {
@@ -124,19 +126,129 @@ static VALUE transform(int argc, VALUE* argv, VALUE self)
124
126
 
125
127
  if (!result) rb_raise(rb_eRuntimeError, "could not perform xslt transform on document");
126
128
 
127
- return Nokogiri_wrap_xml_document(0, result) ;
129
+ return Nokogiri_wrap_xml_document((VALUE)0, result) ;
130
+ }
131
+
132
+ static void method_caller(xmlXPathParserContextPtr ctxt, int nargs)
133
+ {
134
+ const xmlChar * function;
135
+ const xmlChar * functionURI;
136
+ size_t i, count;
137
+
138
+ xsltTransformContextPtr transform;
139
+ xmlXPathObjectPtr xpath;
140
+ VALUE obj;
141
+ VALUE *args;
142
+
143
+ transform = xsltXPathGetTransformContext(ctxt);
144
+
145
+ function = ctxt->context->function;
146
+ functionURI = ctxt->context->functionURI;
147
+ obj = (VALUE)xsltGetExtData(transform, functionURI);
148
+
149
+ count = (size_t)ctxt->valueNr;
150
+ args = calloc(count, sizeof(VALUE *));
151
+
152
+ for(i = 0; i < count; i++) {
153
+ VALUE thing;
154
+
155
+ xpath = valuePop(ctxt);
156
+ switch(xpath->type) {
157
+ case XPATH_STRING:
158
+ thing = NOKOGIRI_STR_NEW2(xpath->stringval);
159
+ break;
160
+ case XPATH_NODESET:
161
+ if(NULL == xpath->nodesetval) {
162
+ thing = Nokogiri_wrap_xml_node_set(
163
+ xmlXPathNodeSetCreate(NULL),
164
+ DOC_RUBY_OBJECT(ctxt->context->doc));
165
+ } else {
166
+ thing = Nokogiri_wrap_xml_node_set(xpath->nodesetval,
167
+ DOC_RUBY_OBJECT(ctxt->context->doc));
168
+ }
169
+ break;
170
+ default:
171
+ rb_raise(rb_eRuntimeError, "do not handle type: %d", xpath->type);
172
+ }
173
+ args[i] = thing;
174
+ }
175
+ VALUE result = rb_funcall3(obj, rb_intern((const char *)function), (int)count, args);
176
+ switch(TYPE(result)) {
177
+ case T_FLOAT:
178
+ case T_BIGNUM:
179
+ case T_FIXNUM:
180
+ xmlXPathReturnNumber(ctxt, NUM2DBL(result));
181
+ break;
182
+ case T_STRING:
183
+ xmlXPathReturnString(
184
+ ctxt,
185
+ xmlStrdup((xmlChar *)StringValuePtr(result))
186
+ );
187
+ break;
188
+ case T_TRUE:
189
+ xmlXPathReturnTrue(ctxt);
190
+ break;
191
+ case T_FALSE:
192
+ xmlXPathReturnFalse(ctxt);
193
+ break;
194
+ case T_NIL:
195
+ break;
196
+ default:
197
+ rb_raise(rb_eRuntimeError, "Invalid return type");
198
+ }
199
+ }
200
+
201
+ static void * initFunc(xsltTransformContextPtr ctxt, const xmlChar *uri)
202
+ {
203
+ VALUE modules = rb_iv_get(xslt, "@modules");
204
+ VALUE obj = rb_hash_aref(modules, rb_str_new2((const char *)uri));
205
+ VALUE args = { Qfalse };
206
+ VALUE methods = rb_funcall(obj, rb_intern("instance_methods"), 1, args);
207
+ int i;
208
+
209
+ for(i = 0; i < RARRAY_LEN(methods); i++) {
210
+ VALUE method_name = rb_obj_as_string(RARRAY_PTR(methods)[i]);
211
+ xsltRegisterExtFunction(ctxt,
212
+ (unsigned char *)StringValuePtr(method_name), uri, method_caller);
213
+ }
214
+
215
+ return (void *)rb_class_new_instance(0, NULL, obj);
216
+ }
217
+
218
+ static void shutdownFunc(xsltTransformContextPtr ctxt,
219
+ const xmlChar *uri, void *data)
220
+ {
221
+ }
222
+
223
+ /*
224
+ * call-seq:
225
+ * register(uri, custom_handler_class)
226
+ *
227
+ * Register a class that implements custom XLST transformation functions.
228
+ */
229
+ static VALUE registr(VALUE self, VALUE uri, VALUE obj)
230
+ {
231
+ VALUE modules = rb_iv_get(self, "@modules");
232
+ if(NIL_P(modules)) rb_raise(rb_eRuntimeError, "wtf! @modules isn't set");
233
+
234
+ rb_hash_aset(modules, uri, obj);
235
+ xsltRegisterExtModule((unsigned char *)StringValuePtr(uri), initFunc, shutdownFunc);
236
+ return self;
128
237
  }
129
238
 
130
239
  VALUE cNokogiriXsltStylesheet ;
131
240
  void init_xslt_stylesheet()
132
241
  {
133
242
  VALUE nokogiri = rb_define_module("Nokogiri");
134
- VALUE xslt = rb_define_module_under(nokogiri, "XSLT");
243
+ xslt = rb_define_module_under(nokogiri, "XSLT");
135
244
  VALUE klass = rb_define_class_under(xslt, "Stylesheet", rb_cObject);
136
245
 
246
+ rb_iv_set(xslt, "@modules", rb_hash_new());
247
+
137
248
  cNokogiriXsltStylesheet = klass;
138
249
 
139
250
  rb_define_singleton_method(klass, "parse_stylesheet_doc", parse_stylesheet_doc, 1);
251
+ rb_define_singleton_method(xslt, "register", registr, 2);
140
252
  rb_define_method(klass, "serialize", serialize, 1);
141
253
  rb_define_method(klass, "transform", transform, -1);
142
254
  }