From 7fc970b217c734f26f3a972ef0fbce42530b6640 Mon Sep 17 00:00:00 2001 From: Jim Menard Date: Fri, 9 Jan 2009 17:27:31 -0500 Subject: [PATCH] Extracted XML-to-Ruby conversion. Created validate script for mongo-qa project. --- bin/validate | 15 ++++++ lib/mongo/util/xml_to_ruby.rb | 96 +++++++++++++++++++++++++++++++++++ tests/test_round_trip.rb | 78 +++------------------------- 3 files changed, 117 insertions(+), 72 deletions(-) create mode 100755 bin/validate create mode 100644 lib/mongo/util/xml_to_ruby.rb diff --git a/bin/validate b/bin/validate new file mode 100755 index 0000000..2c5d8ba --- /dev/null +++ b/bin/validate @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +# +# Reads a .xson file (XML that describes a Mongo-type document) from stdin and +# writes the corresponding BSON to stdout. +# +# This script is used by the mongo-qa project +# (http://github.com/mongodb/mongo-qa). + +$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib') +require 'mongo' +require 'mongo/util/xml_to_ruby' + +obj = XMLToRuby.new.xml_to_ruby($stdin) +bson = BSON.new.serialize(obj).to_a +bson.each { |b| putc(b) } diff --git a/lib/mongo/util/xml_to_ruby.rb b/lib/mongo/util/xml_to_ruby.rb new file mode 100644 index 0000000..18446ee --- /dev/null +++ b/lib/mongo/util/xml_to_ruby.rb @@ -0,0 +1,96 @@ +# -- +# Copyright (C) 2008-2009 10gen Inc. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License, version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License +# for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# ++ + +require 'rexml/document' +require 'mongo' + +# Converts a .bson file (an XML file that describes a Mongo-type document) to +# an OrderedHash. +class XMLToRuby + + include XGen::Mongo::Driver + + def xml_to_ruby(io) + doc = REXML::Document.new(io) + doc_to_ruby(doc.root.elements['doc']) + end + + protected + + def element_to_ruby(e) + type = e.name + child = e.elements[1] + case type + when 'oid' + ObjectID.from_string(e.text) + when 'ref' + dbref_to_ruby(e.elements) + when 'int' + e.text.to_i + when 'number' + e.text.to_f + when 'string', 'code' + e.text.to_s + when 'boolean' + e.text.to_s == 'true' + when 'array' + array_to_ruby(e.elements) + when 'date' + Time.at(e.text.to_f / 1000.0) + when 'regex' + regex_to_ruby(e.elements) + when 'null' + nil + when 'doc' + doc_to_ruby(e) + else + raise "Unknown type #{type} in element with name #{e.attributes['name']}" + end + end + + def doc_to_ruby(element) + oh = OrderedHash.new + element.elements.each { |e| oh[e.attributes['name']] = element_to_ruby(e) } + oh + end + + def array_to_ruby(elements) + a = [] + elements.each { |e| + index_str = e.attributes['name'] + a[index_str.to_i] = element_to_ruby(e) + } + a + end + + def regex_to_ruby(elements) + pattern = elements['pattern'].text + options_str = elements['options'].text || '' + + options = 0 + options |= Regexp::IGNORECASE if options_str.include?('i') + options |= Regexp::MULTILINE if options_str.include?('m') + options |= Regexp::EXTENDED if options_str.include?('x') + Regexp.new(pattern, options) + end + + def dbref_to_ruby(elements) + ns = elements['ns'].text + oid_str = elements['oid'].text + DBRef.new(nil, nil, nil, ns, ObjectID.from_string(oid_str)) + end + +end diff --git a/tests/test_round_trip.rb b/tests/test_round_trip.rb index 9fbd3b7..03ab613 100644 --- a/tests/test_round_trip.rb +++ b/tests/test_round_trip.rb @@ -1,7 +1,7 @@ HERE = File.dirname(__FILE__) $LOAD_PATH[0,0] = File.join(HERE, '..', 'lib') require 'mongo' -require 'rexml/document' +require 'mongo/util/xml_to_ruby' require 'test/unit' # For each xml/bson file in the data subdirectory, we turn the XML into an @@ -16,80 +16,14 @@ class RoundTripTest < Test::Unit::TestCase unless @@ruby names = Dir[File.join(HERE, 'data', '*.xml')].collect {|f| File.basename(f).sub(/\.xml$/, '') } @@ruby = {} - names.each { |name| @@ruby[name] = xml_to_ruby(name) } + names.each { |name| + File.open(File.join(HERE, 'data', "#{name}.xml")) { |f| + @@ruby[name] = XMLToRuby.new.xml_to_ruby(f) + } + } end end - def xml_to_ruby(name) - File.open(File.join(HERE, 'data', "#{name}.xml")) { |f| - doc = REXML::Document.new(f) - doc_to_ruby(doc.root.elements['doc']) - } - end - - def element_to_ruby(e) - type = e.name - child = e.elements[1] - case type - when 'oid' - ObjectID.from_string(e.text) - when 'ref' - dbref_to_ruby(e.elements) - when 'int' - e.text.to_i - when 'number' - e.text.to_f - when 'string', 'code' - e.text.to_s - when 'boolean' - e.text.to_s == 'true' - when 'array' - array_to_ruby(e.elements) - when 'date' - Time.at(e.text.to_f / 1000.0) - when 'regex' - regex_to_ruby(e.elements) - when 'null' - nil - when 'doc' - doc_to_ruby(e) - else - raise "Unknown type #{type} in element with name #{e.attributes['name']}" - end - end - - def doc_to_ruby(element) - oh = OrderedHash.new - element.elements.each { |e| oh[e.attributes['name']] = element_to_ruby(e) } - oh - end - - def array_to_ruby(elements) - a = [] - elements.each { |e| - index_str = e.attributes['name'] - a[index_str.to_i] = element_to_ruby(e) - } - a - end - - def regex_to_ruby(elements) - pattern = elements['pattern'].text - options_str = elements['options'].text || '' - - options = 0 - options |= Regexp::IGNORECASE if options_str.include?('i') - options |= Regexp::MULTILINE if options_str.include?('m') - options |= Regexp::EXTENDED if options_str.include?('x') - Regexp.new(pattern, options) - end - - def dbref_to_ruby(elements) - ns = elements['ns'].text - oid_str = elements['oid'].text - DBRef.new(nil, nil, nil, ns, ObjectID.from_string(oid_str)) - end - # Round-trip comparisons of Ruby-to-BSON and back. # * Take the objects that were read from XML # * Turn them into BSON bytes