activemdb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +19 -0
- data/README.txt +57 -0
- data/Rakefile +17 -0
- data/db/sample.mdb +0 -0
- data/irb.rc +4 -0
- data/lib/active_mdb.rb +18 -0
- data/lib/active_mdb/column.rb +21 -0
- data/lib/active_mdb/mdb.rb +39 -0
- data/lib/active_mdb/mdb_tools.rb +149 -0
- data/lib/active_mdb/record.rb +33 -0
- data/lib/active_mdb/table.rb +89 -0
- data/support/a.insert +7 -0
- data/test/test_activemdb.rb +0 -0
- data/test/test_helper.rb +16 -0
- data/test/test_mdb.rb +35 -0
- data/test/test_mdb_tools.rb +100 -0
- data/test/test_record.rb +24 -0
- data/test/test_table.rb +50 -0
- metadata +77 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
db/sample.mdb
|
6
|
+
irb.rc
|
7
|
+
lib/active_mdb.rb
|
8
|
+
lib/active_mdb/column.rb
|
9
|
+
lib/active_mdb/mdb.rb
|
10
|
+
lib/active_mdb/mdb_tools.rb
|
11
|
+
lib/active_mdb/record.rb
|
12
|
+
lib/active_mdb/table.rb
|
13
|
+
support/a.insert
|
14
|
+
test/test_activemdb.rb
|
15
|
+
test/test_helper.rb
|
16
|
+
test/test_mdb.rb
|
17
|
+
test/test_mdb_tools.rb
|
18
|
+
test/test_record.rb
|
19
|
+
test/test_table.rb
|
data/README.txt
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
ActiveMDB
|
2
|
+
by Matthew King
|
3
|
+
http://rubyforge.org/projects/activemdb/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Lib for getting info out of MS Access (.mdb) files, which uses ActiveRecord-ish reflection to parse table and column names.
|
8
|
+
|
9
|
+
Intended for exploration and migration, not production. ActiveMDB provides a thin wrapper around the mdb-tables, mdb-schema, mdb-sql, and mdb-export binaries from Brian Bruns's MDB Tools project (http://mdbtools.sourceforge.net/).
|
10
|
+
|
11
|
+
== FEATURES/PROBLEMS:
|
12
|
+
|
13
|
+
* MDB, Table, and Record classes do reflection to provide easy attribute readers
|
14
|
+
* I really need to refactor the above classes to something more like ActiveRecord::Base, so that you can subclass to make models.
|
15
|
+
|
16
|
+
== SYNOPSIS:
|
17
|
+
|
18
|
+
@mdb = MDB.new('db/sample.mdb', :exclude => 'lookups')
|
19
|
+
@employees = @mdb.employees
|
20
|
+
|
21
|
+
# in the find_* methods, the entries in the hash
|
22
|
+
# get turned into "WHERE #{key} like %#{value}%" conditions,
|
23
|
+
# unless the column is a boolean, in which case the WHERE uses "="
|
24
|
+
@employees.find_first :f_name => 'Matthew', :l_name => 'King'
|
25
|
+
|
26
|
+
== REQUIREMENTS:
|
27
|
+
|
28
|
+
* http://mdbtools.sourceforge.net/
|
29
|
+
|
30
|
+
== INSTALL:
|
31
|
+
|
32
|
+
* Sadly, no easy install at this time.
|
33
|
+
|
34
|
+
== LICENSE:
|
35
|
+
|
36
|
+
(The MIT License)
|
37
|
+
|
38
|
+
Copyright (c) 2007
|
39
|
+
|
40
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
41
|
+
a copy of this software and associated documentation files (the
|
42
|
+
'Software'), to deal in the Software without restriction, including
|
43
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
44
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
45
|
+
permit persons to whom the Software is furnished to do so, subject to
|
46
|
+
the following conditions:
|
47
|
+
|
48
|
+
The above copyright notice and this permission notice shall be
|
49
|
+
included in all copies or substantial portions of the Software.
|
50
|
+
|
51
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
52
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
53
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
54
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
55
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
56
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
57
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/active_mdb.rb'
|
6
|
+
|
7
|
+
Hoe.new('activemdb', ActiveMDB::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'activemdb'
|
9
|
+
p.author = 'Matthew King'
|
10
|
+
p.email = 'automatthew@gmail.com'
|
11
|
+
p.summary = 'a reflective wrapper around MDB Tools, which lets POSIX platforms read MS Access (.mdb) files'
|
12
|
+
p.description = p.paragraphs_of('README.txt', 2).join("\n\n")
|
13
|
+
p.url = p.paragraphs_of('README.txt',0).first.split(/\n/)[2..-1]
|
14
|
+
# p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
# vim: syntax=Ruby
|
data/db/sample.mdb
ADDED
Binary file
|
data/irb.rc
ADDED
data/lib/active_mdb.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
module ActiveMDB
|
3
|
+
VERSION = '0.1.0'
|
4
|
+
end
|
5
|
+
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'active_support/inflector'
|
9
|
+
require 'active_mdb/mdb_tools'
|
10
|
+
load 'active_mdb/mdb.rb'
|
11
|
+
load 'active_mdb/table.rb'
|
12
|
+
load 'active_mdb/record.rb'
|
13
|
+
load 'active_mdb/column.rb'
|
14
|
+
require 'faster_csv'
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Column
|
2
|
+
include MDBTools
|
3
|
+
|
4
|
+
attr_reader :method_name, :name, :type, :size
|
5
|
+
|
6
|
+
def initialize(name, type, size)
|
7
|
+
@name = name
|
8
|
+
@method_name, @type, @size = methodize(name), methodize(type), size.to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.new_from_describe(describe_hash)
|
12
|
+
self.new(describe_hash["Column Name"], describe_hash["Type"], describe_hash["Size"])
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def boolean?
|
17
|
+
self.type == 'boolean'
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# require 'mdb_tools'
|
2
|
+
|
3
|
+
class MDB
|
4
|
+
include MDBTools
|
5
|
+
|
6
|
+
attr_reader :mdb_file, :prefix, :exclude, :table_names
|
7
|
+
|
8
|
+
def initialize(mdb_file, options = {})
|
9
|
+
@mdb_file = check_file(mdb_file)
|
10
|
+
@prefix = options[:prefix] || ''
|
11
|
+
@exclude, @include = options[:exclude], options[:include]
|
12
|
+
@export_syntax = options[:sql_syntax] || 'mysql'
|
13
|
+
@table_names = mdb_tables(@mdb_file, :exclude => @exclude, :include => @include)
|
14
|
+
@tables = create_table_objects
|
15
|
+
end
|
16
|
+
|
17
|
+
def tables
|
18
|
+
@table_names.collect { |table| methodize(table)}
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def create_table_objects
|
26
|
+
tables = {}
|
27
|
+
@table_names.each do |table|
|
28
|
+
tables[table] = Table.new(self, table, @prefix)
|
29
|
+
metaclass = class << self; self; end
|
30
|
+
metaclass.send :define_method, methodize(table) do
|
31
|
+
tables[table]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
tables
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module MDBTools
|
2
|
+
include Inflector
|
3
|
+
|
4
|
+
DELIMITER = '::'
|
5
|
+
LINEBREAK = "\n"
|
6
|
+
SANITIZER = /^\w\.\_/ # dumb filter for SQL arguments
|
7
|
+
BACKENDS = %w{ access mysql oracle postgres sybase }
|
8
|
+
|
9
|
+
def check_file(mdb_file)
|
10
|
+
raise ArgumentError, "File not found: #{mdb_file}" unless File.exist?(mdb_file)
|
11
|
+
@mdb_version = `mdb-ver #{mdb_file} 2>&1`.chomp
|
12
|
+
if $? != 0
|
13
|
+
raise ArgumentError, "mdbtools cannot access #{mdb_file}"
|
14
|
+
end
|
15
|
+
mdb_file
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_table(mdb_file, table_name)
|
19
|
+
unless mdb_tables(mdb_file).include?(table_name)
|
20
|
+
raise ArgumentError, "mdbtools does not think a table named \"#{table_name}\" exists"
|
21
|
+
end
|
22
|
+
table_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def mdb_tables(mdb_file, options = {})
|
26
|
+
included, excluded = options[:include], options[:exclude]
|
27
|
+
return `mdb-tables -1 #{mdb_file}`.split(LINEBREAK) if not (included || excluded)
|
28
|
+
raise ArgumentError if (options[:include] && options [:exclude])
|
29
|
+
if options[:exclude]
|
30
|
+
regex = Regexp.new options[:exclude].to_a.join('|')
|
31
|
+
tables = `mdb-tables -1 #{mdb_file}`.split(LINEBREAK).delete_if { |name| name =~ regex }
|
32
|
+
end
|
33
|
+
if options[:include]
|
34
|
+
regex = Regexp.new options[:include].to_a.join('|')
|
35
|
+
tables = `mdb-tables -1 #{mdb_file}`.split(LINEBREAK).select { |name| name =~ regex }
|
36
|
+
end
|
37
|
+
tables
|
38
|
+
end
|
39
|
+
|
40
|
+
def sql_select(mdb_file, table_name, attributes = nil, conditions ={})
|
41
|
+
attributes ||= ['*']
|
42
|
+
sql = "select #{attributes.join(' ')} from #{table_name} where #{conditions}".dump
|
43
|
+
mdb_sql(mdb_file, sql)
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def mdb_sql(mdb_file, sql)
|
48
|
+
# puts sql
|
49
|
+
result = `echo -n #{sql} | mdb-sql -Fp -H -d '#{DELIMITER}' #{mdb_file}`.strip
|
50
|
+
arrays = delimited_to_arrays(result)
|
51
|
+
end
|
52
|
+
|
53
|
+
def compile_conditions(conditions_hash, *args)
|
54
|
+
conditions = conditions_hash.sort_by{|k,v| k.to_s}.map do |column_name, value|
|
55
|
+
if block_given?
|
56
|
+
yield column_name, value
|
57
|
+
else
|
58
|
+
"#{column_name} like '%#{value}%'"
|
59
|
+
end
|
60
|
+
end.join(' AND ')
|
61
|
+
end
|
62
|
+
|
63
|
+
def mdb_export(mdb_file, table_name, options = {})
|
64
|
+
defaults = { :format => 'sql',
|
65
|
+
:headers => false,
|
66
|
+
:sanitize => true }
|
67
|
+
options = defaults.merge options
|
68
|
+
|
69
|
+
args = []
|
70
|
+
if options[:delimiter]
|
71
|
+
args << "-d #{options[:delimiter].dump}"
|
72
|
+
elsif options[:format] == 'sql'
|
73
|
+
args << "-I "
|
74
|
+
elsif options[:format] == 'csv'
|
75
|
+
args << "-d ',' "
|
76
|
+
else
|
77
|
+
raise ArgumentError, "Unknown format: #{options[:format]}"
|
78
|
+
end
|
79
|
+
|
80
|
+
args << "-H " unless options[:headers] == true
|
81
|
+
args << "-S" unless options[:sanitize] == false
|
82
|
+
`mdb-export #{args} #{mdb_file} #{table_name.to_s.dump}`
|
83
|
+
end
|
84
|
+
|
85
|
+
def describe_table(mdb_file, table_name)
|
86
|
+
command = "describe table \"#{table_name}\"".dump
|
87
|
+
description = `echo -n #{command} | mdb-sql -Fp -d '#{DELIMITER}' #{mdb_file}`.strip
|
88
|
+
arrays = delimited_to_arrays(description)
|
89
|
+
arrays_to_hashes(arrays.shift, arrays)
|
90
|
+
end
|
91
|
+
|
92
|
+
def mdb_schema(mdb_file, table_name)
|
93
|
+
schema = `mdb-schema -T #{table_name.dump} #{mdb_file}`
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def table_to_csv(mdb_file, table_name)
|
98
|
+
mdb_export(mdb_file, table_name, :format => 'csv', :headers => true)
|
99
|
+
end
|
100
|
+
|
101
|
+
def delimited_to_arrays(text)
|
102
|
+
text.gsub!(/\r\n/,' ')
|
103
|
+
text.split(LINEBREAK).collect { |row| row.split(DELIMITER)}
|
104
|
+
end
|
105
|
+
|
106
|
+
def arrays_to_hashes(headers, arrays)
|
107
|
+
arrays.collect do |record|
|
108
|
+
record_hash = Hash.new
|
109
|
+
until record.empty? do
|
110
|
+
headers.each do |header|
|
111
|
+
record_hash[header] = record.shift
|
112
|
+
end
|
113
|
+
end
|
114
|
+
record_hash
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
def methodize(table_name)
|
121
|
+
underscore table_name
|
122
|
+
end
|
123
|
+
|
124
|
+
def backends
|
125
|
+
BACKENDS
|
126
|
+
end
|
127
|
+
|
128
|
+
def sanitize!(string)
|
129
|
+
string.gsub!(SANITIZER, '')
|
130
|
+
end
|
131
|
+
|
132
|
+
def mdb_truth(value)
|
133
|
+
case value
|
134
|
+
when false
|
135
|
+
0
|
136
|
+
when true
|
137
|
+
1
|
138
|
+
when 0
|
139
|
+
0
|
140
|
+
when 1
|
141
|
+
1
|
142
|
+
when "0"
|
143
|
+
0
|
144
|
+
when "1"
|
145
|
+
1
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# require 'mdb_tools'
|
2
|
+
|
3
|
+
class Record
|
4
|
+
include MDBTools
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(mdb_table, line)
|
8
|
+
raise 'no results' unless line
|
9
|
+
@struct = mdb_table.record_struct
|
10
|
+
@data = @struct.new(*line)
|
11
|
+
create_accessors
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block)
|
15
|
+
@data.members.each {|k| yield k, @data[k] }
|
16
|
+
end
|
17
|
+
|
18
|
+
def compact
|
19
|
+
self.select {|k,v| v && !v.empty? && v != "0" }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def create_accessors
|
25
|
+
meta = class << self; self; end
|
26
|
+
@struct.members.each do |att|
|
27
|
+
meta.send :define_method, att do
|
28
|
+
@data[att]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# require 'mdb_tools'
|
2
|
+
|
3
|
+
class Table
|
4
|
+
include MDBTools
|
5
|
+
|
6
|
+
attr_reader :mdb_file, :table_name, :columns, :record_struct, :schema
|
7
|
+
attr_accessor :primary_key
|
8
|
+
|
9
|
+
|
10
|
+
def initialize(mdb, table_name, prefix)
|
11
|
+
@mdb_file = check_file(mdb.mdb_file)
|
12
|
+
@table_name = check_table(@mdb_file, table_name)
|
13
|
+
# @schema = mdb_schema(@mdb_file, @table_name)
|
14
|
+
@columns = describe_table(mdb_file, table_name).map do |column|
|
15
|
+
Column.new_from_describe(column)
|
16
|
+
end
|
17
|
+
@record_struct = create_record_struct
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def [](method_name)
|
22
|
+
self.columns.detect {|c| c.method_name == method_name }
|
23
|
+
end
|
24
|
+
|
25
|
+
def column_names
|
26
|
+
columns.collect {|x| methodize(x.method_name).to_sym}
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_record_struct
|
30
|
+
attributes = columns.collect {|column| column.method_name.to_sym}
|
31
|
+
Struct.new( *attributes)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_csv
|
35
|
+
table_to_csv(mdb_file, table_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_sql
|
39
|
+
raise 'not implemented'
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_first(conditions_hash)
|
43
|
+
rekey_hash(conditions_hash)
|
44
|
+
result = sql_search(conditions_hash).first
|
45
|
+
create_record(result)
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_all(conditions_hash={})
|
49
|
+
if conditions_hash.empty?
|
50
|
+
return sql_select(mdb_file, table_name, nil, '1 = 1').collect {|r| create_record(r)}
|
51
|
+
end
|
52
|
+
rekey_hash(conditions_hash)
|
53
|
+
sql_search(conditions_hash).collect {|r| create_record(r) }
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def create_record(line)
|
59
|
+
Record.new(self, line)
|
60
|
+
end
|
61
|
+
|
62
|
+
def rekey_hash(conditions_hash)
|
63
|
+
conditions_hash.each do |key,value|
|
64
|
+
column = self[key.to_s]
|
65
|
+
if column.boolean?
|
66
|
+
key = column.name + '!'
|
67
|
+
else
|
68
|
+
key = column.name
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# the conditions hash keys are column names, the values are search values
|
74
|
+
# e.g. sql_search(:first_name => 'Matthew', :last_name => 'King')
|
75
|
+
def sql_search(conditions_hash)
|
76
|
+
conditions = compile_conditions(conditions_hash) do |method_name,value|
|
77
|
+
column = self[method_name.to_s]
|
78
|
+
if column.boolean?
|
79
|
+
"#{column.name} = #{mdb_truth(value)}"
|
80
|
+
else
|
81
|
+
"#{column.name} like '%#{value}%'"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
sql_select(mdb_file, table_name, nil, conditions)
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
end
|
89
|
+
|
data/support/a.insert
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
INSERT INTO ActivityLookup (ActivityID, ActivitySeq, ActivityCode, ActivityDesc) VALUES (1,0000000000000001.,"Conversation","QA conversation with the contact - face to face")
|
2
|
+
INSERT INTO ActivityLookup (ActivityID, ActivitySeq, ActivityCode, ActivityDesc) VALUES (2,0000000000000002.,"Phone Call","A Phone Call with the contact")
|
3
|
+
INSERT INTO ActivityLookup (ActivityID, ActivitySeq, ActivityCode, ActivityDesc) VALUES (3,0000000000000003.,"Mail","Sent something to the contact via mail")
|
4
|
+
INSERT INTO ActivityLookup (ActivityID, ActivitySeq, ActivityCode, ActivityDesc) VALUES (4,0000000000000004.,"eMail","Sent something to the contact via email")
|
5
|
+
INSERT INTO ActivityLookup (ActivityID, ActivitySeq, ActivityCode, ActivityDesc) VALUES (5,0000000000000005.,"Visit","Visited the contact for a delivery or conversation")
|
6
|
+
INSERT INTO ActivityLookup (ActivityID, ActivitySeq, ActivityCode, ActivityDesc) VALUES (6,0000000000000006.,"Meeting","Had a meeting with the contact")
|
7
|
+
INSERT INTO ActivityLookup (ActivityID, ActivitySeq, ActivityCode, ActivityDesc) VALUES (7,0000000000000007.,"Research","Research is needed on a topic for followup/action")
|
File without changes
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
class Test::Unit::TestCase
|
4
|
+
|
5
|
+
TEST_DB = File.join(File.dirname(__FILE__), '..', 'db', 'sample.mdb')
|
6
|
+
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def create_mdb(options={})
|
11
|
+
excluded = options[:exclude]
|
12
|
+
included = options[:include]
|
13
|
+
assert_nothing_raised { @db = MDB.new(TEST_DB, :exclude => excluded, :include => included ) }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/test/test_mdb.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'lib', 'active_mdb')
|
2
|
+
require 'test/unit'
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
4
|
+
|
5
|
+
class MDBTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
def setup
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_table_exclusion
|
14
|
+
create_mdb(:exclude => ['Inventory'])
|
15
|
+
assert !@db.table_names.include?('Inventory')
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_table_inclusion
|
19
|
+
create_mdb(:include => ['Inventory', 'Room'])
|
20
|
+
assert_equal ['Inventory', 'Room'], @db.table_names.sort
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_reflection
|
24
|
+
create_mdb
|
25
|
+
assert_respond_to @db, 'computer'
|
26
|
+
assert_respond_to @db, 'employee'
|
27
|
+
assert_respond_to @db, 'room'
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'lib', 'active_mdb')
|
2
|
+
require 'test/unit'
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
4
|
+
|
5
|
+
|
6
|
+
class MDBToolsTest < Test::Unit::TestCase
|
7
|
+
include MDBTools
|
8
|
+
|
9
|
+
EXCLUDE = ['Inventory']
|
10
|
+
TEST_TABLES = %w{ Room Computer Employee }
|
11
|
+
TORBATI_Y = {"Department"=>"Engineering",
|
12
|
+
"Gender"=>"F",
|
13
|
+
"Room"=>"6044",
|
14
|
+
"Title"=>"Programmer",
|
15
|
+
"Emp_Id"=>"1000",
|
16
|
+
"First_Name"=>"Yolanda",
|
17
|
+
"Last_Name"=>"Torbati"}
|
18
|
+
README = File.join(File.dirname(__FILE__), "..", "README")
|
19
|
+
|
20
|
+
def setup
|
21
|
+
@employees_csv = mdb_export(TEST_DB, 'Employee', :format => 'csv', :headers => true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_check_file
|
25
|
+
assert_nothing_raised { check_file TEST_DB }
|
26
|
+
assert_raise(ArgumentError) { check_file 'completely_bogus_filename' }
|
27
|
+
# the README file is obviously not an Access database
|
28
|
+
assert_raise(ArgumentError) { check_file README}
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_check_table
|
32
|
+
assert_nothing_raised { check_table TEST_DB, 'Employee'}
|
33
|
+
assert_raises(ArgumentError) { check_table TEST_DB, 'foobarbaz' }
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_mdb_tables
|
37
|
+
tables1 = mdb_tables(TEST_DB, :exclude => EXCLUDE)
|
38
|
+
assert_equal TEST_TABLES, tables1
|
39
|
+
|
40
|
+
assert_raises( ArgumentError) { mdb_tables(TEST_DB, :include => [], :exclude => []) }
|
41
|
+
tables4 = mdb_tables(TEST_DB, :exclude => 'Room')
|
42
|
+
assert_equal %w{Computer Employee Inventory}, tables4
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_mdb_tables_with_include
|
46
|
+
tables = mdb_tables(TEST_DB, :include => ['Room', 'Computer'])
|
47
|
+
assert_equal ['Room', 'Computer'], tables
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_mdb_tables_with_nils
|
51
|
+
assert_nothing_raised do
|
52
|
+
tables = mdb_tables(TEST_DB, :include => nil, :exclude => nil)
|
53
|
+
assert_not_nil tables
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_mdb_schema
|
58
|
+
assert_nothing_raised { @schema = mdb_schema(TEST_DB, 'Employee') }
|
59
|
+
assert_match /DROP TABLE/, @schema
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# def test_csv_to_hashes
|
64
|
+
# employee_hash = csv_to_hashes(@employees_csv)
|
65
|
+
# assert_equal TORBATI_Y, employee_hash.first
|
66
|
+
# end
|
67
|
+
|
68
|
+
def test_sql_select
|
69
|
+
assert_equal ["Torbati","Yolanda", "F", "Programmer", "Engineering", "6044", "1000"],
|
70
|
+
sql_select(TEST_DB, 'Employee', ['*'], "First_Name LIKE 'Yolanda'" ).first
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_compile_conditions
|
74
|
+
conditions = {:foo => 'bar', :baz => 1, :nark => 'noo'}
|
75
|
+
assert_equal "baz like '%1%' AND foo like '%bar%' AND nark like '%noo%'", compile_conditions(conditions)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_compiled_sql_select
|
79
|
+
sql_select(TEST_DB, 'Employee', nil, compile_conditions(:first_name => 'Yolanda'))
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_export_table_to_sql
|
83
|
+
# this test is dependent on specific content in the sample database
|
84
|
+
export = mdb_export(TEST_DB, 'Computer', :format => 'sql').split(LINEBREAK)
|
85
|
+
assert_equal 172, export.size
|
86
|
+
assert export.first =~ /INSERT INTO.*MITSUBISHI.*HL6605ATK/
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_export_table_to_csv
|
90
|
+
export = mdb_export(TEST_DB, 'Employee', :format => 'csv').split(LINEBREAK)
|
91
|
+
assert_equal 53, export.size
|
92
|
+
assert export.first =~ /\"Torbati\",/
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_backends
|
96
|
+
assert_nothing_raised { backends }
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
end
|
data/test/test_record.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'lib', 'active_mdb')
|
2
|
+
require 'test/unit'
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
4
|
+
|
5
|
+
class RecordTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
create_mdb
|
9
|
+
@employee = @db.employee
|
10
|
+
@line = ["Torbati","Yolanda", "F", "Programmer", "Engineering", "6044", "1000"]
|
11
|
+
assert_nothing_raised { @record = Record.new(@employee, @line) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_reflection
|
15
|
+
assert_respond_to @record, 'gender'
|
16
|
+
assert_respond_to @record, 'emp_id'
|
17
|
+
assert_equal 'Yolanda', @record.first_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_display
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/test/test_table.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'lib', 'active_mdb')
|
2
|
+
require 'test/unit'
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
4
|
+
|
5
|
+
class TableTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
|
8
|
+
def setup
|
9
|
+
create_mdb
|
10
|
+
@employee = @db.employee
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_columns
|
14
|
+
columns = @employee.columns
|
15
|
+
assert_kind_of Array, columns
|
16
|
+
assert_kind_of Column, columns.first
|
17
|
+
assert_equal 7, columns.size
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_create_record_struct
|
21
|
+
assert_kind_of Class, @employee.record_struct
|
22
|
+
members = @employee.record_struct.members
|
23
|
+
assert_equal 7, members.size
|
24
|
+
assert members.include?('emp_id')
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_to_csv
|
28
|
+
csv_text = @employee.to_csv
|
29
|
+
assert_kind_of String, csv_text
|
30
|
+
arrays = csv_text.split(MDBTools::LINEBREAK)
|
31
|
+
|
32
|
+
# grab the headers and test for content
|
33
|
+
assert arrays.shift.include?('Emp_Id')
|
34
|
+
assert_equal 53, arrays.size
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_find_first
|
38
|
+
y = @employee.find_first(:first_name => 'Yolanda')
|
39
|
+
assert_kind_of Record, y
|
40
|
+
assert_equal 'Yolanda', y.first_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_find_all
|
44
|
+
a_names = @employee.find_all(:last_name => 'A')
|
45
|
+
assert_kind_of Array, a_names
|
46
|
+
assert_kind_of Record, a_names.first
|
47
|
+
assert_equal 2, a_names.size
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: activemdb
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-03-12 00:00:00 -05:00
|
8
|
+
summary: a reflective wrapper around MDB Tools, which lets POSIX platforms read MS Access (.mdb) files
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: automatthew@gmail.com
|
12
|
+
homepage: http://rubyforge.org/projects/activemdb/
|
13
|
+
rubyforge_project: activemdb
|
14
|
+
description: Intended for exploration and migration, not production. ActiveMDB provides a thin wrapper around the mdb-tables, mdb-schema, mdb-sql, and mdb-export binaries from Brian Bruns's MDB Tools project (http://mdbtools.sourceforge.net/).
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Matthew King
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
- Rakefile
|
36
|
+
- db/sample.mdb
|
37
|
+
- irb.rc
|
38
|
+
- lib/active_mdb.rb
|
39
|
+
- lib/active_mdb/column.rb
|
40
|
+
- lib/active_mdb/mdb.rb
|
41
|
+
- lib/active_mdb/mdb_tools.rb
|
42
|
+
- lib/active_mdb/record.rb
|
43
|
+
- lib/active_mdb/table.rb
|
44
|
+
- support/a.insert
|
45
|
+
- test/test_activemdb.rb
|
46
|
+
- test/test_helper.rb
|
47
|
+
- test/test_mdb.rb
|
48
|
+
- test/test_mdb_tools.rb
|
49
|
+
- test/test_record.rb
|
50
|
+
- test/test_table.rb
|
51
|
+
test_files:
|
52
|
+
- test/test_activemdb.rb
|
53
|
+
- test/test_helper.rb
|
54
|
+
- test/test_mdb.rb
|
55
|
+
- test/test_mdb_tools.rb
|
56
|
+
- test/test_record.rb
|
57
|
+
- test/test_table.rb
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
executables: []
|
63
|
+
|
64
|
+
extensions: []
|
65
|
+
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
dependencies:
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hoe
|
71
|
+
version_requirement:
|
72
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.2.0
|
77
|
+
version:
|