Class: RGFA::Line
Overview
different record types.
Direct Known Subclasses
Containment, Header, Link, Path, Segment
Defined Under Namespace
Classes: Containment, CustomOptfieldNameError, DuplicatedOptfieldNameError, FieldnameError, Header, Link, Path, PredefinedOptfieldTypeError, RequiredFieldMissingError, Segment, TagMissingError, UnknownDatatype, UnknownRecordTypeError
Constant Summary collapse
- SEPARATOR =
Separator in the string representation of RGFA lines
"\t"- RECORD_TYPES =
List of allowed record_type values
[ :H, :S, :L, :C, :P ]
- RECORD_TYPE_LABELS =
Full name of the record types
{ :H => "header", :S => "segment", :L => "link", :C => "containment", :P => "path", }
- OPTFIELD_DATATYPE =
A symbol representing a datatype for optional fields
[:A, :i, :f, :Z, :J, :H, :B]
- REQFIELD_DATATYPE =
A symbol representing a datatype for required fields
[:lbl, :orn, :lbs, :seq, :pos, :cig, :cgs]
- FIELD_DATATYPE =
A symbol representing a valid datatype
OPTFIELD_DATATYPE + REQFIELD_DATATYPE
- DELAYED_PARSING_DATATYPES =
List of data types which are parsed only on access; all other are parsed when read.
[:cig, :cgs, :lbs, :H, :J, :B]
- DIRECTION =
Direction of a segment for links/containments
[:from, :to]
- ORIENTATION =
Orientation of segments in paths/links/containments
[:+, :-]
Class Method Summary collapse
-
.subclass(record_type) ⇒ Class
Select a subclass based on the record type.
Instance Method Summary collapse
-
#==(o) ⇒ Boolean
Equivalence check.
-
#clone ⇒ RGFA::Line
Deep copy of a RGFA::Line instance.
-
#delete(fieldname) ⇒ Object?
Remove an optional field from the line, if it exists; do nothing if it does not.
-
#field_to_s(fieldname, optfield: false) ⇒ String
Compute the string representation of a field.
-
#fieldnames ⇒ Array<Symbol>
Fields defined for this instance.
-
#get(fieldname, frozen: false) ⇒ Object?
Get the value of a field.
-
#get!(fieldname) ⇒ Object?
Value of a field, raising an exception if it is not defined.
-
#get_datatype(fieldname) ⇒ RGFA::Line::FIELD_DATATYPE
Returns a symbol, which specifies the datatype of a field.
-
#initialize(data, validate: 2, virtual: false) ⇒ RGFA::Line
constructor
Constants defined by subclasses .
-
#method_missing(m, *args, &block) ⇒ Object
Methods are dynamically created for non-existing but valid optional field names.
-
#optional_fieldnames ⇒ Array<Symbol>
Name of the optional fields.
-
#real!(real_line) ⇒ Object
private
Make a virtual line real.
-
#record_type ⇒ Symbol
Record type code.
-
#required_fieldnames ⇒ Array<Symbol>
Name of the required fields.
-
#respond_to?(m, include_all = false) ⇒ Boolean
Redefines respond_to? to correctly handle dynamical methods.
-
#set(fieldname, value) ⇒ Object
Set the value of a field.
-
#set_datatype(fieldname, datatype) ⇒ RGFA::Line::FIELD_DATATYPE
Set the datatype of a field.
-
#tags ⇒ Array<[Symbol, Symbol, Object]>
Returns the optional fields as an array of [fieldname, datatype, value] arrays.
-
#to_a ⇒ Array<String>
An array of string representations of the fields.
-
#to_rgfa_line(validate: nil) ⇒ Object
Self.
-
#to_s ⇒ String
A string representation of self.
-
#validate! ⇒ void
Validate the RGFA::Line instance.
-
#validate_field!(fieldname) ⇒ void
Raises an error if the content of the field does not correspond to the field type.
-
#virtual? ⇒ Boolean
private
Is the line virtual?.
Constructor Details
#initialize(data, validate: 2, virtual: false) ⇒ RGFA::Line
Constants defined by subclasses
Subclasses of RGFA::Line must define the following constants:
-
RECORD_TYPE [RGFA::Line::RECORD_TYPES]
-
REQFIELDS [Array<Symbol>] required fields
-
PREDEFINED_OPTFIELDS [Array<Symbol>] predefined optional fields
-
DATATYPE [HashSymbol=>Symbol]: datatypes for the required fields and the predefined optional fields
Validation levels
The default is 2, i.e. if a field content is changed, the user is responsible to call #validate_field!, if necessary.
-
0: no validation
-
1: the number of required fields must be correct; optional fields
cannot be duplicated; custom optional field names must be correct; predefined optional fields must have the correct type; only some fields are validated on initialization or first-time access to the field content -
2: 1 + all fields are validated on initialization or first-time
access to the field content -
3: 2 + all fields are validated on initialization and record-specific
validations are run (e.g. compare segment LN tag and sequence lenght) -
4: 3 + all fields are validated on writing to string
-
5: 4 + all fields are validated by get and set methods
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/rgfa/line.rb', line 101 def initialize(data, validate: 2, virtual: false) unless self.class.const_defined?(:"RECORD_TYPE") raise RuntimeError, "This class shall not be directly instantiated" end @validate = validate @virtual = virtual @datatype = {} @data = {} if data.kind_of?(Hash) @data.merge!(data) else # normal initialization, data is an array of strings initialize_required_fields(data) initialize_optional_fields(data) validate_record_type_specific_info! if @validate >= 3 end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(m, *args, &block) ⇒ Object
Methods are dynamically created for non-existing but valid optional field names. Methods for predefined optional fields and required fields are created dynamically for each subclass; methods for existing optional fields are created on instance initialization.
- (Object) <fieldname>(parse=true)
The parsed content of a field. See also #get.
Parameters:
Returns:
-
(String, Hash, Array, Integer, Float) the parsed content of the field
-
(nil) if the field does not exist, but is a valid optional field name
- (Object) <fieldname>!(parse=true)
The parsed content of a field, raising an exception if not available. See also #get!.
Returns:
-
(String, Hash, Array, Integer, Float) the parsed content of the field
Raises:
-
(RGFA::Line::TagMissingError) if the field does not exist
- (self) <fieldname>=(value)
Sets the value of a required or optional field, or creates a new optional field if the fieldname is non-existing but valid. See also #set, #set_datatype.
Parameters:
-
value (String|Hash|Array|Integer|Float) value to set
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/rgfa/line.rb', line 405 def method_missing(m, *args, &block) field_name, operation, state = split_method_name(m) if ((operation == :get or operation == :get!) and args.size > 1) or (operation == :set and args.size != 1) raise ArgumentError, "wrong number of arguments" end case state when :invalid super when :existing case operation when :get if args[0] == false field_to_s(field_name) else get(field_name) end when :get! if args[0] == false field_to_s!(field_name) else get!(field_name) end when :set set_existing_field(field_name, args[0]) return nil end when :valid case operation when :get return nil when :get! raise RGFA::Line::TagMissingError, "No value defined for tag #{field_name}" when :set set(field_name, args[0]) return nil end end end |
Class Method Details
.subclass(record_type) ⇒ Class
Select a subclass based on the record type
122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/rgfa/line.rb', line 122 def self.subclass(record_type) case record_type.to_sym when :H then RGFA::Line::Header when :S then RGFA::Line::Segment when :L then RGFA::Line::Link when :C then RGFA::Line::Containment when :P then RGFA::Line::Path else raise RGFA::Line::UnknownRecordTypeError, "Record type unknown: '#{record_type}'" end end |
Instance Method Details
#==(o) ⇒ Boolean
Equivalence check
463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/rgfa/line.rb', line 463 def ==(o) return self.to_sym == o.to_sym if o.kind_of?(Symbol) return false if (o.record_type != self.record_type) return false if o.data.keys.sort != data.keys.sort o.data.each do |k, v| if @data[k] != o.data[k] if field_to_s(k) != o.field_to_s(k) return false end end end return true end |
#clone ⇒ RGFA::Line
Deep copy of a RGFA::Line instance.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/rgfa/line.rb', line 157 def clone data_cpy = {} @data.each_pair do |k, v| if field_datatype(k) == :J data_cpy[k] = JSON.parse(v.to_json) elsif v.kind_of?(Array) or v.kind_of?(String) data_cpy[k] = v.clone else data_cpy[k] = v end end cpy = self.class.new(data_cpy, validate: @validate, virtual: @virtual) cpy.instance_variable_set("@datatype", @datatype.clone) return cpy end |
#delete(fieldname) ⇒ Object?
Remove an optional field from the line, if it exists;
do nothing if it does not
224 225 226 227 228 229 230 231 |
# File 'lib/rgfa/line.rb', line 224 def delete(fieldname) if optional_fieldnames.include?(fieldname) @datatype.delete(fieldname) return @data.delete(fieldname) else return nil end end |
#field_to_s(fieldname, optfield: false) ⇒ String
Compute the string representation of a field.
256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/rgfa/line.rb', line 256 def field_to_s(fieldname, optfield: false) field = @data[fieldname] raise RGFA::Line::TagMissingError, "No value defined for tag #{fieldname}" if field.nil? t = field_or_default_datatype(fieldname, field) if !field.kind_of?(String) field = field.to_gfa_field(datatype: t) end field.validate_gfa_field!(t, fieldname) if @validate >= 4 return optfield ? field.to_gfa_optfield(fieldname, datatype: t) : field end |
#fieldnames ⇒ Array<Symbol>
Returns fields defined for this instance.
141 142 143 |
# File 'lib/rgfa/line.rb', line 141 def fieldnames @data.keys end |
#get(fieldname, frozen: false) ⇒ Object?
Get the value of a field
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/rgfa/line.rb', line 335 def get(fieldname, frozen: false) v = @data[fieldname] if v.kind_of?(String) t = field_datatype(fieldname) if t != :Z and t != :seq # value was not parsed or was set to a string by the user return (@data[fieldname] = v.parse_gfa_field(datatype: t, validate_strings: @validate >= 2)) else v.validate_gfa_field!(t, fieldname) if (@validate >= 5) end elsif !v.nil? if (@validate >= 5) t = field_datatype(fieldname) v.validate_gfa_field!(t, fieldname) end end return v end |
#get!(fieldname) ⇒ Object?
Value of a field, raising an exception if it is not defined
360 361 362 363 364 365 |
# File 'lib/rgfa/line.rb', line 360 def get!(fieldname) v = get(fieldname) raise RGFA::Line::TagMissingError, "No value defined for tag #{fieldname}" if v.nil? return v end |
#get_datatype(fieldname) ⇒ RGFA::Line::FIELD_DATATYPE
Returns a symbol, which specifies the datatype of a field
272 273 274 |
# File 'lib/rgfa/line.rb', line 272 def get_datatype(fieldname) field_or_default_datatype(fieldname, @data[fieldname]) end |
#optional_fieldnames ⇒ Array<Symbol>
Returns name of the optional fields.
151 152 153 |
# File 'lib/rgfa/line.rb', line 151 def optional_fieldnames (@data.keys - self.class::REQFIELDS) end |
#real!(real_line) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Make a virtual line real. This is called when a line which is expected, and for which a virtual line has been created, is finally found. So the line is converted into a real line, by merging in the line information from the found line.
189 190 191 192 193 194 |
# File 'lib/rgfa/line.rb', line 189 def real!(real_line) @virtual = false real_line.data.each_pair do |k, v| @data[k] = v end end |
#record_type ⇒ Symbol
Returns record type code.
136 137 138 |
# File 'lib/rgfa/line.rb', line 136 def record_type self.class::RECORD_TYPE end |
#required_fieldnames ⇒ Array<Symbol>
Returns name of the required fields.
146 147 148 |
# File 'lib/rgfa/line.rb', line 146 def required_fieldnames self.class::REQFIELDS end |
#respond_to?(m, include_all = false) ⇒ Boolean
Redefines respond_to? to correctly handle dynamical methods.
448 449 450 |
# File 'lib/rgfa/line.rb', line 448 def respond_to?(m, include_all=false) super || (split_method_name(m)[2] != :invalid) end |
#set(fieldname, value) ⇒ Object
Set the value of a field.
If a datatype for a new custom optional field is not set, the default for the value assigned to the field will be used (e.g. J for Hashes, i for Integer, etc).
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/rgfa/line.rb', line 310 def set(fieldname, value) if @data.has_key?(fieldname) or predefined_optional_fieldname?(fieldname) return set_existing_field(fieldname, value) elsif (@validate == 0) or valid_custom_optional_fieldname?(fieldname) define_field_methods(fieldname) if !@datatype[fieldname].nil? return set_existing_field(fieldname, value) elsif !value.nil? @datatype[fieldname] = value.default_gfa_datatype return @data[fieldname] = value end else raise RGFA::Line::FieldnameError, "#{fieldname} is not an existing or predefined field or a "+ "valid custom optional field" end end |
#set_datatype(fieldname, datatype) ⇒ RGFA::Line::FIELD_DATATYPE
Set the datatype of a field.
If an existing field datatype is changed, its content may become invalid (call #validate_field! if necessary).
If the method is used for a required field or a predefined field, the line will use the specified datatype instead of the predefined one, resulting in a potentially invalid line.
291 292 293 294 295 296 |
# File 'lib/rgfa/line.rb', line 291 def set_datatype(fieldname, datatype) unless OPTFIELD_DATATYPE.include?(datatype) raise RGFA::Line::UnknownDatatype, "Unknown datatype: #{datatype}" end @datatype[fieldname] = datatype end |
#tags ⇒ Array<[Symbol, Symbol, Object]>
Returns the optional fields as an array of [fieldname, datatype, value] arrays.
212 213 214 215 216 217 218 |
# File 'lib/rgfa/line.rb', line 212 def retval = [] optional_fieldnames.each do |of| retval << [of, get_datatype(of), get(of)] end return retval end |
#to_a ⇒ Array<String>
Returns an array of string representations of the fields.
202 203 204 205 206 207 |
# File 'lib/rgfa/line.rb', line 202 def to_a a = [record_type] required_fieldnames.each {|fn| a << field_to_s(fn, optfield: false)} optional_fieldnames.each {|fn| a << field_to_s(fn, optfield: true)} return a end |
#to_rgfa_line(validate: nil) ⇒ Object
Returns self.
454 455 456 |
# File 'lib/rgfa/line.rb', line 454 def to_rgfa_line(validate: nil) self end |
#to_s ⇒ String
Returns a string representation of self.
197 198 199 |
# File 'lib/rgfa/line.rb', line 197 def to_s to_a.join(SEPARATOR) end |
#validate! ⇒ void
This method returns an undefined value.
Validate the RGFA::Line instance
480 481 482 483 |
# File 'lib/rgfa/line.rb', line 480 def validate! fieldnames.each {|fieldname| validate_field!(fieldname) } validate_record_type_specific_info! end |
#validate_field!(fieldname) ⇒ void
This method returns an undefined value.
Raises an error if the content of the field does not correspond to the field type
240 241 242 243 244 245 |
# File 'lib/rgfa/line.rb', line 240 def validate_field!(fieldname) v = @data[fieldname] t = field_or_default_datatype(fieldname, v) v.validate_gfa_field!(t, fieldname) return nil end |
#virtual? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Is the line virtual?
Is this RGFA::Line a virtual line repreentation (i.e. a placeholder for an expected but not encountered yet line)?
179 180 181 |
# File 'lib/rgfa/line.rb', line 179 def virtual? @virtual end |