In order to support additional input and/or output formats, Athena::Formats::Base needs to be sub-classed and an instance method parse or an instance method convert supplied, respectively. This way, a specific format can even function as both input and output format.
Define one or more classes that inherit from Athena::Formats::Base and either call ::register with your format class as parameter or call ::register_all with your surrounding namespace (which will then recursively add any format definitions below it). Alternatively, you can call Athena::Formats::Base.register_format at the end of your class definition to register just this class.
The directions supported by your custom format are determined automatically; see below for further details.
An input format must provide a public instance method parse that accepts an input object (usually an IO object) and a block it passes each record (Athena::Record) to. See Athena::Formats::Base#parse.
An output format must provide a public instance method convert that accepts a record (Athena::Record) and either returns a suitable value for output or writes the output itself. See Athena::Formats::Base#convert.
In order to provide an alias name for a format, simply assign the format
class to a new constant. Then you need to call
Athena::Formats.register(your_new_const)
to register your
alias.
CR+LF line ending.
Regular expression to match (multiple) CR+LF line endings.
Mapping of format direction to method required for implementation.
Container for registered formats per direction. Use ::find or ::[] to access them.
Retrieves the format for direction
by its name
format
(see ::find)
and initializes it with args
(see Athena::Formats::Base#init).
Returns format
unaltered if it already is a format instance,
while making sure that it supports direction
.
# File lib/athena/formats.rb, line 98 def [](direction, format, *args) res = find(direction, format, true) res.is_a?(Base) ? res : res.init(direction, *args) end
Retrieves the format for direction
by its name
format
. Returns format
's class if it already
is a format instance, while making sure that it supports
direction
.
# File lib/athena/formats.rb, line 109 def find(direction, format, instance = false) directions = formats.keys unless directions.include?(direction) raise ArgumentError, "invalid direction: #{direction.inspect}" << " (must be one of: #{directions.map { |d| d.inspect }.join(', ')})" end case format when Symbol find(direction, format.to_s) when String formats[direction][format] or raise FormatNotFoundError.new(direction, format) else klass = format.class if klass < Base && !(directions = klass.directions).empty? if klass.has_direction?(direction) return instance ? format : klass else raise DirectionMismatchError.new(direction, directions) end else raise ArgumentError, "invalid format of type #{klass}" << " (expected one of: Symbol, String, or sub-class of #{Base})" end end end
Formats name
as suitable format
name.
# File lib/athena/formats.rb, line 214 def format_name(fn) fn.sub(/\A#{self}::/, ''). gsub(/([a-z\d])(?=[A-Z])/, '\1_'). gsub(/::/, '/').downcase end
Registers klass
as format under name
(defaults to
Athena::Formats::Base.format).
Only warns instead of raising any errors when relax
is
true
. Returns an array of the actual name klass
has been registered under and the directions supported; returns
nil
if nothing has been registered.
# File lib/athena/formats.rb, line 161 def register(klass, name = nil, relax = false) unless klass < Base return if relax raise ArgumentError, "must be a sub-class of #{Base}" end name = name ? name.to_s : klass.format methods = klass.public_instance_methods(false).map { |m| m.to_s } directions = klass.directions METHODS.each { |direction, method| next unless methods.include?(method) if formats[direction].has_key?(name) err = DuplicateFormatDefinitionError.new(direction, name) raise err unless relax warn err next else directions << direction unless klass.has_direction?(direction) formats[direction][name] = klass end } [name, directions] unless directions.empty? end
Recursively registers all formats below klass
(see ::register). Returns an
array of all registered format names with their supported directions.
# File lib/athena/formats.rb, line 195 def register_all(klass = self, registered = []) names = klass.constants names -= klass.superclass.constants if klass.is_a?(Class) names.each { |name| const = klass.const_get(name) next unless const.is_a?(Module) registered << register(const, format_name("#{klass}::#{name}"), true) register_all(const, registered) } registered.compact end
Indicates whether the direction
/format
combination is supported, i.e. a format by name format
has
been registered and supports direction
.
# File lib/athena/formats.rb, line 145 def valid_format?(direction, format) if format.class < Base format.class.has_direction?(direction) else formats[direction].has_key?(format.to_s) end end