module Athena::Formats
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.
Defining custom formats¶ ↑
Define one or more classes that inherit from Athena::Formats::Base
and either call Athena::Formats.register
with your format class as parameter or call Athena::Formats.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.
Defining an input format¶ ↑
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
.
Defining an output format¶ ↑
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
.
Aliases¶ ↑
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.
Constants
- CRLF
CR+LF line ending.
- CRLF_RE
Regular expression to match (multiple) CR+LF line endings.
- METHODS
Mapping of format direction to method required for implementation.
- Midos
Attributes
Public Class Methods
Retrieves the format for direction
by its name format
(see ::find
) and initializes it with args
(see Base#init
). Returns format
unaltered if it already is a format instance, while making sure that it supports direction
.
# File lib/athena/formats.rb 98 def [](direction, format, *args) 99 res = find(direction, format, true) 100 res.is_a?(Base) ? res : res.init(direction, *args) 101 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 109 def find(direction, format, instance = false) 110 directions = formats.keys 111 112 unless directions.include?(direction) 113 raise ArgumentError, "invalid direction: #{direction.inspect}" << 114 " (must be one of: #{directions.map { |d| d.inspect }.join(', ')})" 115 end 116 117 case format 118 when Symbol 119 find(direction, format.to_s) 120 when String 121 formats[direction][format] or 122 raise FormatNotFoundError.new(direction, format) 123 else 124 klass = format.class 125 126 if klass < Base && !(directions = klass.directions).empty? 127 if klass.has_direction?(direction) 128 return instance ? format : klass 129 else 130 raise DirectionMismatchError.new(direction, directions) 131 end 132 else 133 raise ArgumentError, "invalid format of type #{klass}" << 134 " (expected one of: Symbol, String, or sub-class of #{Base})" 135 end 136 end 137 end
Formats
name
as suitable format name.
# File lib/athena/formats.rb 214 def format_name(fn) 215 fn.sub(/\A#{self}::/, ''). 216 gsub(/([a-z\d])(?=[A-Z])/, '\1_'). 217 gsub(/::/, '/').downcase 218 end
Registers klass
as format under name
(defaults to 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 161 def register(klass, name = nil, relax = false) 162 unless klass < Base 163 return if relax 164 raise ArgumentError, "must be a sub-class of #{Base}" 165 end 166 167 name = name ? name.to_s : klass.format 168 methods = klass.public_instance_methods(false).map { |m| m.to_s } 169 directions = klass.directions 170 171 METHODS.each { |direction, method| 172 next unless methods.include?(method) 173 174 if formats[direction].has_key?(name) 175 err = DuplicateFormatDefinitionError.new(direction, name) 176 raise err unless relax 177 178 warn err 179 next 180 else 181 directions << direction unless klass.has_direction?(direction) 182 formats[direction][name] = klass 183 end 184 } 185 186 [name, directions] unless directions.empty? 187 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 195 def register_all(klass = self, registered = []) 196 names = klass.constants 197 names -= klass.superclass.constants if klass.is_a?(Class) 198 199 names.each { |name| 200 const = klass.const_get(name) 201 next unless const.is_a?(Module) 202 203 registered << register(const, format_name("#{klass}::#{name}"), true) 204 register_all(const, registered) 205 } 206 207 registered.compact 208 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 145 def valid_format?(direction, format) 146 if format.class < Base 147 format.class.has_direction?(direction) 148 else 149 formats[direction].has_key?(format.to_s) 150 end 151 end