activemdb 0.1.0
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/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:
|