Module: Athena::Formats

Defined in:
lib/athena/formats.rb,
lib/athena/formats/dbm.rb,
lib/athena/formats/sql.rb,
lib/athena/formats/xml.rb,
lib/athena/formats/lingo.rb,
lib/athena/formats/sisis.rb,
lib/athena/formats/ferret.rb

Overview

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.

Defined Under Namespace

Classes: Base, ConfigError, DBM, DirectionMismatchError, DuplicateFormatDefinitionError, Ferret, FormatError, FormatNotFoundError, IllegalRecordElementError, Lingo, MYSQL, MySQL, NoRecordElementError, PGSQL, PgSQL, Sisis, XML

Constant Summary collapse

CRLF =

CR+LF line ending.

%Q{\015\012}
CRLF_RE =

Regular expression to match (multiple) CR+LF line endings.

%r{(?:\r?\n)+}
METHODS =

Mapping of format direction to method required for implementation.

{ :in => 'parse', :out => 'convert' }
Midos =
DBM

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.formatsObject (readonly)

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



89
90
91
# File 'lib/athena/formats.rb', line 89

def formats
  @formats
end

Class Method Details

.[](direction, format, *args) ⇒ Object

call-seq:

Athena::Formats[direction, format, *args] -> aFormat

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.



98
99
100
101
# 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

.find(direction, format, instance = false) ⇒ Object

call-seq:

Athena::Formats.find(direction, format) -> aFormatClass

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.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# 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

.format_name(fn) ⇒ Object

call-seq:

Athena::Formats.format_name(name) -> aString

Formats name as suitable format name.



214
215
216
217
218
# 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

.register(klass, name = nil, relax = false) ⇒ Object

call-seq:

Athena::Formats.register(klass, name = nil, relax = false) -> anArray | nil

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.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# 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

.register_all(klass = self, registered = []) ⇒ Object

call-seq:

Athena::Formats.register_all(klass = self) -> anArray

Recursively registers all formats below klass (see ::register). Returns an array of all registered format names with their supported directions.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
# 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

.valid_format?(direction, format) ⇒ Boolean

call-seq:

Athena::Formats.valid_format?(direction, format) -> true | false

Indicates whether the direction/format combination is supported, i.e. a format by name format has been registered and supports direction.

Returns:

  • (Boolean)


145
146
147
148
149
150
151
# 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