gfa 0.2.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -7
- data/lib/gfa/common.rb +9 -6
- data/lib/gfa/field/char.rb +2 -1
- data/lib/gfa/field/float.rb +18 -1
- data/lib/gfa/field/hex.rb +18 -1
- data/lib/gfa/field/json.rb +10 -1
- data/lib/gfa/field/numarray.rb +29 -4
- data/lib/gfa/field/sigint.rb +14 -1
- data/lib/gfa/field/string.rb +10 -1
- data/lib/gfa/field.rb +82 -10
- data/lib/gfa/graph.rb +1 -2
- data/lib/gfa/parser.rb +25 -17
- data/lib/gfa/record/comment.rb +7 -2
- data/lib/gfa/record/containment.rb +8 -7
- data/lib/gfa/record/header.rb +2 -0
- data/lib/gfa/record/jump.rb +8 -8
- data/lib/gfa/record/link.rb +8 -7
- data/lib/gfa/record/path.rb +6 -6
- data/lib/gfa/record/segment.rb +3 -2
- data/lib/gfa/record/walk.rb +6 -6
- data/lib/gfa/record.rb +28 -14
- data/lib/gfa/record_set/comment_set.rb +3 -0
- data/lib/gfa/record_set/containment_set.rb +4 -0
- data/lib/gfa/record_set/header_set.rb +3 -0
- data/lib/gfa/record_set/jump_set.rb +3 -0
- data/lib/gfa/record_set/link_set.rb +3 -0
- data/lib/gfa/record_set/path_set.rb +4 -0
- data/lib/gfa/record_set/segment_set.rb +4 -0
- data/lib/gfa/record_set/walk_set.rb +3 -0
- data/lib/gfa/record_set.rb +99 -0
- data/lib/gfa/version.rb +1 -1
- data/test/common_test.rb +5 -5
- data/test/field_test.rb +52 -26
- data/test/parser_test.rb +50 -13
- data/test/record_test.rb +7 -0
- data/test/test_helper.rb +5 -0
- metadata +15 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e8e61ff97b34654b7a660b011826ad5549f66933a91a09658facf58c3fd56b1
|
4
|
+
data.tar.gz: 8f85f07955e71cd38a9dfa28011c70433473ad6a1137ed3e6e217d63eeba20a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b9f8fd92cd30d9e4e5c0263e938169141749c7be43011b574f937c9645608d8e72189bfe9072d7321e3acdf7e8288cecf42c90abe3ceef2bf001780bdb3e472
|
7
|
+
data.tar.gz: ee33c0b9c0dc9adb2df96d95b792b060b248e2f991ed5a0e4b4c136d66f04b9be6cf5ca58462bce046c6cfd9e7fed6c4c6949cb5e8923eb71e3d5f53f0da2703
|
data/README.md
CHANGED
@@ -58,12 +58,12 @@ Any `GFA` object can be exported as an [`RGL`][rgl] graph using the methods
|
|
58
58
|
[tiny.gfa](https://github.com/lmrodriguezr/gfa/raw/master/data/tiny.gfa):
|
59
59
|
|
60
60
|
```ruby
|
61
|
-
require
|
62
|
-
require
|
61
|
+
require 'gfa'
|
62
|
+
require 'rgl/dot'
|
63
63
|
|
64
|
-
my_gfa = GFA.load(
|
64
|
+
my_gfa = GFA.load('data/tiny.gfa')
|
65
65
|
dg = my_gfa.implicit_graph
|
66
|
-
dg.write_to_graphic_file(
|
66
|
+
dg.write_to_graphic_file('jpg')
|
67
67
|
```
|
68
68
|
|
69
69
|
![tiny_dg](https://github.com/lmrodriguezr/gfa/raw/master/data/tiny.jpg)
|
@@ -72,8 +72,8 @@ If you don't care about orientation, you can also build an undirected graph
|
|
72
72
|
without orientation:
|
73
73
|
|
74
74
|
```ruby
|
75
|
-
ug = my_gfa.implicit_graph(orient:false)
|
76
|
-
ug.write_to_graphic_file(
|
75
|
+
ug = my_gfa.implicit_graph(orient: false)
|
76
|
+
ug.write_to_graphic_file('jpg')
|
77
77
|
```
|
78
78
|
|
79
79
|
![tiny_ug](https://github.com/lmrodriguezr/gfa/raw/master/data/tiny_undirected.jpg)
|
@@ -88,7 +88,7 @@ gem install gfa
|
|
88
88
|
Or add the following line to your Gemfile:
|
89
89
|
|
90
90
|
```ruby
|
91
|
-
gem
|
91
|
+
gem 'gfa'
|
92
92
|
```
|
93
93
|
|
94
94
|
and run `bundle install` from your shell.
|
data/lib/gfa/common.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'gfa/version'
|
2
|
-
require 'gfa/
|
2
|
+
require 'gfa/record_set'
|
3
3
|
require 'gfa/field'
|
4
4
|
|
5
5
|
class GFA
|
6
6
|
# Class-level
|
7
7
|
def self.assert_format(value, regex, message)
|
8
|
-
unless value =~ regex
|
9
|
-
raise "#{message}: #{value}
|
8
|
+
unless value =~ /^(?:#{regex})$/
|
9
|
+
raise "#{message}: #{value}"
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
# Instance-level
|
14
|
-
attr :gfa_version, :records
|
14
|
+
attr :gfa_version, :records, :opts
|
15
15
|
|
16
16
|
GFA::Record.TYPES.each do |r_type|
|
17
17
|
plural = "#{r_type.downcase}s"
|
@@ -22,9 +22,12 @@ class GFA
|
|
22
22
|
define_method("add_#{singular}") { |v| @records[r_type] << v }
|
23
23
|
end
|
24
24
|
|
25
|
-
def initialize
|
25
|
+
def initialize(opts = {})
|
26
26
|
@records = {}
|
27
|
-
|
27
|
+
@opts = { index: true, comments: false }.merge(opts)
|
28
|
+
GFA::Record.TYPES.each do |t|
|
29
|
+
@records[t] = GFA::RecordSet.name_class(t).new(self)
|
30
|
+
end
|
28
31
|
end
|
29
32
|
|
30
33
|
def empty?
|
data/lib/gfa/field/char.rb
CHANGED
data/lib/gfa/field/float.rb
CHANGED
@@ -1,9 +1,26 @@
|
|
1
1
|
class GFA::Field::Float < GFA::Field
|
2
2
|
CODE = :f
|
3
|
-
REGEX =
|
3
|
+
REGEX = /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/
|
4
|
+
NATIVE_FUN = :to_f
|
5
|
+
|
6
|
+
def to_f
|
7
|
+
value
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_i
|
11
|
+
value.to_i
|
12
|
+
end
|
4
13
|
|
5
14
|
def initialize(f)
|
6
15
|
GFA.assert_format(f, regex, "Bad #{type}")
|
7
16
|
@value = f.to_f
|
8
17
|
end
|
18
|
+
|
19
|
+
def equivalent?(field)
|
20
|
+
if field.is_a?(GFA::Field::NumArray)
|
21
|
+
return field.size == 1 && field.first.to_f == value
|
22
|
+
end
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
9
26
|
end
|
data/lib/gfa/field/hex.rb
CHANGED
@@ -1,9 +1,26 @@
|
|
1
1
|
class GFA::Field::Hex < GFA::Field
|
2
2
|
CODE = :H
|
3
|
-
REGEX =
|
3
|
+
REGEX = /[0-9A-F]+/
|
4
|
+
NATIVE_FUN = :to_i
|
4
5
|
|
5
6
|
def initialize(f)
|
6
7
|
GFA.assert_format(f, regex, "Bad #{type}")
|
7
8
|
@value = f
|
8
9
|
end
|
10
|
+
|
11
|
+
def to_i
|
12
|
+
value.to_i(16)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_f
|
16
|
+
to_i.to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
def equivalent?(field)
|
20
|
+
if field.is_a? GFA::Field::NumArray
|
21
|
+
return field.size == 1 && field.first.to_i == value
|
22
|
+
end
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
9
26
|
end
|
data/lib/gfa/field/json.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
1
|
class GFA::Field::Json < GFA::Field
|
2
2
|
CODE = :J
|
3
|
-
REGEX =
|
3
|
+
REGEX = /[ !-~]+/
|
4
|
+
NATIVE_FUN = :to_s
|
4
5
|
|
5
6
|
def initialize(f)
|
6
7
|
GFA.assert_format(f, regex, "Bad #{type}")
|
7
8
|
@value = f
|
8
9
|
end
|
10
|
+
|
11
|
+
def equivalent?(field)
|
12
|
+
# TODO
|
13
|
+
# We should parse the contents when comparing two GFA::Field::Json to
|
14
|
+
# evaluate equivalencies such as 'J:{ "a" : 1 }' ~ 'J:{"a":1}' (spaces)
|
15
|
+
# or 'J:{"a":1,"b":2}' ~ 'J:{"b":2,"a":1}' (element order)
|
16
|
+
super
|
17
|
+
end
|
9
18
|
end
|
data/lib/gfa/field/numarray.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
1
|
class GFA::Field::NumArray < GFA::Field
|
2
2
|
CODE = :B
|
3
|
-
REGEX =
|
3
|
+
REGEX = /[cCsSiIf](,[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)+/
|
4
|
+
NATIVE_FUN = :to_a
|
4
5
|
|
5
6
|
def initialize(f)
|
6
7
|
GFA.assert_format(f, regex, "Bad #{type}")
|
7
8
|
@value = f
|
8
9
|
end
|
9
10
|
|
10
|
-
def modifier
|
11
|
+
def modifier
|
12
|
+
value[0]
|
13
|
+
end
|
11
14
|
|
12
|
-
def
|
15
|
+
def modifier_fun
|
16
|
+
modifier == 'f' ? :to_f : :to_i
|
17
|
+
end
|
13
18
|
|
14
|
-
|
19
|
+
def array
|
20
|
+
@array ||= value[2..-1].split(',').map(&modifier_fun)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias to_a array
|
24
|
+
|
25
|
+
%i[empty? size count length first last].each do |i|
|
26
|
+
define_method(i) { array.send(i) }
|
27
|
+
end
|
15
28
|
|
16
29
|
def number_type
|
17
30
|
{
|
@@ -21,4 +34,16 @@ class GFA::Field::NumArray < GFA::Field
|
|
21
34
|
f: 'float'
|
22
35
|
}[modifier.to_sym]
|
23
36
|
end
|
37
|
+
|
38
|
+
def equivalent?(field)
|
39
|
+
return true if eql?(field)
|
40
|
+
|
41
|
+
if field.respond_to?(:to_a)
|
42
|
+
field.to_a.map(&modifier_fun) == array
|
43
|
+
elsif size == 1 && field.respond_to?(modifier_fun)
|
44
|
+
field.send(modifier_fun) == first
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
24
49
|
end
|
data/lib/gfa/field/sigint.rb
CHANGED
@@ -1,9 +1,22 @@
|
|
1
1
|
class GFA::Field::SigInt < GFA::Field
|
2
2
|
CODE = :i
|
3
|
-
REGEX =
|
3
|
+
REGEX = /[-+]?[0-9]+/
|
4
|
+
NATIVE_FUN = :to_i
|
4
5
|
|
5
6
|
def initialize(f)
|
6
7
|
GFA.assert_format(f, regex, "Bad #{type}")
|
7
8
|
@value = f.to_i
|
8
9
|
end
|
10
|
+
|
11
|
+
def to_i
|
12
|
+
value
|
13
|
+
end
|
14
|
+
|
15
|
+
def equivalent?(field)
|
16
|
+
if field.is_a?(GFA::Field::NumArray)
|
17
|
+
return field.size == 1 && field.first.to_i == value
|
18
|
+
end
|
19
|
+
|
20
|
+
super
|
21
|
+
end
|
9
22
|
end
|
data/lib/gfa/field/string.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
class GFA::Field::String < GFA::Field
|
2
2
|
CODE = :Z
|
3
|
-
REGEX =
|
3
|
+
REGEX = /[ !-~]+/
|
4
|
+
NATIVE_FUN = :to_s
|
5
|
+
|
6
|
+
def to_f
|
7
|
+
value.to_f
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_i(base = 10)
|
11
|
+
value.to_i(base)
|
12
|
+
end
|
4
13
|
|
5
14
|
def initialize(f)
|
6
15
|
GFA.assert_format(f, regex, "Bad #{type}")
|
data/lib/gfa/field.rb
CHANGED
@@ -12,7 +12,7 @@ class GFA::Field
|
|
12
12
|
TYPES = CODES.values
|
13
13
|
TYPES.each { |t| require "gfa/field/#{t.downcase}" }
|
14
14
|
|
15
|
-
[
|
15
|
+
%i[CODES TYPES].each do |x|
|
16
16
|
define_singleton_method(x) { const_get(x) }
|
17
17
|
end
|
18
18
|
|
@@ -25,23 +25,95 @@ class GFA::Field
|
|
25
25
|
def self.name_class(name)
|
26
26
|
const_get(name)
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
|
+
def self.[](string)
|
30
|
+
code, value = string.split(':', 2)
|
31
|
+
code_class(code).new(value)
|
32
|
+
end
|
33
|
+
|
29
34
|
# Instance-level
|
30
35
|
|
31
36
|
attr :value
|
32
37
|
|
33
|
-
def type
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def
|
38
|
-
|
39
|
-
|
38
|
+
def type
|
39
|
+
CODES[code]
|
40
|
+
end
|
41
|
+
|
42
|
+
def code
|
43
|
+
self.class::CODE
|
44
|
+
end
|
45
|
+
|
46
|
+
def regex
|
47
|
+
self.class::REGEX
|
48
|
+
end
|
49
|
+
|
50
|
+
def native_fun
|
51
|
+
self.class::NATIVE_FUN
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_native
|
55
|
+
native_fun == :to_s ? to_s(false) : send(native_fun)
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s(with_type = true)
|
40
59
|
"#{"#{code}:" if with_type}#{value}"
|
41
60
|
end
|
42
|
-
|
61
|
+
|
43
62
|
def hash
|
44
63
|
value.hash
|
45
64
|
end
|
46
65
|
|
66
|
+
##
|
67
|
+
# Evaluate equivalency of contents. All the following fields are distinct but
|
68
|
+
# contain the same information, and are therefore considered equivalent:
|
69
|
+
# Z:123, i:123, f:123.0, B:i,123, H:7b
|
70
|
+
#
|
71
|
+
# Note that the information content is determined by the class of the first
|
72
|
+
# operator. For example:
|
73
|
+
# - 'i:123' ~ 'f:123.4' is true because values are compared as integers
|
74
|
+
# - 'f:123.4' ~ 'i:123' if false because values are compared as floats
|
75
|
+
def equivalent?(field)
|
76
|
+
return true if eql?(field) # Might be faster, so testing this first
|
77
|
+
|
78
|
+
if field.respond_to?(native_fun)
|
79
|
+
if field.is_a?(GFA::Field) && native_fun == :to_s
|
80
|
+
field.to_s(false) == to_native
|
81
|
+
else
|
82
|
+
field.send(native_fun) == to_native
|
83
|
+
end
|
84
|
+
else
|
85
|
+
field == value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Non-equivalent to +field+, same as +!equivalent?+
|
91
|
+
def !~(field)
|
92
|
+
!self.~(field)
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Same as +equivalent?+
|
97
|
+
def ~(field)
|
98
|
+
equivalent?(field)
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Evaluate equality. Note that fields with equivalent values evaluate as
|
103
|
+
# different. For example, the following fields have equivalent information,
|
104
|
+
# but they all evaluate as different: Z:123, i:123, f:123.0, B:i,123, H:7b.
|
105
|
+
# To test equivalency of contents instead, use +equivalent?+
|
106
|
+
def eql?(field)
|
107
|
+
if field.is_a?(GFA::Field)
|
108
|
+
type == field.type && value == field.value
|
109
|
+
else
|
110
|
+
field.is_a?(value.class) && value == field
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Same as +eql?+
|
116
|
+
def ==(field)
|
117
|
+
eql?(field)
|
118
|
+
end
|
47
119
|
end
|
data/lib/gfa/graph.rb
CHANGED
@@ -2,7 +2,6 @@ require 'rgl/adjacency'
|
|
2
2
|
require 'rgl/implicit'
|
3
3
|
|
4
4
|
class GFA
|
5
|
-
|
6
5
|
##
|
7
6
|
# Generates a RGL::ImplicitGraph object describing the links in the GFA.
|
8
7
|
# The +opts+ argument is a hash with any of the following key-value pairs:
|
@@ -57,7 +56,7 @@ class GFA
|
|
57
56
|
opts
|
58
57
|
end
|
59
58
|
|
60
|
-
def rgl_implicit_adjacent_iterator(x,b,opts)
|
59
|
+
def rgl_implicit_adjacent_iterator(x, b, opts)
|
61
60
|
links.each do |l|
|
62
61
|
if l.from?(x.segment, x.orient)
|
63
62
|
orient = opts[:orient] ? l.to_orient : nil
|
data/lib/gfa/parser.rb
CHANGED
@@ -4,15 +4,20 @@ class GFA
|
|
4
4
|
# Class-level
|
5
5
|
MIN_VERSION = '1.0'
|
6
6
|
MAX_VERSION = '1.2'
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
|
8
|
+
##
|
9
|
+
# Load a GFA object from a +gfa+ file with options +opts+:
|
10
|
+
# - index: If the records should be indexed as loaded (default: true)
|
11
|
+
# - comments: If the comment records should be saved (default: false)
|
12
|
+
def self.load(file, opts = {})
|
13
|
+
gfa = GFA.new(opts)
|
10
14
|
fh = File.open(file, 'r')
|
11
15
|
fh.each { |ln| gfa << ln }
|
12
|
-
fh.close
|
13
16
|
gfa
|
17
|
+
ensure
|
18
|
+
fh&.close
|
14
19
|
end
|
15
|
-
|
20
|
+
|
16
21
|
def self.supported_version?(v)
|
17
22
|
v.to_f >= MIN_VERSION.to_f and v.to_f <= MAX_VERSION.to_f
|
18
23
|
end
|
@@ -23,24 +28,27 @@ class GFA
|
|
23
28
|
return if obj.nil? || obj.empty?
|
24
29
|
@records[obj.type] << obj
|
25
30
|
|
26
|
-
if obj.type == :Header && !obj.
|
27
|
-
set_gfa_version(obj.
|
31
|
+
if obj.type == :Header && !obj.VN.nil?
|
32
|
+
set_gfa_version(obj.VN.value)
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
31
36
|
def set_gfa_version(v)
|
32
|
-
|
33
|
-
unless GFA::supported_version?
|
34
|
-
raise "GFA version currently unsupported: #{v}
|
37
|
+
v = v.value if v.is_a? GFA::Field
|
38
|
+
unless GFA::supported_version? v
|
39
|
+
raise "GFA version currently unsupported: #{v}"
|
35
40
|
end
|
41
|
+
|
42
|
+
@gfa_version = v
|
36
43
|
end
|
37
|
-
|
44
|
+
|
38
45
|
private
|
39
|
-
|
40
|
-
def parse_line(
|
41
|
-
|
42
|
-
return nil if
|
43
|
-
|
44
|
-
|
46
|
+
|
47
|
+
def parse_line(string)
|
48
|
+
string = string.chomp
|
49
|
+
return nil if string =~ /^\s*$/
|
50
|
+
return nil if !opts[:comments] && string[0] == '#'
|
51
|
+
|
52
|
+
GFA::Record[string]
|
45
53
|
end
|
46
54
|
end
|
data/lib/gfa/record/comment.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
class GFA::Record::Comment < GFA::Record
|
2
2
|
CODE = :'#'
|
3
|
-
REQ_FIELDS = []
|
3
|
+
REQ_FIELDS = %i[comment]
|
4
4
|
OPT_FIELDS = {}
|
5
|
+
|
6
|
+
REQ_FIELDS.each_index do |i|
|
7
|
+
define_method(REQ_FIELDS[i]) { fields[i + 2] }
|
8
|
+
end
|
5
9
|
|
6
|
-
def initialize(*opt_fields)
|
10
|
+
def initialize(comment, *opt_fields)
|
7
11
|
@fields = {}
|
12
|
+
add_field(2, :Z, comment, /.*/)
|
8
13
|
opt_fields.each { |f| add_opt_field(f, OPT_FIELDS) }
|
9
14
|
end
|
10
15
|
end
|
@@ -10,20 +10,21 @@ class GFA::Record::Containment < GFA::Record
|
|
10
10
|
REQ_FIELDS.each_index do |i|
|
11
11
|
define_method(REQ_FIELDS[i]) { fields[i + 2] }
|
12
12
|
end
|
13
|
+
OPT_FIELDS.each_key { |i| define_method(i) { fields[i] } }
|
13
14
|
|
14
15
|
alias container from
|
15
16
|
alias container_orient from_orient
|
16
17
|
alias contained to
|
17
18
|
alias contained_orient to_orient
|
18
|
-
|
19
|
+
|
19
20
|
def initialize(from, from_orient, to, to_orient, pos, overlap, *opt_fields)
|
20
21
|
@fields = {}
|
21
|
-
add_field(2, :Z, from,
|
22
|
-
add_field(3, :Z, from_orient,
|
23
|
-
add_field(4, :Z, to,
|
24
|
-
add_field(5, :Z, to_orient,
|
25
|
-
add_field(6, :i, pos,
|
26
|
-
add_field(7, :Z, overlap,
|
22
|
+
add_field(2, :Z, from, /[!-)+-<>-~][!-~]*/)
|
23
|
+
add_field(3, :Z, from_orient, /[+-]/)
|
24
|
+
add_field(4, :Z, to, /[!-)+-<>-~][!-~]*/)
|
25
|
+
add_field(5, :Z, to_orient, /[+-]/)
|
26
|
+
add_field(6, :i, pos, /[0-9]*/)
|
27
|
+
add_field(7, :Z, overlap, /\*|([0-9]+[MIDNSHPX=])+/)
|
27
28
|
opt_fields.each { |f| add_opt_field(f, OPT_FIELDS) }
|
28
29
|
end
|
29
30
|
end
|
data/lib/gfa/record/header.rb
CHANGED
data/lib/gfa/record/jump.rb
CHANGED
@@ -4,26 +4,26 @@ class GFA::Record::Jump < GFA::Record
|
|
4
4
|
OPT_FIELDS = {
|
5
5
|
SC: :i # 1 indicates indirect shortcut connections. Only 0/1 allowed.
|
6
6
|
}
|
7
|
-
|
7
|
+
|
8
8
|
REQ_FIELDS.each_index do |i|
|
9
9
|
define_method(REQ_FIELDS[i]) { fields[i + 2] }
|
10
10
|
end
|
11
|
+
OPT_FIELDS.each_key { |i| define_method(i) { fields[i] } }
|
11
12
|
|
12
13
|
def initialize(from, from_orient, to, to_orient, distance, *opt_fields)
|
13
14
|
@fields = {}
|
14
|
-
add_field(2, :Z, from,
|
15
|
-
add_field(3, :Z, from_orient,
|
16
|
-
add_field(4, :Z, to,
|
17
|
-
add_field(5, :Z, to_orient,
|
18
|
-
add_field(6, :Z, distance,
|
15
|
+
add_field(2, :Z, from, /[!-)+-<>-~][!-~]*/)
|
16
|
+
add_field(3, :Z, from_orient, /[+-]/)
|
17
|
+
add_field(4, :Z, to, /[!-)+-<>-~][!-~]*/)
|
18
|
+
add_field(5, :Z, to_orient, /[+-]/)
|
19
|
+
add_field(6, :Z, distance, /\*|[-+]?[0-9]+/)
|
19
20
|
opt_fields.each { |f| add_opt_field(f, OPT_FIELDS) }
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
23
|
def from?(segment, orient = nil)
|
24
24
|
links_from_to?(segment, orient, true)
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def to?(segment, orient = nil)
|
28
28
|
links_from_to?(segment, orient, false)
|
29
29
|
end
|
data/lib/gfa/record/link.rb
CHANGED
@@ -9,25 +9,26 @@ class GFA::Record::Link < GFA::Record
|
|
9
9
|
KC: :i, # k-mer count
|
10
10
|
ID: :Z # Edge identifier
|
11
11
|
}
|
12
|
-
|
12
|
+
|
13
13
|
REQ_FIELDS.each_index do |i|
|
14
14
|
define_method(REQ_FIELDS[i]) { fields[i + 2] }
|
15
15
|
end
|
16
|
+
OPT_FIELDS.each_key { |i| define_method(i) { fields[i] } }
|
16
17
|
|
17
18
|
def initialize(from, from_orient, to, to_orient, overlap, *opt_fields)
|
18
19
|
@fields = {}
|
19
|
-
add_field(2, :Z, from,
|
20
|
-
add_field(3, :Z, from_orient,
|
21
|
-
add_field(4, :Z, to,
|
22
|
-
add_field(5, :Z, to_orient,
|
23
|
-
add_field(6, :Z, overlap,
|
20
|
+
add_field(2, :Z, from, /[!-)+-<>-~][!-~]*/)
|
21
|
+
add_field(3, :Z, from_orient, /[+-]/)
|
22
|
+
add_field(4, :Z, to, /[!-)+-<>-~][!-~]*/)
|
23
|
+
add_field(5, :Z, to_orient, /[+-]/)
|
24
|
+
add_field(6, :Z, overlap, /\*|([0-9]+[MIDNSHPX=])+/)
|
24
25
|
opt_fields.each { |f| add_opt_field(f, OPT_FIELDS) }
|
25
26
|
end
|
26
27
|
|
27
28
|
def from?(segment, orient = nil)
|
28
29
|
links_from_to?(segment, orient, true)
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
def to?(segment, orient = nil)
|
32
33
|
links_from_to?(segment, orient, false)
|
33
34
|
end
|
data/lib/gfa/record/path.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
class GFA::Record::Path < GFA::Record
|
2
2
|
CODE = :P
|
3
|
-
REQ_FIELDS = %i[path_name segment_name
|
3
|
+
REQ_FIELDS = %i[path_name segment_name overlaps]
|
4
4
|
OPT_FIELDS = {}
|
5
5
|
|
6
6
|
REQ_FIELDS.each_index do |i|
|
7
7
|
define_method(REQ_FIELDS[i]) { fields[i + 2] }
|
8
8
|
end
|
9
9
|
|
10
|
-
alias overlaps
|
10
|
+
alias cigar overlaps
|
11
11
|
|
12
|
-
def initialize(path_name, segment_name,
|
12
|
+
def initialize(path_name, segment_name, overlaps, *opt_fields)
|
13
13
|
@fields = {}
|
14
|
-
add_field(2, :Z, path_name,
|
15
|
-
add_field(3, :Z, segment_name,
|
16
|
-
add_field(4, :Z,
|
14
|
+
add_field(2, :Z, path_name, /[!-)+-<>-~][!-~]*/)
|
15
|
+
add_field(3, :Z, segment_name, /[!-)+-<>-~][!-~]*/)
|
16
|
+
add_field(4, :Z, overlaps, /\*|([0-9]+[MIDNSHPX=]|[-+]?[0-9]+J|.)+/)
|
17
17
|
opt_fields.each { |f| add_opt_field(f, OPT_FIELDS) }
|
18
18
|
end
|
19
19
|
end
|
data/lib/gfa/record/segment.rb
CHANGED
@@ -15,11 +15,12 @@ class GFA::Record::Segment < GFA::Record
|
|
15
15
|
REQ_FIELDS.each_index do |i|
|
16
16
|
define_method(REQ_FIELDS[i]) { fields[i + 2] }
|
17
17
|
end
|
18
|
+
OPT_FIELDS.each_key { |i| define_method(i) { fields[i] } }
|
18
19
|
|
19
20
|
def initialize(name, sequence, *opt_fields)
|
20
21
|
@fields = {}
|
21
|
-
add_field(2, :Z, name,
|
22
|
-
add_field(3, :Z, sequence,
|
22
|
+
add_field(2, :Z, name, /[!-)+-<>-~][!-~]*/)
|
23
|
+
add_field(3, :Z, sequence, /\*|[A-Za-z=.]+/)
|
23
24
|
opt_fields.each { |f| add_opt_field(f, OPT_FIELDS) }
|
24
25
|
end
|
25
26
|
end
|
data/lib/gfa/record/walk.rb
CHANGED
@@ -9,12 +9,12 @@ class GFA::Record::Walk < GFA::Record
|
|
9
9
|
|
10
10
|
def initialize(sample_id, hap_index, seq_id, seq_start, seq_end, walk, *opt_fields)
|
11
11
|
@fields = {}
|
12
|
-
add_field(2, :Z, sample_id,
|
13
|
-
add_field(3, :i, hap_index,
|
14
|
-
add_field(4, :Z, seq_id,
|
15
|
-
add_field(5, :i, seq_start,
|
16
|
-
add_field(6, :i, seq_end,
|
17
|
-
add_field(7, :Z, walk,
|
12
|
+
add_field(2, :Z, sample_id, /[!-)+-<>-~][!-~]*/)
|
13
|
+
add_field(3, :i, hap_index, /[0-9]+/)
|
14
|
+
add_field(4, :Z, seq_id, /[!-)+-<>-~][!-~]*/)
|
15
|
+
add_field(5, :i, seq_start, /\*|[0-9]+/)
|
16
|
+
add_field(6, :i, seq_end, /\*|[0-9]+/)
|
17
|
+
add_field(7, :Z, walk, /([><][!-;=?-~]+)+/)
|
18
18
|
opt_fields.each { |f| add_opt_field(f, OPT_FIELDS) }
|
19
19
|
end
|
20
20
|
end
|
data/lib/gfa/record.rb
CHANGED
@@ -15,7 +15,7 @@ class GFA::Record
|
|
15
15
|
TYPES = CODES.values
|
16
16
|
TYPES.each { |t| require "gfa/record/#{t.downcase}" }
|
17
17
|
|
18
|
-
[
|
18
|
+
%i[CODES REQ_FIELDS OPT_FIELDS TYPES].each do |x|
|
19
19
|
define_singleton_method(x) { const_get(x) }
|
20
20
|
end
|
21
21
|
|
@@ -29,18 +29,32 @@ class GFA::Record
|
|
29
29
|
const_get(name)
|
30
30
|
end
|
31
31
|
|
32
|
+
def self.[](string)
|
33
|
+
split = string[0] == '#' ? ['', 2] : ["\t", 0]
|
34
|
+
code, *values = string.chomp.split(*split)
|
35
|
+
code_class(code).new(*values)
|
36
|
+
end
|
37
|
+
|
32
38
|
# Instance-level
|
33
39
|
|
34
40
|
attr :fields
|
35
41
|
|
36
|
-
def [](k)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
def [](k)
|
43
|
+
fields[k]
|
44
|
+
end
|
45
|
+
|
46
|
+
def type
|
47
|
+
CODES[code]
|
48
|
+
end
|
49
|
+
|
50
|
+
def code
|
51
|
+
self.class.const_get(:CODE)
|
52
|
+
end
|
53
|
+
|
54
|
+
def empty?
|
55
|
+
fields.empty?
|
56
|
+
end
|
57
|
+
|
44
58
|
def to_s
|
45
59
|
o = [code.to_s]
|
46
60
|
self.class.REQ_FIELDS.each_index do |i|
|
@@ -52,7 +66,7 @@ class GFA::Record
|
|
52
66
|
end
|
53
67
|
o.join("\t")
|
54
68
|
end
|
55
|
-
|
69
|
+
|
56
70
|
def hash
|
57
71
|
{ code => fields }.hash
|
58
72
|
end
|
@@ -64,19 +78,19 @@ class GFA::Record
|
|
64
78
|
alias == eql?
|
65
79
|
|
66
80
|
private
|
67
|
-
|
81
|
+
|
68
82
|
def add_field(f_tag, f_type, f_value, format = nil)
|
69
83
|
unless format.nil?
|
70
84
|
msg = (f_tag.is_a?(Integer) ? "column #{f_tag}" : "#{f_tag} field")
|
71
85
|
GFA.assert_format(f_value, format, "Bad #{type} #{msg}")
|
72
86
|
end
|
73
87
|
|
74
|
-
@fields[
|
88
|
+
@fields[f_tag] = GFA::Field.code_class(f_type).new(f_value)
|
75
89
|
end
|
76
|
-
|
90
|
+
|
77
91
|
def add_opt_field(f, known)
|
78
92
|
m = /^([A-Za-z]+):([A-Za-z]+):(.*)$/.match(f)
|
79
|
-
raise "Cannot parse field: '#{f}'
|
93
|
+
raise "Cannot parse field: '#{f}'" unless m
|
80
94
|
|
81
95
|
f_tag = m[1].to_sym
|
82
96
|
f_type = m[2].to_sym
|
@@ -0,0 +1,99 @@
|
|
1
|
+
|
2
|
+
require 'gfa/record'
|
3
|
+
|
4
|
+
class GFA::RecordSet
|
5
|
+
INDEX_FIELD = nil
|
6
|
+
TYPES = GFA::Record.TYPES.map { |i| :"#{i}Set" }
|
7
|
+
GFA::Record.TYPES.each { |t| require "gfa/record_set/#{t.downcase}_set" }
|
8
|
+
|
9
|
+
%i[TYPES].each do |x|
|
10
|
+
define_singleton_method(x) { const_get(x) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.code_class(code)
|
14
|
+
name = GFA::Record.CODES[code.to_sym]
|
15
|
+
raise "Unknown record type: #{code}." if name.nil?
|
16
|
+
name_class(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.name_class(name)
|
20
|
+
name = "#{name}Set" unless name =~ /Set$/
|
21
|
+
const_get(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Instance-level
|
25
|
+
|
26
|
+
attr_reader :set, :gfa
|
27
|
+
|
28
|
+
def initialize(gfa)
|
29
|
+
@set = []
|
30
|
+
@index = {}
|
31
|
+
@gfa = gfa
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](k)
|
35
|
+
return set[k] if k.is_a?(Integer)
|
36
|
+
find_index(k)
|
37
|
+
end
|
38
|
+
|
39
|
+
def type
|
40
|
+
GFA::Record.CODES[code]
|
41
|
+
end
|
42
|
+
|
43
|
+
def code
|
44
|
+
self.class.const_get(:CODE)
|
45
|
+
end
|
46
|
+
|
47
|
+
def index_field
|
48
|
+
self.class.const_get(:INDEX_FIELD)
|
49
|
+
end
|
50
|
+
|
51
|
+
%i[empty? hash size count length first last].each do |i|
|
52
|
+
define_method(i) { set.send(i) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
set.map(&:to_s).join("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
def eql?(rec)
|
60
|
+
hash == rec.hash
|
61
|
+
end
|
62
|
+
|
63
|
+
alias == eql?
|
64
|
+
|
65
|
+
def <<(v)
|
66
|
+
v = v.split("\t") if v.is_a? String
|
67
|
+
v = GFA::Record.code_class(code).new(*v) if v.is_a? Array
|
68
|
+
raise "Not a GFA Record: #{v}" unless v.is_a? GFA::Record
|
69
|
+
raise "Wrong type of record: #{v.type}" if v.type != type
|
70
|
+
|
71
|
+
@set << v
|
72
|
+
index(v)
|
73
|
+
end
|
74
|
+
|
75
|
+
def index_id(v)
|
76
|
+
v[index_field]&.value
|
77
|
+
end
|
78
|
+
|
79
|
+
def index(v)
|
80
|
+
save_index(index_id(v), v) if index_field
|
81
|
+
|
82
|
+
# Whenever present, index also by ID
|
83
|
+
save_index(v[:ID].value, v) if v[:ID] && v[:ID].value =~ index_id(v)
|
84
|
+
end
|
85
|
+
|
86
|
+
def save_index(k, v)
|
87
|
+
return unless gfa.opts[:index] && k
|
88
|
+
|
89
|
+
if @index[k]
|
90
|
+
warn "#{type} already registered with field #{index_field}: #{k}"
|
91
|
+
end
|
92
|
+
@index[k] = v
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_index(k)
|
96
|
+
k = k.value if k.is_a? GFA::Field
|
97
|
+
@index[k]
|
98
|
+
end
|
99
|
+
end
|
data/lib/gfa/version.rb
CHANGED
data/test/common_test.rb
CHANGED
@@ -4,10 +4,10 @@ class CommonTest < Test::Unit::TestCase
|
|
4
4
|
|
5
5
|
def test_assert_format
|
6
6
|
assert_raise do
|
7
|
-
GFA.assert_format(
|
7
|
+
GFA.assert_format('tsooq', /^.$/, 'Not a char')
|
8
8
|
end
|
9
9
|
assert_nothing_raised do
|
10
|
-
GFA.assert_format(
|
10
|
+
GFA.assert_format('z', /^.$/, 'Not a char')
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -20,15 +20,15 @@ class CommonTest < Test::Unit::TestCase
|
|
20
20
|
def test_record_getters
|
21
21
|
gfa = GFA.new
|
22
22
|
assert_respond_to(gfa, :headers)
|
23
|
-
assert_equal([], gfa.links)
|
23
|
+
assert_equal([], gfa.links.set)
|
24
24
|
assert_nil( gfa.segment(0) )
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_record_setters
|
28
28
|
gfa = GFA.new
|
29
29
|
assert_respond_to(gfa, :add_path)
|
30
|
-
gfa.add_containment("zooq")
|
31
|
-
assert_equal(
|
30
|
+
gfa.add_containment("zooq\t+\ttsuk\t-\t1\t*")
|
31
|
+
assert_equal('zooq', gfa.records[:Containment].first.from.value)
|
32
32
|
end
|
33
33
|
|
34
34
|
end
|
data/test/field_test.rb
CHANGED
@@ -3,48 +3,74 @@ require "test_helper"
|
|
3
3
|
class FieldTest < Test::Unit::TestCase
|
4
4
|
|
5
5
|
def test_char
|
6
|
-
f = GFA::Field::Char.new(
|
7
|
-
assert_equal(
|
8
|
-
assert_raise
|
9
|
-
|
10
|
-
|
11
|
-
assert_raise do
|
12
|
-
GFA::Field::Char.new("")
|
13
|
-
end
|
14
|
-
assert_raise do
|
15
|
-
GFA::Field::Char.new("^.^")
|
16
|
-
end
|
6
|
+
f = GFA::Field::Char.new('%')
|
7
|
+
assert_equal('%', f.value)
|
8
|
+
assert_raise { GFA::Field::Char.new(' ') }
|
9
|
+
assert_raise { GFA::Field::Char.new('') }
|
10
|
+
assert_raise { GFA::Field::Char.new('^.^') }
|
17
11
|
end
|
18
12
|
|
19
13
|
def test_sigint
|
20
14
|
end
|
21
15
|
|
22
16
|
def test_float
|
23
|
-
f = GFA::Field::Float.new(
|
17
|
+
f = GFA::Field::Float.new('1.3e-5')
|
24
18
|
assert_equal(1.3e-5, f.value)
|
25
|
-
assert_raise
|
26
|
-
GFA::Field::Float.new("e-5")
|
27
|
-
end
|
19
|
+
assert_raise { GFA::Field::Float.new('e-5') }
|
28
20
|
end
|
29
21
|
|
30
22
|
def test_string
|
31
23
|
end
|
32
24
|
|
33
25
|
def test_hex
|
34
|
-
f = GFA::Field::Hex.new(
|
35
|
-
assert_equal(
|
36
|
-
assert_raise
|
37
|
-
GFA::Field::Hex.new("C3PO")
|
38
|
-
end
|
26
|
+
f = GFA::Field::Hex.new('C3F0')
|
27
|
+
assert_equal('C3F0', f.value)
|
28
|
+
assert_raise { GFA::Field::Hex.new('C3PO') }
|
39
29
|
end
|
40
30
|
|
41
31
|
def test_numarray
|
42
|
-
f = GFA::Field::NumArray.new(
|
43
|
-
assert_equal(
|
44
|
-
assert_equal(
|
45
|
-
assert_raise
|
46
|
-
GFA::Field::NumArray.new("c,1,e,3")
|
47
|
-
end
|
32
|
+
f = GFA::Field::NumArray.new('i,1,2,3')
|
33
|
+
assert_equal([1, 2, 3], f.array)
|
34
|
+
assert_equal('i', f.modifier)
|
35
|
+
assert_raise { GFA::Field::NumArray.new('c,1,e,3') }
|
48
36
|
end
|
49
37
|
|
38
|
+
def test_equal
|
39
|
+
f = GFA::Field::SigInt.new('123')
|
40
|
+
j = GFA::Field::String.new('123')
|
41
|
+
k = GFA::Field::Float.new('123')
|
42
|
+
assert(f == 123)
|
43
|
+
assert(123 == f)
|
44
|
+
assert(f != 123.0)
|
45
|
+
assert(f != '123')
|
46
|
+
assert(f.eql?(123))
|
47
|
+
assert(f != j)
|
48
|
+
assert(f != k)
|
49
|
+
assert(f != k.value)
|
50
|
+
assert(f.value == k.value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_equivalent
|
54
|
+
# String comparisons
|
55
|
+
assert(GFA::Field['Z:a'].~ GFA::Field['A:a'])
|
56
|
+
assert(GFA::Field['Z:ab'] !~ GFA::Field['A:a'])
|
57
|
+
assert(GFA::Field['Z:{"a":1}'].~ GFA::Field['J:{"a":1}'])
|
58
|
+
assert(GFA::Field['J:{"a":1}'].~ GFA::Field['Z:{"a":1}'])
|
59
|
+
|
60
|
+
# Numeric comparisons
|
61
|
+
assert(GFA::Field['Z:123'].~ GFA::Field['i:123'])
|
62
|
+
assert(GFA::Field['Z:123'].~ GFA::Field['i:123'])
|
63
|
+
assert(GFA::Field['i:123'].~ GFA::Field['f:123'])
|
64
|
+
assert(GFA::Field['f:123'].~ GFA::Field['B:i,123'])
|
65
|
+
assert(GFA::Field['B:i,123'].~ GFA::Field['H:7B'])
|
66
|
+
assert(GFA::Field['H:7B'].~ GFA::Field['f:123.0'])
|
67
|
+
assert(GFA::Field['Z:123'] !~ GFA::Field['H:7B']) # In hex-space!
|
68
|
+
assert(GFA::Field['f:1e3'].~ GFA::Field['f:1000'])
|
69
|
+
assert(GFA::Field['f:1e3'].~ 1e3)
|
70
|
+
assert(GFA::Field['B:i,123,456'].~ [123, 456.0])
|
71
|
+
|
72
|
+
# Non-commutative
|
73
|
+
assert(GFA::Field['i:123'].~ GFA::Field['f:123.4'])
|
74
|
+
assert(GFA::Field['f:123.4'] !~ GFA::Field['i:123'])
|
75
|
+
end
|
50
76
|
end
|
data/test/parser_test.rb
CHANGED
@@ -2,20 +2,55 @@ require "test_helper"
|
|
2
2
|
require "gfa/parser"
|
3
3
|
|
4
4
|
class ParserTest < Test::Unit::TestCase
|
5
|
-
|
5
|
+
|
6
6
|
def test_load
|
7
|
-
sample_f = File.expand_path('../fixtures/sample.gfa', __FILE__)
|
8
7
|
assert_respond_to(GFA, :load)
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
|
9
|
+
# Can load files and close pointers properly
|
10
|
+
pre_fhs = ObjectSpace.each_object(IO).count { |i| not i.closed? }
|
11
|
+
assert_nothing_raised do
|
12
|
+
GFA.load(fixture_path('sample1.gfa'))
|
13
|
+
end
|
14
|
+
assert_nothing_raised do
|
15
|
+
GFA.load(fixture_path('sample2.gfa'))
|
16
|
+
end
|
17
|
+
assert_nothing_raised do
|
18
|
+
GFA.load(fixture_path('sample3.gfa'))
|
19
|
+
end
|
20
|
+
assert_raise do
|
21
|
+
GFA.load(fixture_path('sample4.gfa'))
|
22
|
+
end
|
23
|
+
post_fhs = ObjectSpace.each_object(IO).count { |i| not i.closed? }
|
12
24
|
assert_equal(pre_fhs, post_fhs)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_records
|
28
|
+
# Samples are properly parsed
|
29
|
+
sample1 = GFA.load(fixture_path('sample1.gfa'))
|
30
|
+
assert_equal(1, sample1.headers.size)
|
31
|
+
assert_equal(6, sample1.segments.size)
|
32
|
+
assert_equal(4, sample1.links.size)
|
33
|
+
assert(sample1.containments.empty?)
|
34
|
+
assert(sample1.paths.empty?)
|
35
|
+
assert_respond_to(sample1, :records)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_comments
|
39
|
+
path = fixture_path('sample2.gfa')
|
40
|
+
sample = GFA.load(path)
|
41
|
+
assert(sample.comments.empty?)
|
42
|
+
sample = GFA.load(path, comments: true)
|
43
|
+
assert(!sample.comments.empty?)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_index
|
47
|
+
path = fixture_path('sample3.gfa')
|
48
|
+
sample = GFA.load(path)
|
49
|
+
assert(sample.path('first').is_a?(GFA::Record))
|
50
|
+
assert(sample.paths['first'].is_a?(GFA::Record))
|
51
|
+
assert_equal('first', sample.path('first')[2]&.value)
|
52
|
+
sample = GFA.load(path, index: false)
|
53
|
+
assert_nil(sample.path('first'))
|
19
54
|
end
|
20
55
|
|
21
56
|
def test_version_suppport
|
@@ -28,20 +63,22 @@ class ParserTest < Test::Unit::TestCase
|
|
28
63
|
def test_line_by_line
|
29
64
|
gfa = GFA.new
|
30
65
|
assert_respond_to(gfa, :<<)
|
66
|
+
|
31
67
|
# Empty
|
32
68
|
gfa << ' '
|
33
69
|
assert(gfa.empty?)
|
34
70
|
gfa << 'H'
|
35
71
|
assert(gfa.empty?)
|
72
|
+
|
36
73
|
# Segment
|
37
74
|
assert_equal(0, gfa.segments.size)
|
38
|
-
gfa << "S\t1\tACTG"
|
75
|
+
gfa << "S\t1\tACTG\n"
|
39
76
|
assert(!gfa.empty?)
|
40
77
|
assert_equal(1, gfa.segments.size)
|
78
|
+
|
41
79
|
# Version
|
42
80
|
assert_nil(gfa.gfa_version)
|
43
81
|
gfa << GFA::Record::Header.new('VN:Z:1.0')
|
44
82
|
assert_equal('1.0', gfa.gfa_version)
|
45
83
|
end
|
46
|
-
|
47
84
|
end
|
data/test/record_test.rb
CHANGED
@@ -17,6 +17,13 @@ class RecordTest < Test::Unit::TestCase
|
|
17
17
|
assert_equal("P\ta\tb\t*", $rec_p.to_s)
|
18
18
|
end
|
19
19
|
|
20
|
+
def test_init_by_string
|
21
|
+
p = GFA::Record["P\ta\tb\t*"]
|
22
|
+
assert_equal('a', p.path_name&.value)
|
23
|
+
c = GFA::Record["# doink!\n"]
|
24
|
+
assert_equal(' doink!', c.comment&.value)
|
25
|
+
end
|
26
|
+
|
20
27
|
def test_hash
|
21
28
|
other_h = GFA::Record::Header.new("VN:Z:1.0")
|
22
29
|
assert_equal($rec_h.hash, other_h.hash)
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gfa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luis M. Rodriguez-R
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rgl
|
@@ -85,6 +85,15 @@ files:
|
|
85
85
|
- lib/gfa/record/path.rb
|
86
86
|
- lib/gfa/record/segment.rb
|
87
87
|
- lib/gfa/record/walk.rb
|
88
|
+
- lib/gfa/record_set.rb
|
89
|
+
- lib/gfa/record_set/comment_set.rb
|
90
|
+
- lib/gfa/record_set/containment_set.rb
|
91
|
+
- lib/gfa/record_set/header_set.rb
|
92
|
+
- lib/gfa/record_set/jump_set.rb
|
93
|
+
- lib/gfa/record_set/link_set.rb
|
94
|
+
- lib/gfa/record_set/path_set.rb
|
95
|
+
- lib/gfa/record_set/segment_set.rb
|
96
|
+
- lib/gfa/record_set/walk_set.rb
|
88
97
|
- lib/gfa/version.rb
|
89
98
|
- test/common_test.rb
|
90
99
|
- test/field_test.rb
|
@@ -94,7 +103,7 @@ files:
|
|
94
103
|
homepage: https://github.com/lmrodriguezr/gfa
|
95
104
|
licenses: []
|
96
105
|
metadata: {}
|
97
|
-
post_install_message:
|
106
|
+
post_install_message:
|
98
107
|
rdoc_options:
|
99
108
|
- lib
|
100
109
|
- README.md
|
@@ -115,8 +124,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
124
|
- !ruby/object:Gem::Version
|
116
125
|
version: '0'
|
117
126
|
requirements: []
|
118
|
-
rubygems_version: 3.
|
119
|
-
signing_key:
|
127
|
+
rubygems_version: 3.1.6
|
128
|
+
signing_key:
|
120
129
|
specification_version: 4
|
121
130
|
summary: Graphical Fragment Assembly (GFA) for Ruby
|
122
131
|
test_files: []
|