athena 0.1.5 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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