rxsd 0.2 → 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc ADDED
@@ -0,0 +1,56 @@
1
+ == RXSD - XSD / Ruby Translator
2
+
3
+ Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+
5
+ RXSD is made available under the GNU LESSER GENERAL PUBLIC LICENSE
6
+ as published by the Free Software Foundation, either version 3
7
+ of the License, or (at your option) any later version.
8
+
9
+ == Info
10
+ RXSD is a library that translates XSD XML Schema Definitions into Ruby Classes
11
+ on the fly. It is able to read XSD resources and use them to define Ruby
12
+ classes in memory or string class definitions to be written to the filesystem
13
+
14
+ RXSD implements a full XSD parser that not only defines the various xsd schema
15
+ classes, parsing them out of a XSD file, but translates them into a
16
+ meta-class heirarchy, for use in subsequent transformations. The builder interface
17
+ can easily be extended to output any format one could want including classes
18
+ in other languages (Python, C++, Java, etc), other XML formats, etc.
19
+
20
+ RXSD also parses XML conforming to a XSD schema, and instantiates objects
21
+ corresponding to the XSD classes created. Furthermore, RXSD will work with
22
+ existing class definitions resulting in a quick way to map XSD to Ruby constructs,
23
+ letting you define the schema features that you need, and autogenerting handlers
24
+ to the others.
25
+
26
+ == Installation
27
+
28
+ To install rxsd simply run:
29
+ gem install rxsd
30
+
31
+ Source code is available via:
32
+ git clone http://github.com/movitto/rxsd
33
+
34
+ == Usage
35
+
36
+ require 'lib/rxsd'
37
+
38
+ xsd_uri = "file:///home/user/schema.xsd"
39
+ xml_uri = "file:///home/user/data.xml"
40
+
41
+ schema = RXSD::Parser.parse_xsd :uri => xsd_uri
42
+
43
+ puts "=======Classes======="
44
+ classes = schema.to :ruby_classes
45
+ puts classes.collect{ |cl| !cl.nil? ? (cl.to_s + " < " + cl.superclass.to_s) : ""}.sort.join("\n")
46
+
47
+ puts "=======Tags======="
48
+ puts schema.tags.collect { |n,cb| n + ": " + cb.to_s + ": " + (cb.nil? ? "ncb" : cb.klass_name.to_s + "-" + cb.klass.to_s) }.sort.join("\n")
49
+
50
+ puts "=======Objects======="
51
+ data = RXSD::Parser.parse_xml :uri => xml_uri
52
+ objs = data.to :ruby_objects, :schema => schema
53
+ objs.each { |obj|
54
+ puts "#{obj}"
55
+ }
56
+
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ # rxsd project Rakefile
2
+ #
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+ # Licensed under the LGPLv3+ http://www.gnu.org/licenses/lgpl.txt
5
+
6
+ require 'rake/rdoctask'
7
+ require 'spec/rake/spectask'
8
+ require 'rake/gempackagetask'
9
+
10
+
11
+ GEM_NAME="rxsd"
12
+ PKG_VERSION='0.3'
13
+
14
+ desc "Run all specs"
15
+ Spec::Rake::SpecTask.new('spec') do |t|
16
+ t.spec_files = FileList['spec/**/*_spec.rb']
17
+ end
18
+
19
+ Rake::RDocTask.new do |rd|
20
+ rd.main = "README.rdoc"
21
+ rd.rdoc_dir = "doc/site/api"
22
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
23
+ end
24
+
25
+ PKG_FILES = FileList['bin/**/*', 'lib/**/*.rb', 'COPYING', 'LICENSE', 'Rakefile', 'README.rdoc', 'spec/**/*.rb' ]
26
+
27
+ SPEC = Gem::Specification.new do |s|
28
+ s.name = GEM_NAME
29
+ s.version = PKG_VERSION
30
+ s.files = PKG_FILES
31
+
32
+ s.required_ruby_version = '>= 1.8.1'
33
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.3.3")
34
+
35
+ s.author = "Mohammed Morsi"
36
+ s.email = "movitto@yahoo.com"
37
+ s.date = %q{2010-03-18}
38
+ s.description = %q{A library to translate xsd schemas and xml implementations into ruby classes/objects}
39
+ s.summary = %q{A library to translate xsd schemas and xml implementations into ruby classes/objects}
40
+ s.homepage = %q{http://morsi.org/projects/RXSD}
41
+ end
42
+
43
+ Rake::GemPackageTask.new(SPEC) do |pkg|
44
+ pkg.need_tar = true
45
+ pkg.need_zip = true
46
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/ruby
2
+ # Simple rxsd test utility
3
+ #
4
+ # Usage rxsd-test.rb uri-to-schema uri-to-xml-instance
5
+ #
6
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
7
+ # See COPYING for the License of this software
8
+
9
+ require 'fileutils'
10
+ require 'lib/rxsd'
11
+
12
+ if ARGV.size < 2
13
+ puts "missing required arguments"
14
+ puts "usage: gen_ruby_definitions xsd_uri output_dir"
15
+ exit
16
+ end
17
+
18
+ xsd_uri = ARGV[0]
19
+ output_dir = ARGV[1]
20
+
21
+ if File.exists?(output_dir) && ! File.directory?(output_dir)
22
+ puts "#{output_dir} is not a dir, exiting"
23
+ exit
24
+ end
25
+
26
+ if !File.exists?(output_dir)
27
+ FileUtils.mkdir(output_dir)
28
+ end
29
+
30
+ schema = RXSD::Parser.parse_xsd :uri => xsd_uri
31
+ definitions = schema.to :ruby_definitions
32
+
33
+ definitions.each { |d|
34
+ d =~ /^.*class\s*([A-Za-z]*).*$/ # XXX Hacky way to get class name
35
+ cl = $1
36
+ File.write(output_dir + "/" + $1 + ".rb", d)
37
+ }
data/bin/rxsd-test.rb CHANGED
@@ -1,14 +1,16 @@
1
+ #!/usr/bin/ruby
1
2
  # Simple rxsd test utility
2
3
  #
3
4
  # Usage rxsd-test.rb uri-to-schema uri-to-xml-instance
4
5
  #
5
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
6
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
6
7
  # See COPYING for the License of this software
7
8
 
8
9
  require 'lib/rxsd'
9
10
 
10
11
  if ARGV.size < 2
11
12
  puts "missing required arguments"
13
+ puts "usage: rxsd-test xsd_uri xml_uri"
12
14
  exit
13
15
  end
14
16
 
@@ -16,15 +18,6 @@ xsd_uri = ARGV[0]
16
18
  xml_uri = ARGV[1]
17
19
 
18
20
  schema = RXSD::Parser.parse_xsd :uri => xsd_uri
19
- #def disp_child(obj)
20
- # if obj.respond_to? 'children'
21
- # puts "#{obj}"
22
- # obj.children.each { |c|
23
- # disp_child c
24
- # }
25
- # end
26
- #end
27
- #disp_child(schema)
28
21
 
29
22
  puts "=======Classes======="
30
23
  classes = schema.to :ruby_classes
data/lib/rxsd.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # include all rxsd modules
2
2
  #
3
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
4
  # See COPYING for the License of this software
5
5
 
6
6
  lib = File.dirname(__FILE__)
data/lib/rxsd/builder.rb CHANGED
@@ -2,12 +2,12 @@
2
2
  #
3
3
  # responsible for providing interface to build any output format from XSD metadata
4
4
  #
5
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
5
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
6
6
  # See COPYING for the License of this software
7
7
 
8
8
  module RXSD
9
9
 
10
- # base interface and helper methods to build classes in various formats from specified parameters
10
+ # Base interface and helper methods to build classes in various formats from xsd schemas
11
11
  class ClassBuilder
12
12
  # actual class built
13
13
  attr_accessor :klass
@@ -27,8 +27,10 @@ class ClassBuilder
27
27
  # name of the attribute which this class represents, for use in accessor construction
28
28
  attr_accessor :attribute_name
29
29
 
30
- # create a new class builder w/ specified args
30
+ # Create a new class builder w/ specified args.
31
31
  def initialize(args = {})
32
+ @klass = @klass_name = @attribute_name = @associated_builder = @base_builder = nil
33
+
32
34
  if args.has_key? :builder
33
35
  @klass = args[:builder].klass
34
36
  @klass_name = args[:builder].klass_name
@@ -52,12 +54,13 @@ class ClassBuilder
52
54
  end
53
55
  end
54
56
 
55
- # helper method
57
+ # Set base builder
56
58
  def base=(base)
57
59
  @base_builder = ClassBuilder.new :klass => base
58
60
  end
59
61
 
60
- # perform a deep copy of builder, takes optional recursive guard
62
+ # Perform a deep copy of builder.
63
+ # cloned param is used internally and should not be set by invoker.
61
64
  def clone(cloned = {})
62
65
  return cloned[self] if cloned.has_key? self
63
66
 
@@ -74,7 +77,7 @@ class ClassBuilder
74
77
  return cb
75
78
  end
76
79
 
77
- # helper method to get all associated class builders
80
+ # Helper method to get all associated class builders
78
81
  def associated(builders = [])
79
82
  unless @base_builder.nil? || builders.include?(@base_builder)
80
83
  builders.push @base_builder
@@ -102,7 +105,7 @@ class ClassBuilder
102
105
 
103
106
  end # class ClassBuilder
104
107
 
105
- # base interface and helper methods to build objects in various formats from specified parameters
108
+ # Base interface and helper methods to build objects in various formats from specified parameters
106
109
  class ObjectBuilder
107
110
  # name of class instance to build
108
111
  attr_accessor :tag_name
@@ -124,7 +127,7 @@ class ObjectBuilder
124
127
  # parent object builder, optionally set
125
128
  attr_accessor :parent
126
129
 
127
- # create a new class builder w/ specified args
130
+ # Create a new class builder w/ specified args
128
131
  def initialize(args = {})
129
132
  if args.has_key? :builder
130
133
  @tag_name = args[:builder].tag_name
@@ -1,11 +1,11 @@
1
1
  # RXSD Ruby Class builder
2
2
  #
3
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
4
  # See COPYING for the License of this software
5
5
 
6
6
  module RXSD
7
7
 
8
- # Implements the RXSD::ClassBuilder interface to build Ruby Classes from xsd
8
+ # Implements the RXSD::ClassBuilder interface to build Ruby Classes from a xsd schema
9
9
  class RubyClassBuilder < ClassBuilder
10
10
 
11
11
 
@@ -1,6 +1,6 @@
1
1
  # RXSD Ruby Definition builder
2
2
  #
3
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
4
  # See COPYING for the License of this software
5
5
 
6
6
  module RXSD
@@ -1,6 +1,6 @@
1
1
  # RXSD Ruby Object builder
2
2
  #
3
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
4
  # See COPYING for the License of this software
5
5
 
6
6
  module RXSD
@@ -7,7 +7,7 @@
7
7
  # no arguments as well as from a string string parameter
8
8
  # (exception is made in the case of Array)
9
9
  #
10
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
10
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
11
11
  # See COPYING for the License of this software
12
12
 
13
13
  require 'date'
@@ -15,8 +15,9 @@ require 'date'
15
15
  # Array, String, Time can be instantiated as is
16
16
 
17
17
  class Array
18
- # arrays take addition parameter when instantiating from
19
- # string, the item type which to instantiate array elements w/
18
+ # Convert string to array and return.
19
+ # * str should be the string encoded array
20
+ # * item_type should be the class on which to invoke from_s on each array item
20
21
  def self.from_s(str, item_type)
21
22
  arr = []
22
23
  str.split.each { |i|
@@ -27,47 +28,69 @@ class Array
27
28
  end
28
29
 
29
30
  class String
30
- # convert string to boolean
31
+ # Convert string to boolean
31
32
  def to_b
32
33
  return true if self == true || self =~ /^true$/i
33
34
  return false if self == false || self.nil? || self =~ /^false$/i
34
35
  raise ArgumentError, "invalid value for Boolean: \"#{self}\""
35
36
  end
36
37
 
38
+ # Convert string to string (just return str)
37
39
  def self.from_s(str)
38
40
  str
39
41
  end
42
+
43
+ # Helper to convert string to array of specified type.
44
+ def to_a(args = {})
45
+ arr = []
46
+ item_type = args[:type]
47
+ delim = args.has_key?(:delim) ? args[:delim] : ' '
48
+ split(delim).collect { |item| arr.push(item_type.from_s(item)) }
49
+ return arr
50
+ end
40
51
  end
41
52
 
42
53
  class Time
54
+ # Convert string to Time and return
43
55
  def self.from_s(str)
44
56
  return Time.parse(str)
45
57
  end
46
58
  end
47
59
 
48
- # ruby doesn't define Char class, so we dispatch to string
60
+ # Ruby doesn't define a Char class, so we define one here and dispatch to string
49
61
  class Char < String
50
62
  end
51
63
 
52
- # Since we can't create new instances of Integer, Float,
53
- # etc subclasses, we use the delegate module
54
- # http://codeidol.com/other/rubyckbk/Numbers/Simulating-a-Subclass-of-Fixnum/
55
64
  require 'delegate'
56
65
 
66
+ # Since we can't create new instances of Integer subclasses,
67
+ # we use the delegate module.
68
+ # http://codeidol.com/other/rubyckbk/Numbers/Simulating-a-Subclass-of-Fixnum/
57
69
  class XSDInteger < DelegateClass(::Integer)
70
+
71
+ # Convert string to integer and return
58
72
  def self.from_s(str)
59
73
  str.to_i
60
74
  end
75
+
61
76
  end
62
77
 
78
+ # Since we can't create new instances of Float subclasses,
79
+ # we use the delegate module.
80
+ # http://codeidol.com/other/rubyckbk/Numbers/Simulating-a-Subclass-of-Fixnum/
63
81
  class XSDFloat < DelegateClass(::Float)
82
+
83
+ # Convert string to float and return
64
84
  def self.from_s(str)
65
85
  str.to_f
66
86
  end
87
+
67
88
  end
68
89
 
69
- # ruby doesn't define Boolean class, so we define one ourselves
90
+ # Ruby doesn't define a Boolean class, so we define one ourselves
70
91
  class Boolean
92
+
93
+ # Convert string to boolean and return
71
94
  def self.from_s(str)
72
95
  str.to_b
73
96
  end
data/lib/rxsd/common.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # Things that don't fit elsewhere
2
2
  #
3
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
4
  # See COPYING for the License of this software
5
5
 
6
6
  # we make use of the activesupport inflector
@@ -10,6 +10,7 @@ require 'active_support'
10
10
  require 'logger'
11
11
 
12
12
  class Logger
13
+ # Override default logger format
13
14
  def format_message(severity, timestamp, progname, msg)
14
15
  "#{severity} #{timestamp} (#{$$}) #{msg}\n"
15
16
  end
@@ -1,6 +1,6 @@
1
1
  # RXSD exceptions
2
2
  #
3
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
4
  # See COPYING for the License of this software
5
5
 
6
6
  require 'uri' # use uri to parse sources
@@ -1,7 +1,6 @@
1
-
2
1
  # libxml adapter
3
2
  #
4
- # Copyright (C) 2009 Mohammed Morsi <movitto@yahoo.com>
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
5
4
  # See COPYING for the License of this software
6
5
 
7
6
  require 'rubygems'
@@ -10,65 +9,103 @@ require 'libxml' # based on libxml
10
9
  module RXSD
11
10
  module XML
12
11
 
13
- # class prototype needed :-(
14
- class Node
12
+ class Node ; end
13
+ class Namespace ; end
14
+
15
+ # Some additions to the libxml namespace interface
16
+ class LibXMLNamespace < Namespace
17
+ def initialize(args = {})
18
+ @ns = args[:ns]
19
+ end
20
+
21
+ # Implementation of RXSD::XML::Namespace.prefix
22
+ def prefix
23
+ @ns.prefix
24
+ end
25
+
26
+ # Implementation of RXSD::XML::Namespace.href
27
+ def href
28
+ @ns.href
29
+ end
30
+
31
+ # Implementation of RXSD::XML::Namespace.to_s
32
+ def to_s
33
+ @ns.to_s
34
+ end
15
35
  end
16
36
 
17
- # some additions to libxml xml node interface
37
+ # Some additions to libxml xml node interface
18
38
  class LibXMLNode < Node
19
39
 
20
- # implementation of RXSD::XML::Node::xml_root(xml)
40
+ # Implementation of RXSD::XML::Node::xml_root(xml)
21
41
  def self.xml_root(xml)
22
42
  LibXMLNode.new :node => LibXML::XML::Document.string(xml).root
23
43
  end
24
44
 
25
- # create libxml node adapter w/ specified args, which may include
26
- # * :node LibXML::Node to use to satify requests
45
+ # Create libxml node adapter w/ specified args, which may include
46
+ # * :node LibXML::Node to use to satify requests
27
47
  def initialize(args = {})
28
48
  @node = args[:node]
49
+ @parent = args[:parent] if args.has_key? :parent
50
+
51
+ @attributes = @node.attributes.to_h
52
+
53
+ @children = []
54
+ @node.children.find_all { |n| !n.text? }.each { |n|
55
+ @children << LibXMLNode.new(:node => n, :parent => self)
56
+ }
57
+
58
+ @namespaces = []
59
+ @node.namespaces.each { |ns|
60
+ @namespaces << LibXMLNamespace.new(:ns => ns)
61
+ }
29
62
  end
30
63
 
31
64
 
32
- # implementation of RXSD::XML::Node.name
65
+ # Implementation of RXSD::XML::Node.name
33
66
  def name
34
67
  @node.name
35
68
  end
36
69
 
37
- # implementation of RXSD::XML::Node.attrs
70
+ # Implementation of RXSD::XML::Node.attrs
38
71
  def attrs
39
- @node.attributes.to_h
72
+ @attributes
40
73
  end
41
74
 
42
- # implementation of RXSD::XML::Node.parent?
75
+ # Implementation of RXSD::XML::Node.parent?
43
76
  def parent?
44
77
  @node.parent? && @node.parent.class != LibXML::XML::Document
45
78
  end
46
79
 
47
- # implementation of RXSD::XML::Node.parent
80
+ # FIXME in parent and children don't instantiate new objects, instead use some shared registry
81
+
82
+ # Implementation of RXSD::XML::Node.parent
48
83
  def parent
49
- parent? ? LibXMLNode.new(:node => @node.parent) : nil
84
+ parent? ? @parent : nil
50
85
  end
51
86
 
52
- # implementation of RXSD::XML::Node.children
87
+ # Implementation of RXSD::XML::Node.children
53
88
  def children
54
- @node.children.collect { |n|
55
- LibXMLNode.new :node => n
56
- }
89
+ @children
57
90
  end
58
91
 
59
- # implementation of RXSD::XML::Node.text?
92
+ # Implementation of RXSD::XML::Node.text?.
93
+ # See #content method as well
60
94
  def text?
61
- @node.text?
95
+ @node.text? || (@node.children.size == 1 && @node.children[0].text?)
62
96
  end
63
97
 
64
- # implementation of RXSD::XML::Node.content
98
+ # Implementation of RXSD::XML::Node.content
99
+ # See text? method as well
65
100
  def content
66
- @node.content
101
+ return nil unless text?
102
+ @node.content if @node.text?
103
+ @node.children[0].content
67
104
  end
68
105
 
69
- # implementation of RXSD::XML::Node.namespaces
106
+ # Implementation of RXSD::XML::Node.namespaces
70
107
  def namespaces
71
- @node.namespaces
108
+ @namespaces
72
109
  end
73
110
 
74
111
  end