athena 0.1.5 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  == VERSION
4
4
 
5
- This documentation refers to athena version 0.1.5
5
+ This documentation refers to athena version 0.2.1
6
6
 
7
7
 
8
8
  == DESCRIPTION
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ begin
14
14
  :summary => %q{Convert database files to various formats.},
15
15
  :author => %q{Jens Wille},
16
16
  :email => %q{jens.wille@uni-koeln.de},
17
- :dependencies => ['builder', 'xmlstreamin', ['ruby-nuggets', '>= 0.6.4']]
17
+ :dependencies => %w[builder xmlstreamin] << ['ruby-nuggets', '>= 0.7.4']
18
18
  }
19
19
  }}
20
20
  rescue LoadError => err
data/bin/athena CHANGED
@@ -28,158 +28,5 @@
28
28
  ###############################################################################
29
29
  #++
30
30
 
31
- require 'optparse'
32
- require 'yaml'
33
-
34
- $: << File.join(File.dirname(__FILE__), '..', 'lib')
35
-
36
- require 'athena'
37
-
38
- USAGE = "Usage: #{$0} [-h|--help] [options]"
39
- abort USAGE if ARGV.empty?
40
-
41
- # Global variable to handle verbosity
42
- $Verbose = {}
43
-
44
- options = {
45
- :config => 'config.yaml',
46
- :input => STDIN,
47
- :output => STDOUT,
48
- :target => nil
49
- }
50
-
51
- OptionParser.new { |opts|
52
- opts.banner = USAGE
53
-
54
- opts.separator ''
55
- opts.separator 'Options:'
56
-
57
- opts.on('-c', '--config YAML', "Config file [Default: #{options[:config]}#{' (currently not present)' unless File.readable?(options[:config])}]") { |f|
58
- abort "Can't find config file: #{f}." unless File.readable?(f)
59
-
60
- options[:config] = f
61
- }
62
-
63
- opts.separator ''
64
-
65
- opts.on('-i', '--input FILE', "Input file [Default: STDIN]") { |f|
66
- abort "Can't find input file: #{f}." unless File.readable?(f)
67
-
68
- options[:input] = File.directory?(f) ? Dir.open(f) : File.open(f, 'r')
69
-
70
- p = File.basename(f).split('.')
71
- options[:spec_fallback] = p.last.downcase
72
- options[:target_fallback] = p.size > 1 ? p[0..-2].join('.') : p.first
73
- }
74
-
75
- opts.on('-s', '--spec SPEC', "Input format (spec) [Default: file extension of <input-file>]") { |s|
76
- options[:spec] = s.downcase
77
- }
78
-
79
- opts.on('-L', '--list-specs', "List available input formats (specs) and exit") {
80
- puts 'Available input formats (specs):'
81
-
82
- Athena.input_formats.each { |f, k|
83
- puts " - #{f}#{" (= #{k})" if f != k.to_s}"
84
- }
85
-
86
- exit 0
87
- }
88
-
89
- opts.separator ''
90
-
91
- opts.on('-o', '--output FILE', "Output file [Default: STDOUT]") { |f|
92
- options[:output] = File.open(f, 'w')
93
-
94
- options[:format_fallback] = f.split('.').last.downcase
95
- }
96
-
97
- opts.on('-f', '--format FORMAT', "Output format [Default: file extension of <output-file>]") { |f|
98
- options[:format] = f.downcase
99
- }
100
-
101
- opts.on('-l', '--list-formats', "List available output formats and exit") {
102
- puts 'Available output formats:'
103
-
104
- Athena.output_formats.each { |f, k|
105
- puts " - #{f}#{" (= #{k})" if f != k.to_s}"
106
- }
107
-
108
- exit 0
109
- }
110
-
111
- opts.separator ''
112
-
113
- opts.on('-t', '--target ID', "Target whose config to use [Default: <input-file> minus file extension,", "plus '.<spec>', plus ':<format>' (reversely in turn)]") { |t|
114
- options[:target] = t
115
- }
116
-
117
- opts.separator ''
118
- opts.separator 'Generic options:'
119
-
120
- opts.on('-v', '--verbose [WHAT]', "Be verbose about what's being done. Optional argument is a comma-separated", "list of what should be output, or 'all' [Default: 'all']") { |what|
121
- if what.nil? || what == 'all'
122
- $Verbose.default = true
123
- else
124
- what.split(',').each { |w|
125
- $Verbose[w.to_sym] = true
126
- }
127
- end
128
- }
129
-
130
- opts.on('-h', '--help', 'Print this help message and exit') {
131
- abort opts.to_s
132
- }
133
-
134
- opts.on('--version', 'Print program version and exit') {
135
- abort "#{File.basename($0)} v#{Athena::VERSION}"
136
- }
137
- }.parse!
138
-
139
- spec = options[:spec] || options[:spec_fallback]
140
- abort "No input format (spec) specified and none could be inferred." unless spec
141
- abort "Invalid input format (spec): #{spec}. Use '-L' to get a list of available specs." unless Athena.valid_input_format?(spec)
142
-
143
- format = options[:format] || options[:format_fallback]
144
- abort "No output format specified and none could be inferred." unless format
145
- abort "Invalid output format: #{format}. Use '-l' to get a list of available formats." unless Athena.valid_output_format?(format)
146
-
147
- yaml = YAML.load_file(options[:config])
148
- if t = options[:target]
149
- target = t
150
- config = yaml[t.to_sym]
151
- else
152
- [options[:target_fallback] || 'generic', ".#{spec}", ":#{format}"].inject([]) { |s, t|
153
- s << (s.last ? s.last + t : t)
154
- }.reverse.find { |t|
155
- target = t
156
- config = yaml[t.to_sym]
157
- }
158
- end
159
- abort "Config not found for target: #{target}." unless config
160
-
161
- parser = Athena.parser(config, spec)
162
-
163
- if Athena.deferred_output?(format)
164
- res = parser.parse(options[:input])
165
-
166
- res.map { |record|
167
- record.to(format)
168
- }.flatten.sort.uniq.each { |line|
169
- options[:output].puts line
170
- }
171
- elsif Athena.raw_output?(format)
172
- res = Athena.with_format(format, options[:output]) { |_format|
173
- parser.parse(options[:input]) { |record|
174
- record.to(_format)
175
- }
176
- }
177
- else
178
- res = Athena.with_format(format) { |_format|
179
- parser.parse(options[:input]) { |record|
180
- options[:output].puts record.to(_format)
181
- }
182
- }
183
- end
184
-
185
- Athena::Util.verbose(:count) { spit res.is_a?(Numeric) ? res : res.size }
31
+ require 'athena/cli'
32
+ Athena::CLI.execute
@@ -35,17 +35,14 @@
35
35
  # instance method _convert_ supplied. This way, a specific format can even function
36
36
  # as both input and output format.
37
37
 
38
- module Athena
39
- end
40
-
41
- require 'athena/util'
42
- require 'athena/parser'
43
- require 'athena/record'
44
- require 'athena/formats'
45
38
  require 'athena/version'
46
39
 
47
40
  module Athena
48
41
 
42
+ autoload :Parser, 'athena/parser'
43
+ autoload :Record, 'athena/record'
44
+ autoload :Formats, 'athena/formats'
45
+
49
46
  extend self
50
47
 
51
48
  def parser(config, format)
@@ -0,0 +1,166 @@
1
+ #--
2
+ ###############################################################################
3
+ # #
4
+ # A component of athena, the database file converter. #
5
+ # #
6
+ # Copyright (C) 2007-2011 University of Cologne, #
7
+ # Albertus-Magnus-Platz, #
8
+ # 50923 Cologne, Germany #
9
+ # #
10
+ # Authors: #
11
+ # Jens Wille <jens.wille@uni-koeln.de> #
12
+ # #
13
+ # athena is free software; you can redistribute it and/or modify it under the #
14
+ # terms of the GNU Affero General Public License as published by the Free #
15
+ # Software Foundation; either version 3 of the License, or (at your option) #
16
+ # any later version. #
17
+ # #
18
+ # athena is distributed in the hope that it will be useful, but WITHOUT ANY #
19
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
20
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
21
+ # more details. #
22
+ # #
23
+ # You should have received a copy of the GNU Affero General Public License #
24
+ # along with athena. If not, see <http://www.gnu.org/licenses/>. #
25
+ # #
26
+ ###############################################################################
27
+ #++
28
+
29
+ require 'nuggets/util/cli'
30
+ require 'athena'
31
+
32
+ module Athena
33
+
34
+ class CLI < ::Util::CLI
35
+
36
+ class << self
37
+
38
+ def defaults
39
+ super.merge(
40
+ :config => 'config.yaml',
41
+ :input => '-',
42
+ :output => '-',
43
+ :target => nil
44
+ )
45
+ end
46
+
47
+ end
48
+
49
+ def run(arguments)
50
+ spec = options[:spec] || options[:spec_fallback]
51
+ abort "No input format (spec) specified and none could be inferred." unless spec
52
+ abort "Invalid input format (spec): #{spec}. Use `-L' to get a list of available specs." unless Athena.valid_input_format?(spec)
53
+
54
+ format = options[:format] || options[:format_fallback]
55
+ abort "No output format specified and none could be inferred." unless format
56
+ abort "Invalid output format: #{format}. Use `-l' to get a list of available formats." unless Athena.valid_output_format?(format)
57
+
58
+ target_config = if t = options[:target]
59
+ config[target = t.to_sym]
60
+ else
61
+ [options[:target_fallback] || 'generic', ".#{spec}", ":#{format}"].inject([]) { |s, t|
62
+ s << (s.last ? s.last + t : t)
63
+ }.reverse.find { |t| config[target = t.to_sym] }
64
+ end or abort "Config not found for target: #{target}."
65
+
66
+ parser = Athena.parser(target_config, spec)
67
+
68
+ input = options[:input]
69
+ input = arguments.shift unless input != defaults[:input] || arguments.empty?
70
+ options[:input] = File.directory?(input) ? Dir.open(input) : open_file_or_std(input)
71
+
72
+ quit unless arguments.empty?
73
+
74
+ options[:output] = open_file_or_std(options[:output], true)
75
+
76
+ if Athena.deferred_output?(format)
77
+ res = parser.parse(options[:input])
78
+
79
+ res.map { |record|
80
+ record.to(format)
81
+ }.flatten.sort.uniq.each { |line|
82
+ options[:output].puts line
83
+ }
84
+ elsif Athena.raw_output?(format)
85
+ res = Athena.with_format(format, options[:output]) { |_format|
86
+ parser.parse(options[:input]) { |record|
87
+ record.to(_format)
88
+ }
89
+ }
90
+ else
91
+ res = Athena.with_format(format) { |_format|
92
+ parser.parse(options[:input]) { |record|
93
+ options[:output].puts record.to(_format)
94
+ }
95
+ }
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def merge_config(args = [default])
102
+ super
103
+ end
104
+
105
+ def opts(opts)
106
+ opts.on('-c', '--config YAML', "Config file [Default: #{defaults[:config]}#{' (currently not present)' unless File.readable?(defaults[:config])}]") { |config|
107
+ quit "Can't find config file: #{config}" unless File.readable?(config)
108
+
109
+ options[:config] = config
110
+ }
111
+
112
+ opts.separator ''
113
+
114
+ opts.on('-i', '--input FILE', "Input file [Default: STDIN]") { |input|
115
+ options[:input] = input
116
+
117
+ parts = File.basename(input).split('.')
118
+ options[:spec_fallback] = parts.last.downcase
119
+ options[:target_fallback] = parts.size > 1 ? parts[0..-2].join('.') : parts.first
120
+ }
121
+
122
+ opts.on('-s', '--spec SPEC', "Input format (spec) [Default: file extension of <input-file>]") { |spec|
123
+ options[:spec] = spec.downcase
124
+ }
125
+
126
+ opts.on('-L', '--list-specs', "List available input formats (specs) and exit") {
127
+ puts 'Available input formats (specs):'
128
+
129
+ Athena.input_formats.each { |format, name|
130
+ puts " - #{format}#{" (= #{name})" if format != name.to_s}"
131
+ }
132
+
133
+ exit
134
+ }
135
+
136
+ opts.separator ''
137
+
138
+ opts.on('-o', '--output FILE', "Output file [Default: STDOUT]") { |output|
139
+ options[:output] = output
140
+ options[:format_fallback] = output.split('.').last.downcase
141
+ }
142
+
143
+ opts.on('-f', '--format FORMAT', "Output format [Default: file extension of <output-file>]") { |format|
144
+ options[:format] = format.downcase
145
+ }
146
+
147
+ opts.on('-l', '--list-formats', "List available output formats and exit") {
148
+ puts 'Available output formats:'
149
+
150
+ Athena.output_formats.each { |format, name|
151
+ puts " - #{format}#{" (= #{name})" if format != name.to_s}"
152
+ }
153
+
154
+ exit
155
+ }
156
+
157
+ opts.separator ''
158
+
159
+ opts.on('-t', '--target ID', "Target whose config to use [Default: <input-file> minus file extension,", "plus '.<spec>', plus ':<format>' (reversely in turn)]") { |target|
160
+ options[:target] = target
161
+ }
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -26,114 +26,120 @@
26
26
  ###############################################################################
27
27
  #++
28
28
 
29
+ require 'athena'
30
+
29
31
  module Athena
32
+
30
33
  module Formats
31
34
 
32
- CRLF = %Q{\015\012}
33
- CRLF_RE = %r{(?:\r?\n)+}
35
+ CRLF = %Q{\015\012}
36
+ CRLF_RE = %r{(?:\r?\n)+}
34
37
 
35
- def self.[](direction, format)
36
- if direction == :out
37
- if format.class < Base
38
- if format.class.direction != direction
39
- raise DirectionMismatchError,
40
- "expected #{direction}, got #{format.class.direction}"
38
+ def self.[](direction, format)
39
+ if direction == :out
40
+ if format.class < Base
41
+ if format.class.direction != direction
42
+ raise DirectionMismatchError,
43
+ "expected #{direction}, got #{format.class.direction}"
44
+ else
45
+ format
46
+ end
41
47
  else
42
- format
48
+ Base.formats[direction][format].new
43
49
  end
44
50
  else
45
- Base.formats[direction][format].new
51
+ Base.formats[direction][format]
46
52
  end
47
- else
48
- Base.formats[direction][format]
49
53
  end
50
- end
51
-
52
- class Base
53
54
 
54
- @formats = { :in => {}, :out => {} }
55
+ class Base
55
56
 
56
- class << self
57
+ @formats = { :in => {}, :out => {} }
57
58
 
58
- def formats
59
- Base.instance_variable_get(:@formats)
60
- end
59
+ class << self
61
60
 
62
- def valid_format?(direction, format)
63
- if format.class < Base
64
- direction == format.class.direction
65
- else
66
- formats[direction].has_key?(format)
61
+ def formats
62
+ Base.instance_variable_get(:@formats)
67
63
  end
68
- end
69
64
 
70
- private
71
-
72
- def register_format(direction, *aliases, &block)
73
- format = name.split('::').last.
74
- gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
75
- gsub(/([a-z\d])([A-Z])/, '\1_\2').
76
- downcase
65
+ def valid_format?(direction, format)
66
+ if format.class < Base
67
+ direction == format.class.direction
68
+ else
69
+ formats[direction].has_key?(format)
70
+ end
71
+ end
77
72
 
78
- register_format!(direction, format, *aliases, &block)
79
- end
73
+ private
80
74
 
81
- def register_format!(direction, format, *aliases, &block)
82
- raise "must be a sub-class of #{Base}" unless self < Base
75
+ def register_format(direction, *aliases, &block)
76
+ format = name.split('::').last.
77
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
78
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
79
+ downcase
83
80
 
84
- klass = Class.new(self, &block)
81
+ register_format!(direction, format, *aliases, &block)
82
+ end
85
83
 
86
- klass.instance_eval %Q{
87
- def direction; #{direction.inspect}; end
88
- def name; '#{format}::#{direction}'; end
89
- def to_s; '#{format}'; end
90
- }
84
+ def register_format!(direction, format, *aliases, &block)
85
+ raise "must be a sub-class of #{Base}" unless self < Base
86
+
87
+ klass = Class.new(self, &block)
88
+
89
+ klass.instance_eval %Q{
90
+ def direction; #{direction.inspect}; end
91
+ def name; '#{format}::#{direction}'; end
92
+ def to_s; '#{format}'; end
93
+ }
94
+
95
+ [format, *aliases].each { |name|
96
+ if existing = formats[direction][name]
97
+ raise DuplicateFormatDefinitionError,
98
+ "format already defined (#{direction}): #{name}"
99
+ else
100
+ formats[direction][name] = klass
101
+ end
102
+ }
103
+ end
91
104
 
92
- [format, *aliases].each { |name|
93
- if existing = formats[direction][name]
94
- raise DuplicateFormatDefinitionError,
95
- "format already defined (#{direction}): #{name}"
96
- else
97
- formats[direction][name] = klass
98
- end
99
- }
100
105
  end
101
106
 
102
- end
107
+ def parse(*args)
108
+ raise NotImplementedError, 'must be defined by sub-class'
109
+ end
103
110
 
104
- def parse(*args)
105
- raise NotImplementedError, 'must be defined by sub-class'
106
- end
111
+ def convert(record)
112
+ raise NotImplementedError, 'must be defined by sub-class'
113
+ end
107
114
 
108
- def convert(record)
109
- raise NotImplementedError, 'must be defined by sub-class'
110
- end
115
+ def wrap
116
+ yield self
117
+ end
111
118
 
112
- def wrap
113
- yield self
114
- end
119
+ def deferred?
120
+ false
121
+ end
115
122
 
116
- def deferred?
117
- false
118
- end
123
+ def raw?
124
+ false
125
+ end
119
126
 
120
- def raw?
121
- false
122
127
  end
123
128
 
124
- end
125
-
126
- class FormatError < StandardError; end
129
+ class FormatError < StandardError; end
127
130
 
128
- class DuplicateFormatDefinitionError < FormatError; end
129
- class DirectionMismatchError < FormatError; end
131
+ class DuplicateFormatDefinitionError < FormatError; end
132
+ class DirectionMismatchError < FormatError; end
130
133
 
131
- ConfigError = Parser::ConfigError
134
+ ConfigError = Parser::ConfigError
132
135
 
133
- class NoRecordElementError < ConfigError; end
134
- class IllegalRecordElementError < ConfigError; end
136
+ class NoRecordElementError < ConfigError; end
137
+ class IllegalRecordElementError < ConfigError; end
135
138
 
136
139
  end
140
+
137
141
  end
138
142
 
139
- Dir[__FILE__.sub(/\.rb\z/, '/**/*.rb')].sort.each { |rb| require rb }
143
+ Dir[__FILE__.sub(/\.rb\z/, '/**/*.rb')].sort.each { |rb|
144
+ require "athena/formats/#{File.basename(rb, '.rb')}"
145
+ }
@@ -27,9 +27,9 @@
27
27
  #++
28
28
 
29
29
  require 'iconv'
30
+ require 'athena'
30
31
 
31
- module Athena
32
- module Formats
32
+ module Athena::Formats
33
33
 
34
34
  class DBM < Base
35
35
 
@@ -70,5 +70,4 @@ module Athena
70
70
 
71
71
  end
72
72
 
73
- end
74
73
  end
@@ -26,6 +26,8 @@
26
26
  ###############################################################################
27
27
  #++
28
28
 
29
+ require 'athena'
30
+
29
31
  if ferret_version = ENV['FERRET_VERSION']
30
32
  require 'rubygems'
31
33
  gem 'ferret', ferret_version
@@ -37,8 +39,7 @@ rescue LoadError => err
37
39
  warn "ferret#{" #{ferret_version}" if ferret_version} not available (#{err})"
38
40
  end
39
41
 
40
- module Athena
41
- module Formats
42
+ module Athena::Formats
42
43
 
43
44
  class Ferret < Base
44
45
 
@@ -88,7 +89,7 @@ module Athena
88
89
  unless index.deleted?(i)
89
90
  doc = index[i]
90
91
 
91
- Record.new(doc[record_element], block) { |record|
92
+ Athena::Record.new(doc[record_element], block) { |record|
92
93
  config.each { |element, field_config|
93
94
  record.update(element, doc[element], field_config)
94
95
  }
@@ -101,5 +102,4 @@ module Athena
101
102
 
102
103
  end
103
104
 
104
- end
105
105
  end
@@ -28,9 +28,9 @@
28
28
 
29
29
  require 'iconv'
30
30
  require 'enumerator'
31
+ require 'athena'
31
32
 
32
- module Athena
33
- module Formats
33
+ module Athena::Formats
34
34
 
35
35
  class Lingo < Base
36
36
 
@@ -127,5 +127,4 @@ module Athena
127
127
 
128
128
  end
129
129
 
130
- end
131
130
  end
@@ -26,8 +26,9 @@
26
26
  ###############################################################################
27
27
  #++
28
28
 
29
- module Athena
30
- module Formats
29
+ require 'athena'
30
+
31
+ module Athena::Formats
31
32
 
32
33
  class Sisis < Base
33
34
 
@@ -60,7 +61,7 @@ module Athena
60
61
 
61
62
  if element == record_element
62
63
  record.close if record
63
- record = Record.new(value, block)
64
+ record = Athena::Record.new(value, block)
64
65
  num += 1
65
66
  else
66
67
  record.update(element, value, config[element])
@@ -74,5 +75,4 @@ module Athena
74
75
 
75
76
  end
76
77
 
77
- end
78
78
  end
@@ -27,9 +27,9 @@
27
27
  #++
28
28
 
29
29
  require 'strscan'
30
+ require 'athena'
30
31
 
31
- module Athena
32
- module Formats
32
+ module Athena::Formats
33
33
 
34
34
  class MYSQL < Base
35
35
 
@@ -68,7 +68,7 @@ module Athena
68
68
  next if _columns.empty?
69
69
 
70
70
  sql_parser.parse($2) { |row|
71
- Record.new(nil, block) { |record|
71
+ Athena::Record.new(nil, block) { |record|
72
72
  row.each_with_index { |value, index|
73
73
  column = _columns[index] or next
74
74
 
@@ -158,7 +158,9 @@ module Athena
158
158
  end
159
159
 
160
160
  def parse_string_escape
161
- if @input.scan(/\\['\\]|''/)
161
+ if @input.scan(/\\[abtnvfr]/)
162
+ AST.new(eval(%Q{"#{@input.matched}"}))
163
+ elsif @input.scan(/\\.|''/)
162
164
  AST.new(@input.matched[-1, 1])
163
165
  end
164
166
  end
@@ -179,7 +181,7 @@ module Athena
179
181
  if @input.eos?
180
182
  raise "Unexpected end of input (#{message})."
181
183
  else
182
- raise "#{message} at #{@input.pos}: #{@input.peek(8).inspect}"
184
+ raise "#{message} at #{$.}:#{@input.pos}: #{@input.peek(16).inspect}"
183
185
  end
184
186
  end
185
187
 
@@ -221,7 +223,7 @@ module Athena
221
223
  cols = columns[table]
222
224
  next if cols.empty?
223
225
 
224
- Record.new(nil, block) { |record|
226
+ Athena::Record.new(nil, block) { |record|
225
227
  line.split(/\t/).each_with_index { |value, index|
226
228
  column = cols[index] or next
227
229
 
@@ -245,5 +247,4 @@ module Athena
245
247
  MySQL = MYSQL
246
248
  PgSQL = PGSQL
247
249
 
248
- end
249
250
  end
@@ -27,18 +27,15 @@
27
27
  #++
28
28
 
29
29
  require 'forwardable'
30
-
31
30
  require 'builder'
32
31
  require 'xmlstreamin'
33
32
  require 'nuggets/hash/insert'
33
+ require 'athena'
34
34
 
35
- module Athena
36
- module Formats
35
+ module Athena::Formats
37
36
 
38
37
  class XML < Base
39
38
 
40
- include Util
41
-
42
39
  # <http://www.w3.org/TR/2006/REC-xml-20060816/#NT-Name>
43
40
  ELEMENT_START_RE = %r{\A[a-zA-Z_:]}
44
41
  NON_ELEMENT_CHAR_RE = %r{[^\w:.-]}
@@ -104,11 +101,7 @@ module Athena
104
101
 
105
102
  def wrap(out = nil)
106
103
  res = nil
107
-
108
- builder(:target => out).database {
109
- res = super()
110
- }
111
-
104
+ builder(:target => out).database { res = super() }
112
105
  res
113
106
  end
114
107
 
@@ -184,89 +177,44 @@ module Athena
184
177
  spec.default!(prev_spec)
185
178
  }
186
179
 
187
- verbose(:spec, BaseSpec) { spec.inspect_spec }
188
-
189
180
  XMLStreamin::XMLStreamListener.new(spec)
190
181
  end
191
182
 
192
183
  def define_spec(element, field, config, arg)
193
184
  spec = ElementSpec.new(element, field, config)
194
-
195
- case arg
196
- when Hash then spec.specs!(arg)
197
- else spec.default!(SubElementSpec.new(spec))
198
- end
199
-
185
+ arg.is_a?(Hash) ? spec.specs!(arg) : spec.default!(SubElementSpec.new(spec))
200
186
  spec
201
187
  end
202
188
 
203
189
  def merge_specs(container, key, spec)
204
- container.insert!(key => spec) { |_, s1, s2|
205
- if s1.respond_to?(:specs!)
206
- s1.specs!(s2.respond_to?(:specs) ? s2.specs : s2)
207
- s1
190
+ container.insert!(key => spec) { |_, spec1, spec2|
191
+ if spec1.respond_to?(:specs!)
192
+ spec1.specs!(spec2.respond_to?(:specs) ? spec2.specs : spec2)
193
+ spec1
208
194
  else
209
- s1.merge(s2)
195
+ spec1.merge(spec2)
210
196
  end
211
197
  }
212
198
  end
213
199
 
214
200
  class BaseSpec < XMLStreamin::XMLSpec
215
201
 
216
- include Util
217
-
218
- @level = 0
219
-
220
202
  def start(context, name, attrs)
221
- verbose(:xml) {
222
- spit "#{indent(level)}<#{name}>"; step :down
223
- attrs.each { |attr| spit "#{indent(level + 1)}[#{attr[0]} = #{attr[1]}]" }
224
- }
225
-
226
203
  context
227
204
  end
228
205
 
229
206
  def text(context, data)
230
- verbose(:xml) { spit "#{indent(level)}#{data.strip}" unless data.strip.empty? }
231
207
  context
232
208
  end
233
209
 
234
210
  def done(context, name)
235
- verbose(:xml) { step :up; spit "#{indent(level)}</#{name}>" }
236
211
  context
237
212
  end
238
213
 
239
214
  def empty(context)
240
- verbose(:xml) { step :up }
241
215
  context
242
216
  end
243
217
 
244
- def inspect_spec(element = nil, level = 0)
245
- if respond_to?(:field)
246
- msg = "#{indent(level)}[#{element}] #{field.to_s.upcase} -> #{name}"
247
- respond_to?(:spit) ? spit(msg) : warn(msg)
248
-
249
- inspect_specs(level + 1)
250
- else
251
- specs.empty? ? specs.default.inspect_spec('?', level) : inspect_specs(level)
252
- end
253
- end
254
-
255
- private
256
-
257
- def inspect_specs(level = 0)
258
- specs.each { |element, spec| spec.inspect_spec(element, level) }
259
- end
260
-
261
- def level
262
- BaseSpec.instance_variable_get(:@level)
263
- end
264
-
265
- def step(direction)
266
- steps = { :down => 1, :up => -1 }
267
- BaseSpec.instance_variable_set(:@level, level + steps[direction])
268
- end
269
-
270
218
  end
271
219
 
272
220
  class RecordSpec < BaseSpec
@@ -276,13 +224,12 @@ module Athena
276
224
 
277
225
  def initialize(&block)
278
226
  super()
279
-
280
227
  @block = block
281
228
  end
282
229
 
283
230
  def start(context, name, attrs)
284
231
  context = super
285
- self.record = Record.new(nil, block, true)
232
+ self.record = Athena::Record.new(nil, block, true)
286
233
  context
287
234
  end
288
235
 
@@ -301,15 +248,12 @@ module Athena
301
248
 
302
249
  def initialize(name, field, config)
303
250
  super()
304
-
305
- @name = name
306
- @field = field
307
- @config = config
251
+ @name, @field, @config = name, field, config
308
252
  end
309
253
 
310
254
  def start(context, name, attrs)
311
255
  context = super
312
- self.record = Record[field, config]
256
+ self.record = Athena::Record[field, config]
313
257
  context
314
258
  end
315
259
 
@@ -330,9 +274,7 @@ module Athena
330
274
 
331
275
  def initialize(parent)
332
276
  super()
333
-
334
277
  @parent = parent
335
-
336
278
  default!(self)
337
279
  end
338
280
 
@@ -340,5 +282,4 @@ module Athena
340
282
 
341
283
  end
342
284
 
343
- end
344
285
  end
@@ -26,65 +26,65 @@
26
26
  ###############################################################################
27
27
  #++
28
28
 
29
- module Athena
30
- class Parser
31
-
32
- include Util
33
-
34
- DEFAULT_SEPARATOR = ', '
35
- DEFAULT_EMPTY = '<<EMPTY>>'
36
-
37
- attr_reader :config, :spec
38
-
39
- def initialize(config, spec)
40
- @config = build_config(config)
41
- @spec = Formats[:in, spec].new(self)
42
- end
43
-
44
- def parse(source, &block)
45
- res = spec.parse(source, &block)
46
- res.is_a?(Numeric) ? res : Record.records
47
- end
29
+ require 'athena'
48
30
 
49
- private
50
-
51
- def build_config(config)
52
- hash = {}
31
+ module Athena
53
32
 
54
- config.each { |field, value|
55
- if field.to_s =~ /\A__/
56
- hash[field] = value
57
- else
58
- case value
59
- when String, Array
60
- elements, value = [*value], {}
61
- when Hash
62
- elements = value[:elements] || value[:element].to_a
33
+ class Parser
63
34
 
64
- raise ArgumentError, "no elements specified for field #{field}" unless elements.is_a?(Array)
65
- else
66
- raise ArgumentError, "illegal value for field #{field}"
35
+ DEFAULT_SEPARATOR = ', '
36
+ DEFAULT_EMPTY = '<<EMPTY>>'
37
+
38
+ attr_reader :config, :spec
39
+
40
+ def initialize(config, spec)
41
+ @config = build_config(config)
42
+ @spec = Formats[:in, spec].new(self)
43
+ end
44
+
45
+ def parse(source, &block)
46
+ res = spec.parse(source, &block)
47
+ res.is_a?(Numeric) ? res : Record.records
48
+ end
49
+
50
+ private
51
+
52
+ def build_config(config)
53
+ hash = {}
54
+
55
+ config.each { |field, value|
56
+ if field.to_s =~ /\A__/
57
+ hash[field] = value
58
+ else
59
+ case value
60
+ when String, Array
61
+ elements, value = [*value], {}
62
+ when Hash
63
+ elements = value[:elements] || value[:element].to_a
64
+
65
+ raise ArgumentError, "no elements specified for field #{field}" unless elements.is_a?(Array)
66
+ else
67
+ raise ArgumentError, "illegal value for field #{field}"
68
+ end
69
+
70
+ separator = value[:separator] || DEFAULT_SEPARATOR
71
+
72
+ elements.each { |element|
73
+ (hash[element] ||= {})[field] = {
74
+ :string => value[:string] || ['%s'] * elements.size * separator,
75
+ :empty => value[:empty] || DEFAULT_EMPTY,
76
+ :elements => elements
77
+ }
78
+ }
67
79
  end
80
+ }
68
81
 
69
- separator = value[:separator] || DEFAULT_SEPARATOR
82
+ hash
83
+ end
70
84
 
71
- elements.each { |element|
72
- verbose(:config) { spit "#{field.to_s.upcase} -> #{element}" }
85
+ class ConfigError < StandardError
86
+ end
73
87
 
74
- (hash[element] ||= {})[field] = {
75
- :string => value[:string] || ['%s'] * elements.size * separator,
76
- :empty => value[:empty] || DEFAULT_EMPTY,
77
- :elements => elements
78
- }
79
- }
80
- end
81
- }
82
-
83
- hash
84
- end
85
-
86
- class ConfigError < StandardError
87
88
  end
88
89
 
89
- end
90
90
  end
@@ -27,80 +27,74 @@
27
27
  #++
28
28
 
29
29
  require 'nuggets/integer/map'
30
+ require 'athena'
30
31
 
31
32
  module Athena
33
+
32
34
  class Record
33
35
 
34
- include Util
36
+ @records = []
35
37
 
36
- @records = []
38
+ class << self
37
39
 
38
- class << self
40
+ def records
41
+ @records
42
+ end
39
43
 
40
- def records
41
- @records
42
- end
44
+ def [](field = nil, config = nil)
45
+ record = records.last
46
+ raise NoRecordError unless record
43
47
 
44
- def [](field = nil, config = nil)
45
- record = records.last
46
- raise NoRecordError unless record
48
+ record.fill(field, config) if field && config
49
+ record
50
+ end
47
51
 
48
- record.fill(field, config) if field && config
49
- record
50
52
  end
51
53
 
52
- end
53
-
54
- attr_reader :struct, :block, :id
54
+ attr_reader :struct, :block, :id
55
55
 
56
- def initialize(id = nil, block = nil, add = !block)
57
- @id = id || object_id.map_positive
58
- @block = block
59
- @struct = {}
56
+ def initialize(id = nil, block = nil, add = !block)
57
+ @id = id || object_id.map_positive
58
+ @block = block
59
+ @struct = {}
60
60
 
61
- add_record if add
61
+ add_record if add
62
62
 
63
- if block_given?
64
- begin
65
- yield self
66
- ensure
67
- close
63
+ if block_given?
64
+ begin
65
+ yield self
66
+ ensure
67
+ close
68
+ end
68
69
  end
69
70
  end
70
- end
71
71
 
72
- def fill(field, config)
73
- struct[field] ||= config.merge(:values => Hash.new { |h, k| h[k] = [] })
74
- end
75
-
76
- def update(element, data, field_config = nil)
77
- field_config.each { |field, config| fill(field, config) } if field_config
72
+ def fill(field, config)
73
+ struct[field] ||= config.merge(:values => Hash.new { |h, k| h[k] = [] })
74
+ end
78
75
 
79
- struct.each_key { |field|
80
- verbose(:data) {
81
- spit "#{field.to_s.upcase}[#{element}] << #{data.strip}" unless data.strip.empty?
82
- }
76
+ def update(element, data, field_config = nil)
77
+ field_config.each { |field, config| fill(field, config) } if field_config
78
+ struct.each_key { |field| struct[field][:values][element] << data }
79
+ end
83
80
 
84
- struct[field][:values][element] << data
85
- }
86
- end
81
+ def close
82
+ block ? block[self] : self
83
+ end
87
84
 
88
- def close
89
- block ? block[self] : self
90
- end
85
+ def to(format)
86
+ Formats[:out, format].convert(self)
87
+ end
91
88
 
92
- def to(format)
93
- Formats[:out, format].convert(self)
94
- end
89
+ private
95
90
 
96
- private
91
+ def add_record
92
+ self.class.records << self
93
+ end
97
94
 
98
- def add_record
99
- self.class.records << self
100
- end
95
+ class NoRecordError < StandardError
96
+ end
101
97
 
102
- class NoRecordError < StandardError
103
98
  end
104
99
 
105
- end
106
100
  end
@@ -3,8 +3,8 @@ module Athena
3
3
  module Version
4
4
 
5
5
  MAJOR = 0
6
- MINOR = 1
7
- TINY = 5
6
+ MINOR = 2
7
+ TINY = 1
8
8
 
9
9
  class << self
10
10
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: athena
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 2
8
9
  - 1
9
- - 5
10
- version: 0.1.5
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jens Wille
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-12 00:00:00 Z
18
+ date: 2011-07-27 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: builder
@@ -53,12 +53,12 @@ dependencies:
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- hash: 15
56
+ hash: 11
57
57
  segments:
58
58
  - 0
59
- - 6
59
+ - 7
60
60
  - 4
61
- version: 0.6.4
61
+ version: 0.7.4
62
62
  type: :runtime
63
63
  version_requirements: *id003
64
64
  description: Convert database files to various formats.
@@ -72,8 +72,8 @@ extra_rdoc_files:
72
72
  - COPYING
73
73
  - ChangeLog
74
74
  files:
75
- - lib/athena/util.rb
76
75
  - lib/athena/record.rb
76
+ - lib/athena/cli.rb
77
77
  - lib/athena/formats/xml.rb
78
78
  - lib/athena/formats/lingo.rb
79
79
  - lib/athena/formats/ferret.rb
@@ -99,14 +99,14 @@ licenses: []
99
99
 
100
100
  post_install_message:
101
101
  rdoc_options:
102
+ - --all
103
+ - --main
104
+ - README
102
105
  - --charset
103
106
  - UTF-8
104
107
  - --title
105
- - athena Application documentation (v0.1.5)
106
- - --main
107
- - README
108
+ - athena Application documentation (v0.2.1)
108
109
  - --line-numbers
109
- - --all
110
110
  require_paths:
111
111
  - lib
112
112
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -130,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
130
  requirements: []
131
131
 
132
132
  rubyforge_project: prometheus
133
- rubygems_version: 1.8.5
133
+ rubygems_version: 1.8.6
134
134
  signing_key:
135
135
  specification_version: 3
136
136
  summary: Convert database files to various formats.
@@ -1,49 +0,0 @@
1
- #--
2
- ###############################################################################
3
- # #
4
- # A component of athena, the database file converter. #
5
- # #
6
- # Copyright (C) 2007-2011 University of Cologne, #
7
- # Albertus-Magnus-Platz, #
8
- # 50923 Cologne, Germany #
9
- # #
10
- # Authors: #
11
- # Jens Wille <jens.wille@uni-koeln.de> #
12
- # #
13
- # athena is free software; you can redistribute it and/or modify it under the #
14
- # terms of the GNU Affero General Public License as published by the Free #
15
- # Software Foundation; either version 3 of the License, or (at your option) #
16
- # any later version. #
17
- # #
18
- # athena is distributed in the hope that it will be useful, but WITHOUT ANY #
19
- # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
20
- # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
21
- # more details. #
22
- # #
23
- # You should have received a copy of the GNU Affero General Public License #
24
- # along with athena. If not, see <http://www.gnu.org/licenses/>. #
25
- # #
26
- ###############################################################################
27
- #++
28
-
29
- module Athena
30
- module Util
31
-
32
- extend self
33
-
34
- def verbose(what, klass = self.class, &block)
35
- if $Verbose && $Verbose[what]
36
- klass.send(:define_method, :spit) { |msg|
37
- warn "*#{what}: #{msg}"
38
- }
39
-
40
- klass.send(:define_method, :indent) { |*level|
41
- ' ' * (level.first || 0)
42
- }
43
-
44
- instance_eval(&block)
45
- end
46
- end
47
-
48
- end
49
- end