Skip to content
Browse files

ready for release

  • Loading branch information...
1 parent 5a51083 commit ccac6e9e7caee85218a618d96d3501063288730d Peter Ohler committed
Showing with 253 additions and 153 deletions.
  1. +2 −0 README.md
  2. +2 −0 build_test.sh
  3. +2 −0 ext/ox/extconf.rb
  4. +30 −28 ext/ox/ox.c
  5. +2 −0 ext/ox/ox.h
  6. +51 −9 ext/ox/sax.c
  7. +1 −2 lib/ox.rb
  8. +0 −24 notes
  9. +1 −1 ox.gemspec
  10. +30 −88 test/perf_sax.rb
  11. +115 −0 test/sax_test.rb
  12. +1 −1 test/weather/HellsGate_2012_weather.xml
  13. +7 −0 test/weather/README
  14. +9 −0 test/weather/perf_weather.rb
View
2 README.md
@@ -52,6 +52,8 @@ A fast XML parser and Object marshaller as a Ruby gem.
- Added an example that demonstrates using several parsing techniques with different parsers. The example is in the test/weather directory.
+ - The SAX parser was optimized slightly for StringIO input.
+
## <a name="description">Description</a>
Optimized XML (Ox), as the name implies was written to provide speed optimized
View
2 build_test.sh
@@ -1,5 +1,7 @@
#!/bin/sh
+export RBXOPT=-X19
+
for ruby in \
1.8.7-p358\
1.9.2-p290\
View
2 ext/ox/extconf.rb
@@ -35,3 +35,5 @@
$CPPFLAGS += ' -Wall'
#puts "*** $CPPFLAGS: #{$CPPFLAGS}"
create_makefile(extension_name)
+
+%x{make clean}
View
58 ext/ox/ox.c
@@ -76,6 +76,7 @@ ID ox_parse_id;
ID ox_read_id;
ID ox_readpartial_id;
ID ox_start_element_id;
+ID ox_string_id;
ID ox_text_id;
ID ox_to_c_id;
ID ox_to_s_id;
@@ -99,6 +100,7 @@ VALUE ox_bag_clas;
VALUE ox_struct_class;
VALUE ox_time_class;
VALUE ox_date_class;
+VALUE ox_stringio_class;
Cache ox_symbol_cache = 0;
Cache ox_class_cache = 0;
@@ -657,10 +659,7 @@ cache8_test(VALUE self) {
}
void Init_ox() {
- VALUE keep = Qnil;
-
Ox = rb_define_module("Ox");
- keep = rb_cv_get(Ox, "@@keep"); // needed to stop GC from deleting and reusing VALUEs
rb_define_module_function(Ox, "default_options", get_def_opts, 0);
rb_define_module_function(Ox, "default_options=", set_def_opts, 1);
@@ -677,6 +676,8 @@ void Init_ox() {
rb_define_module_function(Ox, "to_file", to_file, -1);
rb_require("time");
+ rb_require("date");
+ rb_require("stringio");
ox_at_id = rb_intern("at");
ox_at_value_id = rb_intern("@value");
@@ -706,6 +707,7 @@ void Init_ox() {
ox_readpartial_id = rb_intern("readpartial");
ox_read_id = rb_intern("read");
ox_start_element_id = rb_intern("start_element");
+ ox_string_id = rb_intern("string");
ox_text_id = rb_intern("text");
ox_value_id = rb_intern("value");
ox_to_c_id = rb_intern("to_c");
@@ -718,32 +720,32 @@ void Init_ox() {
ox_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
ox_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
ox_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
+ ox_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
+
+ ox_encoding_sym = ID2SYM(rb_intern("encoding")); rb_gc_register_address(&ox_encoding_sym);
+ indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&indent_sym);
+ xsd_date_sym = ID2SYM(rb_intern("xsd_date")); rb_gc_register_address(&xsd_date_sym);
+ opt_format_sym = ID2SYM(rb_intern("opt_format")); rb_gc_register_address(&opt_format_sym);
+ mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
+ auto_sym = ID2SYM(rb_intern("auto")); rb_gc_register_address(&auto_sym);
+ optimized_sym = ID2SYM(rb_intern("optimized")); rb_gc_register_address(&optimized_sym);
+ object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
+ circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
+ generic_sym = ID2SYM(rb_intern("generic")); rb_gc_register_address(&generic_sym);
+ limited_sym = ID2SYM(rb_intern("limited")); rb_gc_register_address(&limited_sym);
+ trace_sym = ID2SYM(rb_intern("trace")); rb_gc_register_address(&trace_sym);
+ effort_sym = ID2SYM(rb_intern("effort")); rb_gc_register_address(&effort_sym);
+ strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
+ tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_gc_register_address(&tolerant_sym);
+ auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
+ with_dtd_sym = ID2SYM(rb_intern("with_dtd")); rb_gc_register_address(&with_dtd_sym);
+ with_instruct_sym = ID2SYM(rb_intern("with_instructions")); rb_gc_register_address(&with_instruct_sym);
+ with_xml_sym = ID2SYM(rb_intern("with_xml")); rb_gc_register_address(&with_xml_sym);
+ convert_special_sym = ID2SYM(rb_intern("convert_special")); rb_gc_register_address(&convert_special_sym);
+
+ ox_empty_string = rb_str_new2(""); rb_gc_register_address(&ox_empty_string);
+ ox_zero_fixnum = INT2NUM(0); rb_gc_register_address(&ox_zero_fixnum);
- ox_encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, ox_encoding_sym);
- indent_sym = ID2SYM(rb_intern("indent")); rb_ary_push(keep, indent_sym);
- xsd_date_sym = ID2SYM(rb_intern("xsd_date")); rb_ary_push(keep, xsd_date_sym);
- opt_format_sym = ID2SYM(rb_intern("opt_format")); rb_ary_push(keep, opt_format_sym);
- mode_sym = ID2SYM(rb_intern("mode")); rb_ary_push(keep, mode_sym);
- auto_sym = ID2SYM(rb_intern("auto")); rb_ary_push(keep, auto_sym);
- optimized_sym = ID2SYM(rb_intern("optimized")); rb_ary_push(keep, optimized_sym);
- object_sym = ID2SYM(rb_intern("object")); rb_ary_push(keep, object_sym);
- circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
- generic_sym = ID2SYM(rb_intern("generic")); rb_ary_push(keep, generic_sym);
- limited_sym = ID2SYM(rb_intern("limited")); rb_ary_push(keep, limited_sym);
- trace_sym = ID2SYM(rb_intern("trace")); rb_ary_push(keep, trace_sym);
- effort_sym = ID2SYM(rb_intern("effort")); rb_ary_push(keep, effort_sym);
- strict_sym = ID2SYM(rb_intern("strict")); rb_ary_push(keep, strict_sym);
- tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_ary_push(keep, tolerant_sym);
- auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_ary_push(keep, auto_define_sym);
- with_dtd_sym = ID2SYM(rb_intern("with_dtd")); rb_ary_push(keep, with_dtd_sym);
- with_instruct_sym = ID2SYM(rb_intern("with_instructions")); rb_ary_push(keep, with_instruct_sym);
- with_xml_sym = ID2SYM(rb_intern("with_xml")); rb_ary_push(keep, with_xml_sym);
- convert_special_sym = ID2SYM(rb_intern("convert_special")); rb_ary_push(keep, convert_special_sym);
-
- ox_empty_string = rb_str_new2(""); rb_ary_push(keep, ox_empty_string);
- ox_zero_fixnum = INT2NUM(0); rb_ary_push(keep, ox_zero_fixnum);
-
- //rb_require("node"); // generic xml node classes
ox_document_clas = rb_const_get_at(Ox, rb_intern("Document"));
ox_element_clas = rb_const_get_at(Ox, rb_intern("Element"));
ox_comment_clas = rb_const_get_at(Ox, rb_intern("Comment"));
View
2 ext/ox/ox.h
@@ -248,6 +248,7 @@ extern ID ox_parse_id;
extern ID ox_read_id;
extern ID ox_readpartial_id;
extern ID ox_start_element_id;
+extern ID ox_string_id;
extern ID ox_text_id;
extern ID ox_to_c_id;
extern ID ox_to_s_id;
@@ -262,6 +263,7 @@ extern VALUE ox_empty_string;
extern VALUE ox_encoding_sym;
extern VALUE ox_struct_class;
extern VALUE ox_time_class;
+extern VALUE ox_stringio_class;
extern VALUE ox_zero_fixnum;
extern VALUE ox_document_clas;
View
60 ext/ox/sax.c
@@ -55,8 +55,9 @@ typedef struct _SaxDrive {
int (*read_func)(struct _SaxDrive *dr);
int convert_special;
union {
- int fd;
- VALUE io;
+ int fd;
+ VALUE io;
+ const char *in_str;
};
int has_instruct;
int has_attr;
@@ -99,6 +100,7 @@ static int read_from_io(SaxDrive dr);
static int read_from_fd(SaxDrive dr);
#endif
static int read_from_io_partial(SaxDrive dr);
+static int read_from_str(SaxDrive dr);
static VALUE sax_value_class;
@@ -244,7 +246,12 @@ respond_to(VALUE obj, ID method) {
static void
sax_drive_init(SaxDrive dr, VALUE handler, VALUE io, int convert) {
- if (rb_respond_to(io, ox_readpartial_id)) {
+ if (ox_stringio_class == rb_obj_class(io)) {
+ VALUE s = rb_funcall2(io, ox_string_id, 0, 0);
+
+ dr->read_func = read_from_str;
+ dr->in_str = StringValuePtr(s);
+ } else if (rb_respond_to(io, ox_readpartial_id)) {
#ifdef JRUBY_RUBY
dr->read_func = read_from_io_partial;
dr->io = io;
@@ -919,6 +926,25 @@ read_from_fd(SaxDrive dr) {
#endif
static int
+read_from_str(SaxDrive dr) {
+ size_t max = dr->buf_end - dr->cur - 1;
+ char *s;
+ long cnt;
+
+ if ('\0' == *dr->in_str) {
+ // done
+ return -1;
+ }
+ s = stpncpy(dr->cur, dr->in_str, max);
+ *s = '\0';
+ cnt = s - dr->cur;
+ dr->in_str += cnt;
+ dr->read_end = dr->cur + cnt;
+
+ return 0;
+}
+
+static int
collapse_special(char *str) {
char *s = str;
char *b = str;
@@ -997,7 +1023,7 @@ parse_double_time(const char *text) {
}
v2 = 10 * v2 + (long)(c - '0');
}
- for (; text - dot <= 6; text++) {
+ for (; text - dot <= 9; text++) {
v2 *= 10;
}
#if HAS_NANO_TIME
@@ -1015,7 +1041,6 @@ typedef struct _Tp {
static VALUE
parse_xsd_time(const char *text) {
- const char *start = text;
long cargs[10];
long *cp = cargs;
long v;
@@ -1023,7 +1048,7 @@ parse_xsd_time(const char *text) {
char c = '\0';
struct _Tp tpa[10] = { { 4, '-', '-' },
{ 2, '-', '-' },
- { 2, 'T', 'T' },
+ { 2, 'T', ' ' },
{ 2, ':', ':' },
{ 2, ':', ':' },
{ 2, '.', '.' },
@@ -1042,7 +1067,7 @@ parse_xsd_time(const char *text) {
if ('\0' == c || tp->end == c || tp->alt == c) {
break;
}
- rb_raise(rb_eArgError, "Not a valid Time (%s).\n", start);
+ return Qnil;
}
v = 10 * v + (long)(c - '0');
}
@@ -1051,7 +1076,7 @@ parse_xsd_time(const char *text) {
}
c = *text++;
if (tp->end != c && tp->alt != c) {
- rb_raise(rb_eArgError, "Not a valid Time (%s).\n", start);
+ return Qnil;
}
*cp++ = v;
}
@@ -1073,6 +1098,9 @@ sax_value_as_s(VALUE self) {
SaxDrive dr = DATA_PTR(self);
VALUE rs;
+ if ('\0' == *dr->str) {
+ return Qnil;
+ }
if (dr->convert_special) {
if (0 != collapse_special(dr->str) && 0 != strchr(dr->str, '&')) {
sax_drive_error(dr, "invalid format, special character does not end with a semicolon", 0);
@@ -1091,12 +1119,20 @@ static VALUE
sax_value_as_sym(VALUE self) {
SaxDrive dr = DATA_PTR(self);
+ if ('\0' == *dr->str) {
+ return Qnil;
+ }
return str2sym(dr->str, dr);
}
static VALUE
sax_value_as_f(VALUE self) {
- return rb_float_new(strtod(((SaxDrive)DATA_PTR(self))->str, 0));
+ SaxDrive dr = DATA_PTR(self);
+
+ if ('\0' == *dr->str) {
+ return Qnil;
+ }
+ return rb_float_new(strtod(dr->str, 0));
}
static VALUE
@@ -1106,6 +1142,9 @@ sax_value_as_i(VALUE self) {
long n = 0;
int neg = 0;
+ if ('\0' == *s) {
+ return Qnil;
+ }
if ('-' == *s) {
neg = 1;
s++;
@@ -1131,6 +1170,9 @@ sax_value_as_time(VALUE self) {
const char *str = dr->str;
VALUE t;
+ if ('\0' == *str) {
+ return Qnil;
+ }
if (Qnil == (t = parse_double_time(str)) &&
Qnil == (t = parse_xsd_time(str))) {
VALUE args[1];
View
3 lib/ox.rb
@@ -83,8 +83,7 @@
# doc2 = Ox.parse(xml)
# puts "Same? #{doc == doc2}"
module Ox
- private
- @@keep = []
+
end
require 'ox/version'
View
24 notes
@@ -5,30 +5,6 @@
- todo
- - handle string io as string
- - sax_test
- - value() callback
- - attr_value() callback
-
- - update perf tests to use perf
- - perf_sax
-
- - use address instead of @keep for avoiding gc
- - rb_gc_register_address(VALUE *)
- - rb_gc_unregister_address(VALUE *)
-
- - create weather example
- - weather dir (Weather module)
- - different class for each parser approach
- - wox.rb
- + wsax
- - wno.rb
- + wnosax
- - wnoread
- - wlx
- + wlxsax
- - wfox - fast ox
-
- Ox::Doc or Ox::Fast or Ox::Fast::Doc (fast.c)
- same as Oj but with attributes
View
2 ox.gemspec
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
s.authors = "Peter Ohler"
s.date = Date.today.to_s
s.email = "peter@ohler.com"
- s.homepage = "https://github.com/ohler55/ox"
+ s.homepage = "http://www.ohler.com/ox"
s.summary = "A fast XML parser and object serializer."
s.description = %{A fast XML parser and object serializer that uses only standard C lib.
View
118 test/perf_sax.rb
@@ -14,6 +14,7 @@
require 'optparse'
require 'ox'
+require 'perf'
require 'files'
begin
require 'nokogiri'
@@ -30,11 +31,13 @@
$filename = nil # nil indicates new file names perf.xml will be created and used
$filesize = 1000 # KBytes
$iter = 100
+$strio = false
opts = OptionParser.new
opts.on("-v", "increase verbosity") { $verbose += 1 }
opts.on("-x", "ox only") { $ox_only = true }
opts.on("-a", "all callbacks") { $all_cbs = true }
+opts.on("-z", "use StringIO instead of file") { $strio = true }
opts.on("-f", "--file [String]", String, "filename") { |f| $filename = f }
opts.on("-i", "--iterations [Int]", Integer, "iterations") { |i| $iter = i }
opts.on("-s", "--size [Int]", Integer, "file size in KBytes") { |s| $filesize = s }
@@ -42,9 +45,6 @@
rest = opts.parse(ARGV)
$xml_str = nil
-$ox_time = 0
-$no_time = 0
-$lx_time = 0
# size is in Kbytes
def create_file(filename, size)
@@ -134,102 +134,44 @@ def on_start_element_ns(name, attributes, prefix, uri, namespaces); end
end
end
-def perf_stringio()
- start = Time.now
- handler = $all_cbs ? OxAllSax.new() : OxSax.new()
- $iter.times do
- input = StringIO.new($xml_str)
- Ox.sax_parse(handler, input)
- input.close
- end
- $ox_time = Time.now - start
- puts "StringIO SAX parsing #{$iter} times with Ox took #{$ox_time} seconds."
-
- return if $ox_only
-
- unless defined?(::Nokogiri).nil?
- handler = Nokogiri::XML::SAX::Parser.new($all_cbs ? NoAllSax.new() : NoSax.new())
- start = Time.now
- $iter.times do
- input = StringIO.new($xml_str)
- handler.parse(input)
- input.close
- end
- $no_time = Time.now - start
- puts "StringIO SAX parsing #{$iter} times with Nokogiri took #{$no_time} seconds."
- end
-
- unless defined?(::LibXML).nil?
- start = Time.now
- $iter.times do
- input = StringIO.new($xml_str)
- parser = LibXML::XML::SaxParser.io(input)
- parser.callbacks = $all_cbs ? LxAllSax.new() : LxSax.new()
- parser.parse
- input.close
- end
- $lx_time = Time.now - start
- puts "StringIO SAX parsing #{$iter} times with LibXML took #{$lx_time} seconds."
- end
-
- puts "\n"
- puts ">>> Ox is %0.1f faster than Nokogiri SAX parsing using StringIO." % [$no_time/$ox_time] unless defined?(::Nokogiri).nil?
- puts ">>> Ox is %0.1f faster than LibXML SAX parsing using StringIO." % [$lx_time/$ox_time] unless defined?(::LibXML).nil?
- puts "\n"
+if $filename.nil?
+ create_file('perf.xml', $filesize)
+ $filename = 'perf.xml'
end
+$xml_str = File.read($filename)
-def perf_fileio()
- puts "\n"
- puts "A #{$filesize} KByte XML file was parsed #{$iter} times for this test."
- puts "\n"
- start = Time.now
- handler = $all_cbs ? OxAllSax.new() : OxSax.new()
- $iter.times do
- input = IO.open(IO.sysopen($filename))
- Ox.sax_parse(handler, input)
- input.close
- end
- $ox_time = Time.now - start
- puts "File IO SAX parsing #{$iter} times with Ox took #{$ox_time} seconds."
+puts "A #{$filesize} KByte XML file was parsed #{$iter} times for this test."
- return if $ox_only
+$handler = nil
+perf = Perf.new
+perf.add('Ox::Sax', 'sax_parse') {
+ input = $strio ? StringIO.new($xml_str) : IO.open(IO.sysopen($filename))
+ Ox.sax_parse($handler, input)
+ input.close
+}
+perf.before('Ox::Sax') { $handler = $all_cbs ? OxAllSax.new() : OxSax.new() }
+
+unless $ox_only
unless defined?(::Nokogiri).nil?
- handler = Nokogiri::XML::SAX::Parser.new($all_cbs ? NoAllSax.new() : NoSax.new())
- start = Time.now
- $iter.times do
- input = IO.open(IO.sysopen($filename))
- handler.parse(input)
+ perf.add('Nokogiri::XML::Sax', 'parse') {
+ input = $strio ? StringIO.new($xml_str) : IO.open(IO.sysopen($filename))
+ $handler.parse(input)
input.close
- end
- $no_time = Time.now - start
- puts "File IO SAX parsing #{$iter} times with Nokogiri took #{$no_time} seconds."
+ }
+ perf.before('Nokogiri::XML::Sax') { $handler = Nokogiri::XML::SAX::Parser.new($all_cbs ? NoAllSax.new() : NoSax.new()) }
end
unless defined?(::LibXML).nil?
- start = Time.now
- $iter.times do
- input = IO.open(IO.sysopen($filename))
+ perf.add('LibXML::XML::Sax', 'parse') {
+ input = $strio ? StringIO.new($xml_str) : IO.open(IO.sysopen($filename))
parser = LibXML::XML::SaxParser.io(input)
- parser.callbacks = $all_cbs ? LxAllSax.new() : LxSax.new()
- parser.parse
+ parser.callbacks = $handler
+ parser.parse()
input.close
- end
- $lx_time = Time.now - start
- puts "File IO SAX parsing #{$iter} times with LibXML took #{$lx_time} seconds."
+ }
+ perf.before('LibXML::XML::Sax') { $handler = $all_cbs ? LxAllSax.new() : LxSax.new() }
end
-
- puts "\n"
- puts ">>> Ox is %0.1f faster than Nokogiri SAX parsing using file IO." % [$no_time/$ox_time] unless defined?(::Nokogiri).nil?
- puts ">>> Ox is %0.1f faster than LibXML SAX parsing using file IO." % [$lx_time/$ox_time] unless defined?(::LibXML).nil?
- puts "\n"
end
-if $filename.nil?
- create_file('perf.xml', $filesize)
- $filename = 'perf.xml'
-end
-$xml_str = File.read($filename)
-
-# perf_stringio()
-perf_fileio()
+perf.run($iter)
View
115 test/sax_test.rb
@@ -63,6 +63,25 @@ def error(message, line, column)
end
end
+class TypeSax < ::Ox::Sax
+ attr_accessor :item
+ # method to call on the Ox::Sax::Value Object
+ attr_accessor :type
+
+ def initialize(type)
+ @item = nil
+ @type = type
+ end
+
+ def attr_value(name, value)
+ @item = value.send(name)
+ end
+
+ def value(value)
+ @item = value.send(@type)
+ end
+end
+
class Func < ::Test::Unit::TestCase
def test_sax_io_pipe
@@ -463,4 +482,100 @@ def test_sax_full_encoding
end
end
+ def test_sax_value_fixnum
+ handler = TypeSax.new(:as_i)
+ Ox.sax_parse(handler, StringIO.new(%{<top>7</top>}))
+ assert_equal(7, handler.item)
+ end
+
+ def test_sax_value_string
+ handler = TypeSax.new(:as_s)
+ Ox.sax_parse(handler, StringIO.new(%{<top>cheese</top>}))
+ assert_equal('cheese', handler.item)
+ end
+
+ def test_sax_value_float
+ handler = TypeSax.new(:as_f)
+ Ox.sax_parse(handler, StringIO.new(%{<top>7</top>}))
+ assert_equal(7.0, handler.item)
+ end
+
+ def test_sax_value_sym
+ handler = TypeSax.new(:as_sym)
+ Ox.sax_parse(handler, StringIO.new(%{<top>cheese</top>}))
+ assert_equal(:cheese, handler.item)
+ end
+
+ def test_sax_value_bool
+ handler = TypeSax.new(:as_bool)
+ Ox.sax_parse(handler, StringIO.new(%{<top>true</top>}))
+ assert_equal(true, handler.item)
+ end
+
+ def test_sax_value_nil
+ handler = TypeSax.new(:as_i)
+ Ox.sax_parse(handler, StringIO.new(%{<top></top>}))
+ assert_equal(nil, handler.item)
+ end
+
+ def test_sax_value_time
+ t = Time.local(2012, 1, 5, 10, 20, 30)
+ handler = TypeSax.new(:as_time)
+ Ox.sax_parse(handler, StringIO.new(%{<top>#{t}</top>}))
+ assert_equal(t.sec, handler.item.sec)
+ assert_equal(t.usec, handler.item.usec)
+ t = Time.now
+ Ox.sax_parse(handler, StringIO.new(%{<top>%d.%06d</top>} % [t.sec, t.usec]))
+ assert_equal(t.sec, handler.item.sec)
+ assert_equal(t.usec, handler.item.usec)
+ end
+
+ def test_sax_attr_value_fixnum
+ handler = TypeSax.new(nil)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_i="7"/>}))
+ assert_equal(7, handler.item)
+ end
+
+ def test_sax_attr_value_string
+ handler = TypeSax.new(nil)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_s="cheese"/>}))
+ assert_equal('cheese', handler.item)
+ end
+
+ def test_sax_attr_value_float
+ handler = TypeSax.new(nil)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_f="7"/>}))
+ assert_equal(7.0, handler.item)
+ end
+
+ def test_sax_attr_value_sym
+ handler = TypeSax.new(nil)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_sym="cheese"/>}))
+ assert_equal(:cheese, handler.item)
+ end
+
+ def test_sax_attr_value_bool
+ handler = TypeSax.new(nil)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_bool="true"/>}))
+ assert_equal(true, handler.item)
+ end
+
+ def test_sax_attr_value_nil
+ handler = TypeSax.new(nil)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_i=""/>}))
+ assert_equal(nil, handler.item)
+ end
+
+ def test_sax_attr_value_time
+ t = Time.local(2012, 1, 5, 10, 20, 30)
+ handler = TypeSax.new(nil)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_time="#{t}"/>}))
+ assert_equal(t.sec, handler.item.sec)
+ assert_equal(t.usec, handler.item.usec)
+ t = Time.local(2012, 1, 5, 10, 20, 30.123456)
+ Ox.sax_parse(handler, StringIO.new(%{<top as_time="%d.%06d"/>} % [t.sec, t.usec]))
+ assert_equal(t.sec, handler.item.sec)
+ assert_equal(t.usec, handler.item.usec)
+ end
+
end
View
2 test/weather/HellsGate_2012_weather.xml
@@ -8,7 +8,7 @@
<Author>Ohler Peter</Author>
<LastAuthor>Ohler Peter</LastAuthor>
<Created>2012-04-03T03:05:35Z</Created>
- <Company>KVH</Company>
+ <Company>Home</Company>
<Version>14.0</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
View
7 test/weather/README
@@ -0,0 +1,7 @@
+
+To run the example, cd to the ox/test directory and execute the command
+
+weather/perf_weather.rb
+
+Browse the sample parser files in the ox/test/weather directory to see how
+each parser is used.
View
9 test/weather/perf_weather.rb
@@ -1,5 +1,14 @@
#!/usr/bin/env ruby -wW1
+# This example uses SAX and DOM approaches to parsing XML files. It is also
+# used to compare the differences in performance and code structure for
+# multiple XML parsers.
+#
+# Weather files taken from the British of Columbia are used as a source for
+# capturing the high temperatures over some period of time. The files are
+# loaded into a spreadsheet and then dumped as XML. The high temperatures for
+# each time is stored in a Hash but no further action is taken on the data.
+
$: << File.dirname(__FILE__)
$: << File.join(File.dirname(__FILE__), '..')
$: << File.join(File.dirname(__FILE__), '../../lib')

0 comments on commit ccac6e9

Please sign in to comment.
Something went wrong with that request. Please try again.