bio-nexml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/Gemfile +15 -0
  2. data/Gemfile.lock +24 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.rdoc +47 -0
  5. data/Rakefile +55 -0
  6. data/TODO.txt +6 -0
  7. data/VERSION +1 -0
  8. data/bio-nexml.gemspec +126 -0
  9. data/extconf.rb +2 -0
  10. data/lib/bio-nexml.rb +0 -0
  11. data/lib/bio.rb +321 -0
  12. data/lib/bio/db/nexml.rb +109 -0
  13. data/lib/bio/db/nexml/mapper.rb +113 -0
  14. data/lib/bio/db/nexml/mapper/framework.rb +157 -0
  15. data/lib/bio/db/nexml/mapper/inflection.rb +99 -0
  16. data/lib/bio/db/nexml/mapper/repository.rb +59 -0
  17. data/lib/bio/db/nexml/matrix.rb +1046 -0
  18. data/lib/bio/db/nexml/parser.rb +622 -0
  19. data/lib/bio/db/nexml/schema/README.txt +21 -0
  20. data/lib/bio/db/nexml/schema/abstract.xsd +159 -0
  21. data/lib/bio/db/nexml/schema/characters/README.txt +1 -0
  22. data/lib/bio/db/nexml/schema/characters/abstractcharacters.xsd +361 -0
  23. data/lib/bio/db/nexml/schema/characters/characters.xsd +22 -0
  24. data/lib/bio/db/nexml/schema/characters/continuous.xsd +190 -0
  25. data/lib/bio/db/nexml/schema/characters/dna.xsd +282 -0
  26. data/lib/bio/db/nexml/schema/characters/protein.xsd +280 -0
  27. data/lib/bio/db/nexml/schema/characters/restriction.xsd +239 -0
  28. data/lib/bio/db/nexml/schema/characters/rna.xsd +283 -0
  29. data/lib/bio/db/nexml/schema/characters/standard.xsd +261 -0
  30. data/lib/bio/db/nexml/schema/external/sawsdl.xsd +21 -0
  31. data/lib/bio/db/nexml/schema/external/xhtml-datatypes-1.xsd +177 -0
  32. data/lib/bio/db/nexml/schema/external/xlink.xsd +75 -0
  33. data/lib/bio/db/nexml/schema/external/xml.xsd +145 -0
  34. data/lib/bio/db/nexml/schema/meta/README.txt +2 -0
  35. data/lib/bio/db/nexml/schema/meta/annotations.xsd +100 -0
  36. data/lib/bio/db/nexml/schema/meta/meta.xsd +294 -0
  37. data/lib/bio/db/nexml/schema/nexml.xsd +104 -0
  38. data/lib/bio/db/nexml/schema/taxa/README.txt +2 -0
  39. data/lib/bio/db/nexml/schema/taxa/taxa.xsd +39 -0
  40. data/lib/bio/db/nexml/schema/trees/README.txt +2 -0
  41. data/lib/bio/db/nexml/schema/trees/abstracttrees.xsd +135 -0
  42. data/lib/bio/db/nexml/schema/trees/network.xsd +113 -0
  43. data/lib/bio/db/nexml/schema/trees/tree.xsd +149 -0
  44. data/lib/bio/db/nexml/schema/trees/trees.xsd +36 -0
  45. data/lib/bio/db/nexml/taxa.rb +147 -0
  46. data/lib/bio/db/nexml/trees.rb +663 -0
  47. data/lib/bio/db/nexml/writer.rb +265 -0
  48. data/test/data/nexml/test.xml +69 -0
  49. data/test/test_bio-nexml.rb +17 -0
  50. data/test/unit/bio/db/nexml/tc_factory.rb +119 -0
  51. data/test/unit/bio/db/nexml/tc_mapper.rb +78 -0
  52. data/test/unit/bio/db/nexml/tc_matrix.rb +551 -0
  53. data/test/unit/bio/db/nexml/tc_parser.rb +21 -0
  54. data/test/unit/bio/db/nexml/tc_taxa.rb +118 -0
  55. data/test/unit/bio/db/nexml/tc_trees.rb +370 -0
  56. data/test/unit/bio/db/nexml/tc_writer.rb +633 -0
  57. metadata +253 -0
@@ -0,0 +1,109 @@
1
+ #load XML library for parser and serializer
2
+ require 'xml'
3
+
4
+ #load required class and module definitions
5
+ require "bio/db/nexml/mapper"
6
+
7
+ #Autoload definition
8
+ module Bio
9
+ module NeXML
10
+
11
+ @@id_counter = 0;
12
+ def self.generate_id( klass )
13
+ myname = klass.name
14
+ local = myname.gsub(/.*:/,"")
15
+ @@id_counter += 1
16
+ newid = @@id_counter
17
+ "#{local}#{newid}"
18
+ end
19
+
20
+ module Base
21
+ attr_accessor :xml_base
22
+ attr_accessor :xml_id
23
+ attr_accessor :xml_lang
24
+ attr_accessor :xml_space
25
+ end #end module Base
26
+
27
+ # Autoload multiple modules that reside in the same file.
28
+ def self.mautoload( modules, file )
29
+ modules.each do |m|
30
+ autoload m, file
31
+ end
32
+ end
33
+
34
+ mautoload %w|Otu Otus|, 'bio/db/nexml/taxa.rb'
35
+ mautoload %w|Node Edge Tree Network Trees|, 'bio/db/nexml/trees.rb'
36
+ mautoload %w|State Char States Cell Sequence Format Characters|, 'bio/db/nexml/matrix.rb'
37
+
38
+ autoload :Parser, 'bio/db/nexml/parser'
39
+ autoload :Writer, 'bio/db/nexml/writer'
40
+
41
+ class Nexml
42
+ @@writer = Bio::NeXML::Writer.new
43
+ include Mapper
44
+ include Enumerable
45
+ attr_accessor :version
46
+ attr_accessor :generator
47
+
48
+ has_n :otus, :singularize => false
49
+ has_n :trees, :singularize => false
50
+ has_n :characters, :singularize => false
51
+
52
+ def initialize( version = '0.9', generator = 'bioruby' )
53
+ @version = version
54
+ @generator = generator
55
+ end
56
+
57
+ # Append a Otus, Trees, or Characters object to any
58
+ # Nexml object.
59
+ def <<( element )
60
+ case element
61
+ when Otus
62
+ add_otus element
63
+ when Trees
64
+ add_trees element
65
+ when Characters
66
+ add_characters element
67
+ end
68
+ end
69
+
70
+ def create_otus( options = {} )
71
+ otus = Otus.new( Bio::NeXML.generate_id( Otus ), options )
72
+ self << otus
73
+ otus
74
+ end
75
+
76
+ def create_trees( options )
77
+ trees = Trees.new( Bio::NeXML.generate_id( Trees ), options )
78
+ self << trees
79
+ trees
80
+ end
81
+
82
+ def create_characters( type = "Dna", verbose = false, options = {} )
83
+ subtype = verbose ? "Cells" : "Seqs"
84
+ klass_name = "#{type.to_s.capitalize}#{subtype}"
85
+ klass = NeXML.const_get( klass_name )
86
+ characters = klass.new( Bio::NeXML.generate_id( klass ), options )
87
+ self << characters
88
+ characters
89
+ end
90
+
91
+ def to_xml
92
+ node = @@writer.create_node( "nex:nexml", :"xsi:schemaLocation" => "http://www.nexml.org/2009 http://www.nexml.org/2009/xsd/nexml.xsd", :generator => generator, :version => version )
93
+ node.namespaces = { nil => "http://www.nexml.org/2009", :xsi => "http://www.w3.org/2001/XMLSchema-instance", :xlink => "http://www.w3.org/1999/xlink", :nex => "http://www.nexml.org/2009" }
94
+ self.each_otus do |otus|
95
+ node << otus.to_xml
96
+ end
97
+ self.each_characters do |characters|
98
+ node << characters.to_xml
99
+ end
100
+ self.each_trees do |trees|
101
+ node << trees.to_xml
102
+ end
103
+ node
104
+ end
105
+
106
+ end #end class Nexml
107
+ end
108
+ end
109
+
@@ -0,0 +1,113 @@
1
+ require 'bio/db/nexml/mapper/inflection'
2
+ require 'bio/db/nexml/mapper/repository'
3
+ require 'bio/db/nexml/mapper/framework'
4
+
5
+ module Bio
6
+ module NeXML
7
+
8
+ # Mapper provides a very small DSL for defining NeXML classes, specifically one-to-many relation
9
+ # between classes as they are found a lot in NeXML. Though the style has been borrowed from
10
+ # ActiveRecord, it should not be mistaken for an ORM. It just factors out repetitive chunk of
11
+ # code that would otherwise be present.
12
+ #
13
+ # Including Mapper in a class, creates <tt>has_n</tt>, and <tt>belongs_to</tt> class methods
14
+ # by extending Framework module. These methods are used to define a one-to-many relation
15
+ # between two classes. Two types of one-to-many relation occurs in NeXML.
16
+ # * parent-child - an element <em>contains</em> other elements. In raw NeXML it would mean
17
+ # nested elements.
18
+ # * association - an element <em>refers</em> to other elements. In raw NeXML it would mean
19
+ # a source element, referred to by several targets.
20
+ #
21
+ # Their implementation however is same. Defining a relation automatically creates methods to
22
+ # add, delete and query objects.
23
+ #
24
+ # class Otu
25
+ # property :id
26
+ # belongs_to :otus
27
+ # end
28
+ #
29
+ # class Otus
30
+ # property :id
31
+ # has_n :otus
32
+ # end
33
+ #
34
+ # t1 = Otu.new
35
+ # t1.id = 't1'
36
+ # t2 = Otu.new
37
+ # t2.id = 't2'
38
+ # o1 = Otus.new
39
+ # o1.id = 'o1'
40
+ #
41
+ # # add to source
42
+ # o1.add_otu( t1 )
43
+ # o1.otus
44
+ # => [ t1 ]
45
+ # t1.otus
46
+ # => o1
47
+ #
48
+ # # add to target
49
+ # t2.otus = o1
50
+ # o1.otus
51
+ # => [ t1, t2 ]
52
+ # t2.otus
53
+ # => o1
54
+ #
55
+ # # query from source
56
+ # o1.has_otu?( t1 )
57
+ # => true
58
+ # o1.get_otu_by_id( 't1' )
59
+ # => t1
60
+ #
61
+ # # delete from source
62
+ # o1.delete_otu( t1 )
63
+ # o1.otus
64
+ # => [ t2 ]
65
+ # t1.otus
66
+ # => nil
67
+ module Mapper # :nodoc: all
68
+
69
+ # Gets or sets properties of the receiver.
70
+ def properties( args = {} )
71
+ @properties ||= {}
72
+
73
+ # set properties if defined
74
+ args.each do |property, value|
75
+ raise "#{property} not defined" unless respond_to?( setter = "#{property}=" )
76
+ send( setter, value )
77
+ end unless args.empty?
78
+
79
+ # return a copy of @properties so that it is not modified inadvertently by the user
80
+ @properties.dup
81
+ end
82
+ alias properties= properties
83
+
84
+ private
85
+
86
+ # Fetches an instance variable corresponding to name. If the instance variable
87
+ # is nil, set it to defaul value and return it.
88
+ def repository( name, type = HashRepository )
89
+ ivget( name ) || ivset( name, type.new )
90
+ end
91
+
92
+ # Return an instance variable corresponding to the given name.
93
+ # '@' is automatically prepended to the name.
94
+ # object.ivget( 'name' ) #=> returns value of @name
95
+ # object.ivget( :name ) #=> returns value of @name
96
+ def ivget( name )
97
+ instance_variable_get( "@#{name}" )
98
+ end
99
+
100
+ # Set an instance variable corresponding to the given name.
101
+ # '@' is automatically prepended to the name.
102
+ # object.ivset( 'name', value ) #=> sets @name to value
103
+ # object.ivget( :name, value ) #=> sets @name to value
104
+ def ivset( name, value = nil )
105
+ instance_variable_set( "@#{name}", value )
106
+ end
107
+
108
+ def self.included( klass )
109
+ klass.extend Framework
110
+ end
111
+ end #end module Mapper
112
+ end #end module NeXML
113
+ end #end module Bio
@@ -0,0 +1,157 @@
1
+ module Bio
2
+ module NeXML
3
+ module Mapper
4
+
5
+ # Framework is the real workhorse of Mapper.
6
+ module Framework
7
+
8
+ # Define a source for the target.
9
+ # belongs_to :source
10
+ def belongs_to( name, options = {} )
11
+ update = ( options[ :update ] || self.name.key ).to_sym
12
+
13
+ # create attribute reader for source
14
+ attr_reader name
15
+
16
+ # Add this target to a source.
17
+ # def add_to_source( source )
18
+ # return if @source == source or source.nil?
19
+ # @source = source
20
+ # source.add_target( self )
21
+ # end
22
+ define_method( "add_to_#{name}" ) do |source,|
23
+ # return if the target already belongs to
24
+ # the given source or if the source is nil.
25
+ return if ivget( name ) == source or source.nil?
26
+
27
+ # add source and send a message to source
28
+ # to add self as target.
29
+ ivset( name, source )
30
+ source.send( "add_#{update}", self )
31
+ end
32
+
33
+ # Set or remove source.
34
+ # def source=( source )
35
+ # return remove_from_source if source.nil?
36
+ # return add_to_source( self )
37
+ # end
38
+ define_method( "#{name}=" ) do |source,|
39
+ return send( "remove_from_#{name}" ) if source.nil?
40
+ return send( "add_to_#{name}", source )
41
+ end
42
+
43
+ # Remove this target from an existing source.
44
+ # def remove_from_source
45
+ # return unless source = @source
46
+ # @source = nil
47
+ # source.delete_target( self )
48
+ # end
49
+ define_method( "remove_from_#{name}" ) do
50
+ # return if a sourc is not set.
51
+ return unless source = ivget( name )
52
+
53
+ # remove source and send a message to source
54
+ # to remove itself as target.
55
+ ivset( name, nil )
56
+ source.send( "delete_#{update}", self )
57
+ end
58
+ end
59
+
60
+ # Define target set for the source.
61
+ # has_n :otu
62
+ def has_n( target, options = {} )
63
+ name = ( options[ :singularize ] == false ) ? target.to_s : target.to_s.singular
64
+ type = ( options[ :index ] == false ) ? ArrayRepository : HashRepository
65
+ update = ( options[ :update ] || self.name.key ).to_sym
66
+
67
+ # Return an Array of targets.
68
+ # def targets
69
+ # @targets.objects
70
+ # end
71
+ define_method( target ) do
72
+ repository( target, type ).objects
73
+ end
74
+
75
+ # Set an Array as a target.
76
+ # def targets=( targets )
77
+ # @targets.clear
78
+ # targets.each do |t|
79
+ # add_target( t )
80
+ # end
81
+ # end
82
+ define_method( "#{target}=" ) do |objects,|
83
+ repository( target, type ).clear
84
+ objects.each do |o|
85
+ send( "add_#{name}", o )
86
+ end
87
+ end
88
+
89
+ # Add a target.
90
+ # def add_target( target )
91
+ # return if @targets.include?( target )
92
+ # @targets.append( target )
93
+ # target.source = self
94
+ # end
95
+ define_method( "add_#{name}" ) do |object,|
96
+ repository = repository( target, type )
97
+ return if repository.include?( object )
98
+ repository.append( object )
99
+ object.send( "#{update}=", self )
100
+ self
101
+ end
102
+
103
+ # Delete a target.
104
+ # def delete_target( target )
105
+ # return unless @targets.include?( target )
106
+ # return unless deleted = @targets.delete( target )
107
+ # target.source = nil
108
+ # end
109
+ define_method( "delete_#{name}" ) do |object,|
110
+ repository = repository( target, type )
111
+ return unless deleted = repository.delete( object )
112
+ object.send( "#{update}=", nil )
113
+ deleted
114
+ end
115
+
116
+ # Return the number of targets a source has.
117
+ # def number_of_targets
118
+ # @targets.length
119
+ # end
120
+ define_method( "number_of_#{target}" ) do
121
+ repository( target, type ).length
122
+ end
123
+
124
+ # Return true if the source has the given target.
125
+ # def has_target?( target )
126
+ # @targets.include?( target )
127
+ # end
128
+ define_method( "has_#{name}?" ) do |object,|
129
+ repository( target, type ).include?( object )
130
+ end
131
+
132
+ # Iterate over each target.
133
+ # def each( &block )
134
+ # @targets.each( &block )
135
+ # end
136
+ class_eval <<_END_OF_EVAL_
137
+ def each_#{name}( &block )
138
+ repository( "#{target}", #{type} ).each( &block )
139
+ end
140
+ _END_OF_EVAL_
141
+
142
+ if type == HashRepository
143
+ define_method( "get_#{name}_by_id" ) do |id,|
144
+ repository( target, type )[ id ]
145
+ end
146
+ class_eval <<_END_OF_EVAL_
147
+ def each_#{name}_with_id( &block )
148
+ repository( #{target }, #{type } ).each_with_id( &block )
149
+ end
150
+ _END_OF_EVAL_
151
+ end
152
+ end
153
+
154
+ end #end module Framework
155
+ end #end module Mapper
156
+ end #end module NeXML
157
+ end #end module Mapper
@@ -0,0 +1,99 @@
1
+ module Bio
2
+ module NeXML
3
+ module Mapper # :nodoc:
4
+
5
+ # String inflections. This module is mixed with the String class.
6
+ #
7
+ # "targets".singular #=> "target"
8
+ # "target".plural #=> "targets"
9
+ # "Bio::NeXML::Otu".key #=> "otu"
10
+ module Inflections
11
+ PLURALS =
12
+ [
13
+ [ /$/, 's' ],
14
+ [ /s$/i, 's' ],
15
+ [ /(ax|test)is$/i, '\1es' ],
16
+ [ /(octop|vir)us$/i, '\1i' ],
17
+ [ /(alias|status)$/i, '\1es' ],
18
+ [ /(bu)s$/i, '\1ses' ],
19
+ [ /(buffal|tomat)o$/i, '\1oes' ],
20
+ [ /([ti])um$/i, '\1a' ],
21
+ [ /sis$/i, 'ses' ],
22
+ [ /(?:([^f])fe|([lr])f)$/i, '\1\2ves' ],
23
+ [ /(hive)$/i, '\1s' ],
24
+ [ /([^aeiouy]|qu)y$/i, '\1ies' ],
25
+ [ /(x|ch|ss|sh)$/i, '\1es' ],
26
+ [ /(matr|vert|ind)(?:ix|ex)$/i, '\1ices' ],
27
+ [ /([m|l])ouse$/i, '\1ice' ],
28
+ [ /^(ox)$/i, '\1en' ],
29
+ [ /(quiz)$/i, '\1zes' ]
30
+ ]
31
+
32
+ SINGULARS =
33
+ [
34
+ [ /s$/i, '' ],
35
+ [ /(n)ews$/i, '\1ews' ],
36
+ [ /([ti])a$/i, '\1um' ],
37
+ [ /((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis' ],
38
+ [ /(^analy)ses$/i, '\1sis' ],
39
+ [ /([^f])ves$/i, '\1fe' ],
40
+ [ /(hive)s$/i, '\1' ],
41
+ [ /(tive)s$/i, '\1' ],
42
+ [ /([lr])ves$/i, '\1f' ],
43
+ [ /([^aeiouy]|qu)ies$/i, '\1y' ],
44
+ [ /(s)eries$/i, '\1eries' ],
45
+ [ /(m)ovies$/i, '\1ovie' ],
46
+ [ /(x|ch|ss|sh)es$/i, '\1' ],
47
+ [ /([m|l])ice$/i, '\1ouse' ],
48
+ [ /(bus)es$/i, '\1' ],
49
+ [ /(o)es$/i, '\1' ],
50
+ [ /(shoe)s$/i, '\1' ],
51
+ [ /(cris|ax|test)es$/i, '\1is' ],
52
+ [ /(octop|vir)i$/i, '\1us' ],
53
+ [ /(alias|status)es$/i, '\1' ],
54
+ [ /^(ox)en/i, '\1' ],
55
+ [ /(vert|ind)ices$/i, '\1ex' ],
56
+ [ /(matr)ices$/i, '\1ix' ],
57
+ [ /(quiz)zes$/i, '\1' ],
58
+ [ /(database)s$/i, '\1' ]
59
+ ]
60
+
61
+ # Return the singular form of string.
62
+ def singular
63
+ result = self.dup
64
+ SINGULARS.each do |match, replace|
65
+ rule = Regexp.compile( match )
66
+ unless match( rule ).nil?
67
+ result = gsub( rule, replace)
68
+ end
69
+ end
70
+ return result
71
+ end
72
+
73
+ # Return the plural form of a string.
74
+ def plural
75
+ result = self.dup
76
+ PLURALS.each do |match_exp, replacement_exp|
77
+ unless match(Regexp.compile(match_exp)).nil?
78
+ result = gsub(Regexp.compile(match_exp), replacement_exp)
79
+ end
80
+ end
81
+ return result
82
+ end
83
+
84
+ # For a module name as "Bio::NeXML" return "nexml".
85
+ def key
86
+ result = self.dup
87
+ if i = rindex( ':' )
88
+ result = self[ i + 1 .. -1 ]
89
+ end
90
+ result.downcase
91
+ end
92
+ end # end module Inflections
93
+
94
+ String.class_eval do
95
+ include Inflections
96
+ end
97
+ end #end module Mapper
98
+ end #end module NeXML
99
+ end #end module Bio