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

formats[R]

Container for registered formats per direction. Use ::find or ::[] to access them.

Public Class Methods

Athena::Formats[direction, format, *args] → aFormat click to toggle source

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
Athena::Formats.find(direction, format) → aFormatClass click to toggle source

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
Athena::Formats.format_name(name) → aString click to toggle source

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
Athena::Formats.register(klass, name = nil, relax = false) → anArray | nil click to toggle source

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
Athena::Formats.register_all(klass = self) → anArray click to toggle source

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
Athena::Formats.valid_format?(direction, format) → true | false click to toggle source

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