rxsd 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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