dbf 1.0.5 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.txt +1 -2
- data/README.txt +1 -9
- data/Rakefile +8 -3
- data/dbf.gemspec +35 -0
- data/lib/dbf/column.rb +6 -12
- data/lib/dbf/globals.rb +12 -4
- data/lib/dbf/record.rb +51 -37
- data/lib/dbf/table.rb +55 -63
- data/spec/fixtures/dbase_03.dbf +0 -0
- data/spec/fixtures/dbase_8b.dbf +0 -0
- data/spec/fixtures/dbase_8b.dbt +0 -0
- data/spec/functional/dbf_shared.rb +3 -5
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/column_spec.rb +57 -20
- data/spec/unit/record_spec.rb +84 -40
- data/spec/unit/table_spec.rb +134 -140
- metadata +54 -47
- data/benchmarks/performance.rb +0 -14
- data/benchmarks/seek_benchmark.rb +0 -18
data/History.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 1.0.6
|
2
|
+
|
3
|
+
* DBF::Table now includes the Enumerable module
|
4
|
+
* Return nil for memo values if the memo file is missing
|
5
|
+
* Finder conditions now support the original and downcased/underscored column names
|
6
|
+
|
1
7
|
== 1.0.5
|
2
8
|
|
3
9
|
* Strip non-ascii characters from column names
|
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -70,14 +70,6 @@ An example of migrating a DBF book table to ActiveRecord using a migration:
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
== Large databases
|
74
|
-
|
75
|
-
DBF::Table defaults to loading all records into memory. This may not be what
|
76
|
-
you want, especially if the database is large. To disable this behavior, set
|
77
|
-
the in_memory option to false during initialization.
|
78
|
-
|
79
|
-
table = DBF::Table.new("old_data.dbf", :in_memory => false)
|
80
|
-
|
81
73
|
== Command-line utility
|
82
74
|
|
83
75
|
A small command-line utility called dbf is installed along with the gem.
|
@@ -97,7 +89,7 @@ A small command-line utility called dbf is installed along with the gem.
|
|
97
89
|
|
98
90
|
(The MIT Licence)
|
99
91
|
|
100
|
-
Copyright (c) 2006-
|
92
|
+
Copyright (c) 2006-2008 Keith Morrison <keithm@infused.org>
|
101
93
|
|
102
94
|
Permission is hereby granted, free of charge, to any person
|
103
95
|
obtaining a copy of this software and associated documentation
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'hoe'
|
|
2
2
|
require 'spec/rake/spectask'
|
3
3
|
|
4
4
|
PKG_NAME = "dbf"
|
5
|
-
PKG_VERSION = "1.0.
|
5
|
+
PKG_VERSION = "1.0.6"
|
6
6
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
7
7
|
|
8
8
|
Hoe.new PKG_NAME, PKG_VERSION do |p|
|
@@ -12,12 +12,12 @@ Hoe.new PKG_NAME, PKG_VERSION do |p|
|
|
12
12
|
p.summary = "A small fast library for reading dBase, xBase, Clipper and FoxPro database files."
|
13
13
|
p.description = p.paragraphs_of("README.txt", 1..3).join("\n\n")
|
14
14
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
15
|
-
p.url = "http://dbf
|
15
|
+
p.url = "http://github.com/infused/dm-dbf/tree/master"
|
16
16
|
p.need_tar = true
|
17
17
|
p.need_zip = true
|
18
18
|
end
|
19
19
|
|
20
|
-
task :default =>
|
20
|
+
task :default => :spec
|
21
21
|
|
22
22
|
desc "Run specs"
|
23
23
|
Spec::Rake::SpecTask.new :spec do |t|
|
@@ -28,4 +28,9 @@ desc "Run spec docs"
|
|
28
28
|
Spec::Rake::SpecTask.new :specdoc do |t|
|
29
29
|
t.spec_opts = ["-f specdoc"]
|
30
30
|
t.spec_files = FileList['spec/**/*spec.rb']
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Generate gemspec"
|
34
|
+
task :gemspec do |t|
|
35
|
+
`rake debug_gem > dbf.gemspec`
|
31
36
|
end
|
data/dbf.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
(in /Users/keithm/projects/dbf)
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = %q{dbf}
|
4
|
+
s.version = "1.0.6"
|
5
|
+
|
6
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
7
|
+
s.authors = ["Keith Morrison"]
|
8
|
+
s.date = %q{2008-06-30}
|
9
|
+
s.default_executable = %q{dbf}
|
10
|
+
s.description = %q{DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files. It is written completely in Ruby and has no external dependencies. Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org, www.infused.org> * Official project page: http://rubyforge.org/projects/dbf * API Documentation: http://dbf.rubyforge.org/docs * To report bugs: http://www.rubyforge.org/tracker/?group_id=2009 * Questions: Email keithm@infused.org and put DBF somewhere in the subject line}
|
11
|
+
s.email = %q{keithm@infused.org}
|
12
|
+
s.executables = ["dbf"]
|
13
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt", "spec/fixtures/dbase_83_schema.txt"]
|
14
|
+
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "bin/dbf", "dbf.gemspec", "lib/dbf.rb", "lib/dbf/column.rb", "lib/dbf/globals.rb", "lib/dbf/record.rb", "lib/dbf/table.rb", "spec/fixtures/dbase_03.dbf", "spec/fixtures/dbase_30.dbf", "spec/fixtures/dbase_30.fpt", "spec/fixtures/dbase_83.dbf", "spec/fixtures/dbase_83.dbt", "spec/fixtures/dbase_83_schema.txt", "spec/fixtures/dbase_8b.dbf", "spec/fixtures/dbase_8b.dbt", "spec/fixtures/dbase_f5.dbf", "spec/fixtures/dbase_f5.fpt", "spec/functional/dbf_shared.rb", "spec/functional/format_03_spec.rb", "spec/functional/format_30_spec.rb", "spec/functional/format_83_spec.rb", "spec/functional/format_8b_spec.rb", "spec/functional/format_f5_spec.rb", "spec/spec_helper.rb", "spec/unit/column_spec.rb", "spec/unit/record_spec.rb", "spec/unit/table_spec.rb"]
|
15
|
+
s.has_rdoc = true
|
16
|
+
s.homepage = %q{http://github.com/infused/dm-dbf/tree/master}
|
17
|
+
s.rdoc_options = ["--main", "README.txt"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = %q{dbf}
|
20
|
+
s.rubygems_version = %q{1.2.0}
|
21
|
+
s.summary = %q{A small fast library for reading dBase, xBase, Clipper and FoxPro database files.}
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 2
|
26
|
+
|
27
|
+
if current_version >= 3 then
|
28
|
+
s.add_runtime_dependency(%q<hoe>, [">= 1.6.0"])
|
29
|
+
else
|
30
|
+
s.add_dependency(%q<hoe>, [">= 1.6.0"])
|
31
|
+
end
|
32
|
+
else
|
33
|
+
s.add_dependency(%q<hoe>, [">= 1.6.0"])
|
34
|
+
end
|
35
|
+
end
|
data/lib/dbf/column.rb
CHANGED
@@ -2,6 +2,8 @@ module DBF
|
|
2
2
|
class ColumnLengthError < DBFError; end
|
3
3
|
|
4
4
|
class Column
|
5
|
+
include Helpers
|
6
|
+
|
5
7
|
attr_reader :name, :type, :length, :decimal
|
6
8
|
|
7
9
|
def initialize(name, type, length, decimal)
|
@@ -10,8 +12,7 @@ module DBF
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def schema_definition
|
13
|
-
|
14
|
-
case type
|
15
|
+
data_type = case type
|
15
16
|
when "N" # number
|
16
17
|
if decimal > 0
|
17
18
|
":float"
|
@@ -26,19 +27,12 @@ module DBF
|
|
26
27
|
":text"
|
27
28
|
else
|
28
29
|
":string, :limit => #{length}"
|
29
|
-
end
|
30
|
-
|
30
|
+
end
|
31
|
+
|
32
|
+
"\"#{underscore(name)}\", #{data_type}\n"
|
31
33
|
end
|
32
34
|
|
33
35
|
private
|
34
|
-
|
35
|
-
def underscore(camel_cased_word)
|
36
|
-
camel_cased_word.to_s.gsub(/::/, '/').
|
37
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
38
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
39
|
-
tr("-", "_").
|
40
|
-
downcase
|
41
|
-
end
|
42
36
|
|
43
37
|
def strip_non_ascii_chars(s)
|
44
38
|
clean = ''
|
data/lib/dbf/globals.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module DBF
|
2
2
|
DBF_HEADER_SIZE = 32
|
3
3
|
FPT_HEADER_SIZE = 512
|
4
|
-
|
4
|
+
BLOCK_HEADER_SIZE = 8
|
5
5
|
DATE_REGEXP = /([\d]{4})([\d]{2})([\d]{2})/
|
6
6
|
VERSION_DESCRIPTIONS = {
|
7
7
|
"02" => "FoxBase",
|
@@ -20,9 +20,17 @@ module DBF
|
|
20
20
|
|
21
21
|
MS_PER_SECOND = 1000
|
22
22
|
MS_PER_MINUTE = MS_PER_SECOND * 60
|
23
|
-
MS_PER_HOUR
|
23
|
+
MS_PER_HOUR = MS_PER_MINUTE * 60
|
24
24
|
|
25
25
|
class DBFError < StandardError; end
|
26
|
-
|
27
|
-
|
26
|
+
|
27
|
+
module Helpers
|
28
|
+
def underscore(camel_cased_word)
|
29
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
30
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
31
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
32
|
+
tr("-", "_").
|
33
|
+
downcase
|
34
|
+
end
|
35
|
+
end
|
28
36
|
end
|
data/lib/dbf/record.rb
CHANGED
@@ -1,33 +1,34 @@
|
|
1
1
|
module DBF
|
2
2
|
class Record
|
3
|
-
|
3
|
+
include Helpers
|
4
4
|
|
5
|
-
|
5
|
+
attr_reader :attributes
|
6
6
|
|
7
7
|
def initialize(table)
|
8
8
|
@table, @data, @memo = table, table.data, table.memo
|
9
|
-
@attributes = {}
|
10
9
|
initialize_values(table.columns)
|
11
10
|
define_accessors
|
12
|
-
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
other.respond_to?(:attributes) && other.attributes == attributes
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
16
18
|
|
17
19
|
def define_accessors
|
18
|
-
return if @@accessors_defined
|
19
20
|
@table.columns.each do |column|
|
20
21
|
underscored_column_name = underscore(column.name)
|
21
|
-
|
22
|
+
unless respond_to?(underscored_column_name)
|
22
23
|
self.class.send :define_method, underscored_column_name do
|
23
24
|
@attributes[column.name]
|
24
25
|
end
|
25
|
-
@@accessors_defined = true
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
def initialize_values(columns)
|
31
|
+
@attributes = {}
|
31
32
|
columns.each do |column|
|
32
33
|
@attributes[column.name] = case column.type
|
33
34
|
when 'N' # number
|
@@ -54,11 +55,13 @@ module DBF
|
|
54
55
|
else
|
55
56
|
unpack_string(column).strip
|
56
57
|
end
|
58
|
+
@attributes[underscore(column.name)] = @attributes[column.name]
|
59
|
+
@attributes
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
60
63
|
def unpack_column(column)
|
61
|
-
@data.read(column.length).unpack("a#{column.length}")
|
64
|
+
@data.read(column.length).to_s.unpack("a#{column.length}")
|
62
65
|
end
|
63
66
|
|
64
67
|
def unpack_string(column)
|
@@ -78,42 +81,53 @@ module DBF
|
|
78
81
|
end
|
79
82
|
|
80
83
|
def read_memo(start_block)
|
81
|
-
return nil if
|
82
|
-
|
83
|
-
|
84
|
-
|
84
|
+
return nil if !@table.has_memo_file? || start_block < 1
|
85
|
+
|
86
|
+
@table.memo_file_format == :fpt ? build_fpt_memo(start_block) : build_dbt_memo(start_block)
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_fpt_memo(start_block)
|
90
|
+
@memo.seek(start_block * memo_block_size)
|
85
91
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
memo_string << @memo.read(memo_size - @table.memo_block_size + FPT_BLOCK_HEADER_SIZE)
|
92
|
-
elsif memo_size > 0 and memo_size < memo_block_content_size
|
93
|
-
memo_string = memo_string[0, memo_size]
|
94
|
-
end
|
92
|
+
memo_type, memo_size, memo_string = @memo.read(memo_block_size).unpack("NNa56")
|
93
|
+
return nil unless memo_type == 1 and memo_size > 0
|
94
|
+
|
95
|
+
if memo_size > memo_block_content_size
|
96
|
+
memo_string << @memo.read(memo_content_size(memo_size))
|
95
97
|
else
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
98
|
+
memo_string = memo_string[0, memo_size]
|
99
|
+
end
|
100
|
+
memo_string
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_dbt_memo(start_block)
|
104
|
+
@memo.seek(start_block * memo_block_size)
|
105
|
+
|
106
|
+
case @table.version
|
107
|
+
when "83" # dbase iii
|
108
|
+
memo_string = ""
|
109
|
+
loop do
|
110
|
+
memo_string << block = @memo.read(memo_block_size)
|
111
|
+
break if block.rstrip.size < memo_block_size
|
106
112
|
end
|
113
|
+
when "8b" # dbase iv
|
114
|
+
memo_type, memo_size = @memo.read(BLOCK_HEADER_SIZE).unpack("LL")
|
115
|
+
memo_string = @memo.read(memo_size)
|
107
116
|
end
|
108
117
|
memo_string
|
109
118
|
end
|
110
119
|
|
111
|
-
def
|
112
|
-
|
113
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
114
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
115
|
-
tr("-", "_").
|
116
|
-
downcase
|
120
|
+
def memo_block_size
|
121
|
+
@memo_block_size ||= @table.memo_block_size
|
117
122
|
end
|
123
|
+
|
124
|
+
def memo_block_content_size
|
125
|
+
memo_block_size - BLOCK_HEADER_SIZE
|
126
|
+
end
|
127
|
+
|
128
|
+
def memo_content_size(memo_size)
|
129
|
+
(memo_size - memo_block_size) + BLOCK_HEADER_SIZE
|
130
|
+
end
|
131
|
+
|
118
132
|
end
|
119
133
|
end
|
data/lib/dbf/table.rb
CHANGED
@@ -1,40 +1,25 @@
|
|
1
1
|
module DBF
|
2
2
|
|
3
3
|
class Table
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# An array of DBF::Column
|
8
|
-
attr_reader :
|
9
|
-
|
10
|
-
#
|
11
|
-
attr_reader :
|
12
|
-
|
13
|
-
#
|
14
|
-
attr_reader :
|
15
|
-
|
16
|
-
#
|
17
|
-
attr_reader :memo_file_format
|
18
|
-
|
19
|
-
# The block size for memo records
|
20
|
-
attr_reader :memo_block_size
|
21
|
-
|
22
|
-
# The options that were used when initializing DBF::Table. This is a Hash.
|
23
|
-
attr_reader :options
|
24
|
-
|
25
|
-
attr_reader :data
|
26
|
-
attr_reader :memo
|
27
|
-
|
28
|
-
# Initialize a new DBF::Reader.
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :column_count # The total number of columns (columns)
|
7
|
+
attr_reader :columns # An array of DBF::Column
|
8
|
+
attr_reader :version # Internal dBase version number
|
9
|
+
attr_reader :last_updated # Last updated datetime
|
10
|
+
attr_reader :memo_file_format # :fpt or :dpt
|
11
|
+
attr_reader :memo_block_size # The block size for memo records
|
12
|
+
attr_reader :options # The options hash that was used to initialize the table
|
13
|
+
attr_reader :data # DBF file handle
|
14
|
+
attr_reader :memo # Memo file handle
|
15
|
+
|
16
|
+
# Initializes a new DBF::Reader
|
29
17
|
# Example:
|
30
18
|
# reader = DBF::Reader.new 'data.dbf'
|
31
19
|
def initialize(filename, options = {})
|
32
|
-
@options = {:in_memory => true, :accessors => true}.merge(options)
|
33
|
-
|
34
|
-
@in_memory = @options[:in_memory]
|
35
|
-
@accessors = @options[:accessors]
|
36
20
|
@data = File.open(filename, 'rb')
|
37
21
|
@memo = open_memo(filename)
|
22
|
+
@options = options
|
38
23
|
reload!
|
39
24
|
end
|
40
25
|
|
@@ -57,8 +42,8 @@ module DBF
|
|
57
42
|
@db_index.size
|
58
43
|
end
|
59
44
|
|
60
|
-
# Returns an instance of DBF::Column for <b>column_name</b>. <b>column_name</b>
|
61
|
-
# can be a symbol or
|
45
|
+
# Returns an instance of DBF::Column for <b>column_name</b>. The <b>column_name</b>
|
46
|
+
# can be a specified as either a symbol or string.
|
62
47
|
def column(column_name)
|
63
48
|
@columns.detect {|f| f.name == column_name.to_s}
|
64
49
|
end
|
@@ -66,22 +51,28 @@ module DBF
|
|
66
51
|
# An array of all the records contained in the database file. Each record is an instance
|
67
52
|
# of DBF::Record (or nil if the record is marked for deletion).
|
68
53
|
def records
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
54
|
+
self.to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :rows, :records
|
58
|
+
|
59
|
+
def each
|
60
|
+
0.upto(@record_count - 1) do |n|
|
61
|
+
seek_to_record(n)
|
62
|
+
unless deleted_record?
|
63
|
+
yield DBF::Record.new(self)
|
64
|
+
end
|
73
65
|
end
|
74
66
|
end
|
75
67
|
|
76
|
-
|
68
|
+
# def get_record_from_file(index)
|
69
|
+
# seek_to_record(@db_index[index])
|
70
|
+
# Record.new(self)
|
71
|
+
# end
|
77
72
|
|
78
73
|
# Returns a DBF::Record (or nil if the record has been marked for deletion) for the record at <tt>index</tt>.
|
79
74
|
def record(index)
|
80
|
-
|
81
|
-
records[index]
|
82
|
-
else
|
83
|
-
get_record_from_file(index)
|
84
|
-
end
|
75
|
+
records[index]
|
85
76
|
end
|
86
77
|
|
87
78
|
# Find records using a simple ActiveRecord-like syntax.
|
@@ -158,21 +149,38 @@ module DBF
|
|
158
149
|
end
|
159
150
|
end
|
160
151
|
|
152
|
+
# Returns the record at <tt>index</tt> by seeking to the record in the
|
153
|
+
# physical database file. See the documentation for the records method for
|
154
|
+
# information on how these two methods differ.
|
155
|
+
def get_record_from_file(index)
|
156
|
+
seek_to_record(@db_index[index])
|
157
|
+
Record.new(self)
|
158
|
+
end
|
159
|
+
|
161
160
|
private
|
162
161
|
|
163
162
|
def open_memo(file)
|
164
|
-
%w(fpt FPT dbt DBT).each do |
|
165
|
-
filename =
|
163
|
+
%w(fpt FPT dbt DBT).each do |extname|
|
164
|
+
filename = replace_extname(file, extname)
|
166
165
|
if File.exists?(filename)
|
167
|
-
@memo_file_format =
|
166
|
+
@memo_file_format = extname.downcase.to_sym
|
168
167
|
return File.open(filename, 'rb')
|
169
168
|
end
|
170
169
|
end
|
171
170
|
nil
|
172
171
|
end
|
172
|
+
|
173
|
+
def replace_extname(filename, extension)
|
174
|
+
filename.sub(/#{File.extname(filename)[1..-1]}$/, extension)
|
175
|
+
end
|
173
176
|
|
174
177
|
def deleted_record?
|
175
|
-
@data.read(1).unpack('a') == ['*']
|
178
|
+
if @data.read(1).unpack('a') == ['*']
|
179
|
+
@data.rewind
|
180
|
+
true
|
181
|
+
else
|
182
|
+
false
|
183
|
+
end
|
176
184
|
end
|
177
185
|
|
178
186
|
def get_header_info
|
@@ -189,7 +197,7 @@ module DBF
|
|
189
197
|
@columns << Column.new(name.strip, type, length, decimal)
|
190
198
|
end
|
191
199
|
end
|
192
|
-
# Reset the column count
|
200
|
+
# Reset the column count in case any were skipped
|
193
201
|
@column_count = @columns.size
|
194
202
|
|
195
203
|
@columns
|
@@ -199,6 +207,7 @@ module DBF
|
|
199
207
|
@memo.rewind
|
200
208
|
if @memo_file_format == :fpt
|
201
209
|
@memo_next_available_block, @memo_block_size = @memo.read(FPT_HEADER_SIZE).unpack('N x2 n')
|
210
|
+
@memo_block_size = 0 if @memo_block_size.nil?
|
202
211
|
else
|
203
212
|
@memo_block_size = 512
|
204
213
|
@memo_next_available_block = File.size(@memo.path) / @memo_block_size
|
@@ -213,23 +222,6 @@ module DBF
|
|
213
222
|
seek(index * @record_length)
|
214
223
|
end
|
215
224
|
|
216
|
-
# Returns the record at <tt>index</tt> by seeking to the record in the
|
217
|
-
# physical database file. See the documentation for the records method for
|
218
|
-
# information on how these two methods differ.
|
219
|
-
def get_record_from_file(index)
|
220
|
-
seek_to_record(@db_index[index])
|
221
|
-
deleted_record? ? nil : Record.new(self)
|
222
|
-
end
|
223
|
-
|
224
|
-
def get_all_records_from_file
|
225
|
-
all_records = []
|
226
|
-
0.upto(@record_count - 1) do |n|
|
227
|
-
seek_to_record(n)
|
228
|
-
all_records << DBF::Record.new(self) unless deleted_record?
|
229
|
-
end
|
230
|
-
all_records
|
231
|
-
end
|
232
|
-
|
233
225
|
def build_db_index
|
234
226
|
@db_index = []
|
235
227
|
@deleted_records = []
|
data/spec/fixtures/dbase_03.dbf
CHANGED
File without changes
|
data/spec/fixtures/dbase_8b.dbf
CHANGED
File without changes
|
data/spec/fixtures/dbase_8b.dbt
CHANGED
File without changes
|
@@ -36,11 +36,9 @@ describe DBF, :shared => true do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
specify "column read accessors should return the attribute after typecast" do
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
record.send(column.name).should == record[column.name]
|
43
|
-
end
|
39
|
+
@table.columns do |column|
|
40
|
+
record = table.records.first
|
41
|
+
record.send(column.name).should == record[column.name]
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
data/spec/spec_helper.rb
CHANGED
data/spec/unit/column_spec.rb
CHANGED
@@ -1,34 +1,71 @@
|
|
1
1
|
require File.dirname(__FILE__) + "/../spec_helper"
|
2
2
|
|
3
|
-
describe DBF::Column
|
3
|
+
describe DBF::Column do
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
context "when initialized" do
|
6
|
+
before do
|
7
|
+
@column = DBF::Column.new "ColumnName", "N", 1, 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should set the #name accessor" do
|
11
|
+
@column.name.should == "ColumnName"
|
12
|
+
end
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
14
|
+
it "should set the #type accessor" do
|
15
|
+
@column.type.should == "N"
|
16
|
+
end
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
|
18
|
+
it "should set the #length accessor" do
|
19
|
+
@column.length.should == 1
|
20
|
+
end
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
it "should set the #decimal accessor" do
|
23
|
+
@column.decimal.should == 0
|
24
|
+
end
|
20
25
|
|
21
|
-
|
22
|
-
|
26
|
+
it "should raise an error if length is greater than 0" do
|
27
|
+
lambda { column = DBF::Column.new "ColumnName", "N", -1, 0 }.should raise_error(DBF::ColumnLengthError)
|
28
|
+
end
|
29
|
+
|
23
30
|
end
|
24
31
|
|
25
|
-
|
26
|
-
|
32
|
+
context "#schema_definition" do
|
33
|
+
it "should define an integer column if type is (N)umber with 9 decimals" do
|
34
|
+
column = DBF::Column.new "ColumnName", "N", 1, 0
|
35
|
+
column.schema_definition.should == "\"column_name\", :integer\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should define a float colmn if type is (N)umber with more than 0 decimals" do
|
39
|
+
column = DBF::Column.new "ColumnName", "N", 1, 2
|
40
|
+
column.schema_definition.should == "\"column_name\", :float\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should define a datetime column if type is (D)ate" do
|
44
|
+
column = DBF::Column.new "ColumnName", "D", 8, 0
|
45
|
+
column.schema_definition.should == "\"column_name\", :datetime\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should define a boolean column if type is (L)ogical" do
|
49
|
+
column = DBF::Column.new "ColumnName", "L", 1, 0
|
50
|
+
column.schema_definition.should == "\"column_name\", :boolean\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should define a text column if type is (M)emo" do
|
54
|
+
column = DBF::Column.new "ColumnName", "M", 1, 0
|
55
|
+
column.schema_definition.should == "\"column_name\", :text\n"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should define a string column with length for any other data types" do
|
59
|
+
column = DBF::Column.new "ColumnName", "X", 20, 0
|
60
|
+
column.schema_definition.should == "\"column_name\", :string, :limit => 20\n"
|
61
|
+
end
|
27
62
|
end
|
28
63
|
|
29
|
-
|
30
|
-
|
31
|
-
|
64
|
+
context "#strip_non_ascii_chars" do
|
65
|
+
it "should strip characters below decimal 32 and above decimal 128" do
|
66
|
+
column = DBF::Column.new "ColumnName", "N", 1, 0
|
67
|
+
column.send(:strip_non_ascii_chars, "--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--").should == "---hello world---"
|
68
|
+
end
|
32
69
|
end
|
33
70
|
|
34
71
|
end
|
data/spec/unit/record_spec.rb
CHANGED
@@ -1,56 +1,100 @@
|
|
1
1
|
require File.dirname(__FILE__) + "/../spec_helper"
|
2
2
|
|
3
|
-
describe DBF::Record
|
4
|
-
|
5
|
-
def
|
6
|
-
|
7
|
-
table.stubs(:data)
|
8
|
-
table.data.stubs(:read).returns(binary_data)
|
9
|
-
table.stubs(:memo).returns(nil)
|
10
|
-
table.stubs(:columns).returns([])
|
11
|
-
DBF::Record.new(table)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should typecast number columns with decimals == 0 to Integer" do
|
15
|
-
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
16
|
-
table.column("ID").type.should == "N"
|
17
|
-
table.column("ID").decimal.should == 0
|
18
|
-
table.records.all? {|record| record.attributes['ID'].should be_kind_of(Integer)}
|
3
|
+
describe DBF::Record do
|
4
|
+
|
5
|
+
def example_record(data = '')
|
6
|
+
DBF::Record.new(mock_table(data))
|
19
7
|
end
|
20
8
|
|
21
|
-
|
22
|
-
table =
|
23
|
-
table.
|
24
|
-
table.
|
25
|
-
table.
|
9
|
+
def mock_table(data = '')
|
10
|
+
table = mock('table')
|
11
|
+
table.stub!(:memo_block_size).and_return(8)
|
12
|
+
table.stub!(:memo).and_return(nil)
|
13
|
+
table.stub!(:columns).and_return([])
|
14
|
+
table.stub!(:data)
|
15
|
+
table.stub!(:has_memo_file?).and_return(true)
|
16
|
+
table.data.stub!(:read).and_return(data)
|
17
|
+
table
|
26
18
|
end
|
27
19
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
20
|
+
context "when initialized" do
|
21
|
+
it "should typecast number columns with decimals == 0 to Integer" do
|
22
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
23
|
+
table.column("ID").type.should == "N"
|
24
|
+
table.column("ID").decimal.should == 0
|
25
|
+
table.records.all? {|record| record.attributes['ID'].should be_kind_of(Integer)}
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should typecast number columns with decimals > 0 to Float" do
|
29
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
30
|
+
table.column("ID").type.should == "N"
|
31
|
+
table.column("COST").decimal.should == 2
|
32
|
+
table.records.all? {|record| record.attributes['COST'].should be_kind_of(Float)}
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should typecast memo columns to String" do
|
36
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
37
|
+
table.column("DESC").type.should == "M"
|
38
|
+
table.records.all? {|record| record.attributes['DESC'].should be_kind_of(String)}
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should typecast logical columns to True or False" do
|
42
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
|
43
|
+
table.column("WEBINCLUDE").type.should == "L"
|
44
|
+
table.records.all? {|record| record.attributes["WEBINCLUDE"].should satisfy {|v| v == true || v == false}}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should typecast datetime columns to DateTime" do
|
48
|
+
record = example_record("Nl%\000\300Z\252\003")
|
49
|
+
column = mock('column', :length => 8)
|
50
|
+
|
51
|
+
record.instance_eval {unpack_datetime(column)}.to_s.should == "2002-10-10T17:04:56+00:00"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should typecast integers to Fixnum" do
|
55
|
+
record = example_record("\017\020\000\000")
|
56
|
+
column = mock('column', :length => 4)
|
57
|
+
|
58
|
+
record.instance_eval {unpack_integer(column)}.should == 4111
|
59
|
+
end
|
32
60
|
end
|
33
61
|
|
34
|
-
|
35
|
-
table
|
36
|
-
|
37
|
-
|
62
|
+
describe '#memo_block_content_size' do
|
63
|
+
it "should equal the difference between the table's memo_block_size and 8" do
|
64
|
+
table = mock_table
|
65
|
+
table.should_receive(:memo_block_size).and_return(128)
|
66
|
+
record = DBF::Record.new(table)
|
67
|
+
|
68
|
+
record.send(:memo_block_content_size).should == 120
|
69
|
+
end
|
38
70
|
end
|
39
71
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
72
|
+
describe '#memo_content_size' do
|
73
|
+
it "should equal 8 plus the difference between memo_size and the table's memo_block_size" do
|
74
|
+
record = example_record
|
75
|
+
record.should_receive(:memo_block_size).and_return(8)
|
76
|
+
|
77
|
+
record.send(:memo_content_size, 1024).should == 1024
|
78
|
+
end
|
46
79
|
end
|
47
80
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
81
|
+
describe '#read_memo' do
|
82
|
+
it 'should return nil if start_block is less than 1' do
|
83
|
+
table = mock_table
|
84
|
+
record = DBF::Record.new(table)
|
85
|
+
|
86
|
+
record.send(:read_memo, 0).should be_nil
|
87
|
+
record.send(:read_memo, -1).should be_nil
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should return nil if memo file is missing' do
|
91
|
+
table = mock_table
|
92
|
+
table.should_receive(:has_memo_file?).and_return(false)
|
93
|
+
record = DBF::Record.new(table)
|
52
94
|
|
53
|
-
|
95
|
+
record.send(:read_memo, 5).should be_nil
|
96
|
+
end
|
97
|
+
|
54
98
|
end
|
55
99
|
|
56
100
|
end
|
data/spec/unit/table_spec.rb
CHANGED
@@ -1,167 +1,161 @@
|
|
1
1
|
require File.dirname(__FILE__) + "/../spec_helper"
|
2
2
|
|
3
|
-
describe DBF::Table
|
4
|
-
|
5
|
-
before(:all) do
|
6
|
-
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should load the data file" do
|
10
|
-
@table.data.should be_kind_of(File)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should locate load the memo file" do
|
14
|
-
@table.has_memo_file?.should be_true
|
15
|
-
@table.instance_eval("@memo").should be_kind_of(File)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should determine the memo file format" do
|
19
|
-
@table.memo_file_format.should == :dbt
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should determine the correct memo block size" do
|
23
|
-
@table.memo_block_size.should == 512
|
24
|
-
end
|
25
|
-
|
26
|
-
it "should default to loading all records into memory" do
|
27
|
-
@table.options[:in_memory].should be_true
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should determine the number of columns in each record" do
|
31
|
-
@table.columns.size.should == 15
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should determine the number of records in the database" do
|
35
|
-
@table.record_count.should == 67
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should determine the database version" do
|
39
|
-
@table.version.should == "83"
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should set the in_memory option" do
|
43
|
-
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf", :in_memory => false
|
44
|
-
table.options[:in_memory].should be_false
|
45
|
-
end
|
3
|
+
describe DBF::Table do
|
46
4
|
|
47
|
-
|
5
|
+
context "when initialized" do
|
6
|
+
before do
|
7
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
8
|
+
end
|
48
9
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should build the records array from disk only on the first request" do
|
56
|
-
@table.expects(:get_all_records_from_file).at_most_once.returns([])
|
57
|
-
3.times { @table.records }
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should read from the records array when using the record() method" do
|
61
|
-
@table.expects(:get_all_records_from_file).at_most_once.returns([])
|
62
|
-
@table.expects(:get_record_from_file).never
|
63
|
-
@table.expects(:records).times(2).returns([])
|
64
|
-
@table.record(1)
|
65
|
-
@table.record(10)
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
10
|
+
it "should load the data file" do
|
11
|
+
@table.data.should be_kind_of(File)
|
12
|
+
end
|
69
13
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
table.expects(:get_all_records_from_file).times(3).returns([])
|
75
|
-
3.times { table.records }
|
76
|
-
end
|
77
|
-
end
|
14
|
+
it "should locate load the memo file" do
|
15
|
+
@table.has_memo_file?.should be_true
|
16
|
+
@table.instance_eval("@memo").should be_kind_of(File)
|
17
|
+
end
|
78
18
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
83
|
-
control_schema = File.read(File.dirname(__FILE__) + '/../fixtures/dbase_83_schema.txt')
|
84
|
-
|
85
|
-
table.schema.should == control_schema
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
19
|
+
it "should determine the memo file format" do
|
20
|
+
@table.memo_file_format.should == :dbt
|
21
|
+
end
|
89
22
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
94
|
-
end
|
95
|
-
|
96
|
-
it "should return the correct record" do
|
97
|
-
@table.find(5).should == @table.record(5)
|
98
|
-
end
|
99
|
-
|
100
|
-
end
|
23
|
+
it "should determine the correct memo block size" do
|
24
|
+
@table.memo_block_size.should == 512
|
25
|
+
end
|
101
26
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
@table.find(:all, "WEIGHT" => 0.0).should == @table.records.select {|r| r.attributes["WEIGHT"] == 0.0}
|
114
|
-
end
|
115
|
-
|
116
|
-
it "with multiple options should search for all search terms as if using AND" do
|
117
|
-
@table.find(:all, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should == []
|
118
|
-
end
|
119
|
-
end
|
27
|
+
it "should determine the number of columns in each record" do
|
28
|
+
@table.columns.size.should == 15
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should determine the number of records in the database" do
|
32
|
+
@table.record_count.should == 67
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should determine the database version" do
|
36
|
+
@table.version.should == "83"
|
37
|
+
end
|
120
38
|
|
121
|
-
describe DBF::Table, "find(:first)" do
|
122
|
-
|
123
|
-
before(:all) do
|
124
|
-
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
125
39
|
end
|
126
40
|
|
127
|
-
|
128
|
-
|
41
|
+
describe "#find" do
|
42
|
+
describe "with index" do
|
43
|
+
it "should return the correct record" do
|
44
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
45
|
+
table.find(5).should == table.record(5)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "with :all" do
|
50
|
+
before do
|
51
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return all records if options are empty" do
|
55
|
+
@table.find(:all).should == @table.records
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return matching records when used with options" do
|
59
|
+
@table.find(:all, "WEIGHT" => 0.0).should == @table.select {|r| r.attributes["WEIGHT"] == 0.0}
|
60
|
+
end
|
61
|
+
|
62
|
+
it "with multiple options should search for all search terms as if using AND" do
|
63
|
+
@table.find(:all, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should == []
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should match original column names" do
|
67
|
+
@table.find(:all, "WEIGHT" => 0.0).should_not be_empty
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should match symbolized column names" do
|
71
|
+
@table.find(:all, :WEIGHT => 0.0).should_not be_empty
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should match downcased column names" do
|
75
|
+
@table.find(:all, "weight" => 0.0).should_not be_empty
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should match symbolized downcased column names" do
|
79
|
+
@table.find(:all, :weight => 0.0).should_not be_empty
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "with :first" do
|
84
|
+
before do
|
85
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should return the first record if options are empty" do
|
89
|
+
@table.find(:first).should == @table.records.first
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return the first matching record when used with options" do
|
93
|
+
@table.find(:first, "CODE" => "C").should == @table.record(5)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "with multiple options should search for all search terms as if using AND" do
|
97
|
+
@table.find(:first, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should be_nil
|
98
|
+
end
|
99
|
+
end
|
129
100
|
end
|
130
101
|
|
131
|
-
|
132
|
-
|
102
|
+
describe "#reload" do
|
103
|
+
# TODO
|
133
104
|
end
|
134
105
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
106
|
+
describe "#column" do
|
107
|
+
it "should accept a string or symbol as input" do
|
108
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
109
|
+
table.column(:IMAGE).should be_kind_of(DBF::Column)
|
110
|
+
table.column("IMAGE").should be_kind_of(DBF::Column)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should return a DBF::Field object when the column_name exists" do
|
114
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
115
|
+
table.column(:IMAGE).should be_kind_of(DBF::Column)
|
116
|
+
end
|
141
117
|
|
142
|
-
|
143
|
-
|
118
|
+
it "should return nil when the column_name does not exist" do
|
119
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
120
|
+
table.column(:NOTANIMAGE).should be_nil
|
121
|
+
end
|
144
122
|
end
|
145
123
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
124
|
+
describe "#schema" do
|
125
|
+
it "should match the test schema fixture" do
|
126
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
127
|
+
control_schema = File.read(File.dirname(__FILE__) + '/../fixtures/dbase_83_schema.txt')
|
128
|
+
|
129
|
+
table.schema.should == control_schema
|
130
|
+
end
|
151
131
|
end
|
152
132
|
|
153
|
-
|
154
|
-
|
155
|
-
|
133
|
+
describe "#version_description" do
|
134
|
+
it "should return a text description of the database type" do
|
135
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
136
|
+
table.version_description.should == "dBase III with memo file"
|
137
|
+
end
|
156
138
|
end
|
157
139
|
|
158
|
-
|
159
|
-
|
160
|
-
|
140
|
+
describe '#replace_extname' do
|
141
|
+
it 'should replace the extname' do
|
142
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
143
|
+
table.send(:replace_extname, "dbase_83.dbf", 'fpt').should == 'dbase_83.fpt'
|
144
|
+
end
|
161
145
|
end
|
162
146
|
|
163
|
-
|
164
|
-
|
147
|
+
describe '#each' do
|
148
|
+
it 'should enumerate all records' do
|
149
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
150
|
+
records = []
|
151
|
+
table.each do |record|
|
152
|
+
records << record
|
153
|
+
end
|
154
|
+
|
155
|
+
records.map! { |r| r.attributes }
|
156
|
+
records.should == table.records.map {|r| r.attributes}
|
157
|
+
end
|
158
|
+
|
165
159
|
end
|
166
160
|
|
167
161
|
end
|
metadata
CHANGED
@@ -1,41 +1,45 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.4
|
3
|
-
specification_version: 1
|
4
2
|
name: dbf
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.0.
|
7
|
-
date: 2007-11-27 00:00:00 -08:00
|
8
|
-
summary: A small fast library for reading dBase, xBase, Clipper and FoxPro database files.
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: keithm@infused.org
|
12
|
-
homepage: http://dbf.rubyforge.org
|
13
|
-
rubyforge_project: dbf
|
14
|
-
description: "DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files. It is written completely in Ruby and has no external dependencies. Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org, www.infused.org> * Official project page: http://rubyforge.org/projects/dbf * API Documentation: http://dbf.rubyforge.org/docs * To report bugs: http://www.rubyforge.org/tracker/?group_id=2009 * Questions: Email keithm@infused.org and put DBF somewhere in the subject line"
|
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:
|
4
|
+
version: 1.0.6
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Keith Morrison
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-08-08 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.7.0
|
24
|
+
version:
|
25
|
+
description: "DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files. It is written completely in Ruby and has no external dependencies. Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org, www.infused.org> * Official project page: http://rubyforge.org/projects/dbf * API Documentation: http://dbf.rubyforge.org/docs * To report bugs: http://www.rubyforge.org/tracker/?group_id=2009 * Questions: Email keithm@infused.org and put DBF somewhere in the subject line"
|
26
|
+
email: keithm@infused.org
|
27
|
+
executables:
|
28
|
+
- dbf
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
- spec/fixtures/dbase_83_schema.txt
|
31
36
|
files:
|
32
37
|
- History.txt
|
33
38
|
- Manifest.txt
|
34
39
|
- README.txt
|
35
40
|
- Rakefile
|
36
|
-
- benchmarks/performance.rb
|
37
|
-
- benchmarks/seek_benchmark.rb
|
38
41
|
- bin/dbf
|
42
|
+
- dbf.gemspec
|
39
43
|
- lib/dbf.rb
|
40
44
|
- lib/dbf/column.rb
|
41
45
|
- lib/dbf/globals.rb
|
@@ -61,29 +65,32 @@ files:
|
|
61
65
|
- spec/unit/column_spec.rb
|
62
66
|
- spec/unit/record_spec.rb
|
63
67
|
- spec/unit/table_spec.rb
|
64
|
-
|
65
|
-
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/infused/dm-dbf/tree/master
|
70
|
+
post_install_message:
|
66
71
|
rdoc_options:
|
67
72
|
- --main
|
68
73
|
- README.txt
|
69
|
-
|
70
|
-
-
|
71
|
-
|
72
|
-
|
73
|
-
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
version:
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: "0"
|
87
|
+
version:
|
78
88
|
requirements: []
|
79
89
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: 1.3.0
|
89
|
-
version:
|
90
|
+
rubyforge_project: dbf
|
91
|
+
rubygems_version: 1.2.0
|
92
|
+
signing_key:
|
93
|
+
specification_version: 2
|
94
|
+
summary: A small fast library for reading dBase, xBase, Clipper and FoxPro database files.
|
95
|
+
test_files: []
|
96
|
+
|
data/benchmarks/performance.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
4
|
-
require 'dbf'
|
5
|
-
require 'profiler'
|
6
|
-
|
7
|
-
dbf = DBF::Reader.new(File.join(File.dirname(__FILE__),'databases', 'foxpro.dbf'))
|
8
|
-
|
9
|
-
Profiler__::start_profile
|
10
|
-
|
11
|
-
dbf.records
|
12
|
-
|
13
|
-
Profiler__::stop_profile
|
14
|
-
Profiler__::print_profile($stdout)
|
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'benchmark'
|
3
|
-
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
4
|
-
require 'dbf'
|
5
|
-
|
6
|
-
puts
|
7
|
-
puts "Runs 5000 random row seeks first using the I/O based record(n) method and then using"
|
8
|
-
puts "using the array of records."
|
9
|
-
puts
|
10
|
-
|
11
|
-
iterations = 5000
|
12
|
-
Benchmark.bm(20) do |x|
|
13
|
-
@dbf = DBF::Reader.new(File.join(File.dirname(__FILE__), '..', 'test', 'databases', 'foxpro.dbf'))
|
14
|
-
max = @dbf.record_count + 1
|
15
|
-
|
16
|
-
x.report("I/O based record(n)") { iterations.times { @dbf.record(rand(max)) } }
|
17
|
-
x.report("array of records[n]") { iterations.times { @dbf.records[rand(max)] } }
|
18
|
-
end
|