dbf 1.0.5 → 1.0.6
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 +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
|