dbf 1.0.10 → 1.0.11
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/.autotest +2 -2
- data/.gitignore +1 -0
- data/History.txt +8 -0
- data/README.markdown +7 -11
- data/Rakefile +1 -3
- data/VERSION.yml +1 -1
- data/dbf.gemspec +6 -5
- data/lib/dbf.rb +1 -1
- data/lib/dbf/column.rb +22 -18
- data/lib/dbf/record.rb +10 -16
- data/spec/functional/dbf_shared.rb +8 -1
- data/spec/unit/record_spec.rb +33 -3
- metadata +4 -3
data/.autotest
CHANGED
@@ -2,10 +2,10 @@ Autotest.add_hook :initialize do |autotest|
|
|
2
2
|
autotest.clear_mappings
|
3
3
|
|
4
4
|
autotest.add_mapping(%r%^lib/dbf/(.*)\.rb$%) do |filename, m|
|
5
|
-
autotest.files_matching %r!spec/(unit|
|
5
|
+
autotest.files_matching %r!spec/(unit|functional)/#{m[1]}_spec.rb!
|
6
6
|
end
|
7
7
|
|
8
|
-
autotest.add_mapping(%r%^spec/(unit|
|
8
|
+
autotest.add_mapping(%r%^spec/(unit|functional)/.*\.rb$%) do |filename, m|
|
9
9
|
filename
|
10
10
|
end
|
11
11
|
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
data/History.txt
CHANGED
data/README.markdown
CHANGED
@@ -2,20 +2,16 @@
|
|
2
2
|
|
3
3
|
DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
*
|
8
|
-
*
|
9
|
-
* To report bugs: <http://www.rubyforge.org/tracker/?group_id=2009>
|
10
|
-
* Questions: Email keithm@infused.org and put DBF somewhere in the subject
|
11
|
-
line
|
5
|
+
* Project page: <http://github.com/infused/dbf>
|
6
|
+
* API Documentation: <http://rdoc.info/projects/infused/dbf>
|
7
|
+
* Report bugs: <http://github.com/infused/dbf/issues>
|
8
|
+
* Questions: Email <mailto:keithm@infused.org> and put DBF somewhere in the subject line
|
12
9
|
|
13
10
|
## Features
|
14
11
|
|
15
12
|
* No external dependencies
|
16
13
|
* Fields are type cast to the appropriate Ruby types
|
17
|
-
* Ability to dump the database schema in the portable ActiveRecord::Schema
|
18
|
-
format
|
14
|
+
* Ability to dump the database schema in the portable ActiveRecord::Schema format
|
19
15
|
|
20
16
|
## Installation
|
21
17
|
|
@@ -30,7 +26,7 @@ Copyright (c) 2006-2009 Keith Morrison <keithm@infused.org>, <www.infused.org>
|
|
30
26
|
|
31
27
|
# Tables are enumerable
|
32
28
|
widget_ids = table.map { |row| row.id }
|
33
|
-
abc_names = table.select { |row| row.name =~ /^[a-cA-C] }
|
29
|
+
abc_names = table.select { |row| row.name =~ /^[a-cA-C]/ }
|
34
30
|
sorted = table.sort_by { |row| row.name }
|
35
31
|
|
36
32
|
# Print the 'name' field from record number 4
|
@@ -91,7 +87,7 @@ A small command-line utility called dbf is installed along with the gem.
|
|
91
87
|
|
92
88
|
(The MIT Licence)
|
93
89
|
|
94
|
-
Copyright (c) 2006-2009 Keith Morrison <keithm@infused.org
|
90
|
+
Copyright (c) 2006-2009 Keith Morrison <mailto:keithm@infused.org>, <http://www.infused.org>
|
95
91
|
|
96
92
|
Permission is hereby granted, free of charge, to any person
|
97
93
|
obtaining a copy of this software and associated documentation
|
data/Rakefile
CHANGED
@@ -4,10 +4,10 @@ $: << File.join(PROJECT_ROOT, 'lib')
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'jeweler'
|
6
6
|
require 'spec/rake/spectask'
|
7
|
+
require 'metric_fu'
|
7
8
|
|
8
9
|
Jeweler::Tasks.new do |s|
|
9
10
|
s.name = 'dbf'
|
10
|
-
s.version = '1.0.9'
|
11
11
|
s.description = 'A small fast library for reading dBase, xBase, Clipper and FoxPro database files.'
|
12
12
|
s.summary = 'Read xBase files'
|
13
13
|
s.platform = Gem::Platform::RUBY
|
@@ -16,11 +16,9 @@ Jeweler::Tasks.new do |s|
|
|
16
16
|
s.add_dependency('activesupport', ['>= 2.1.0'])
|
17
17
|
s.add_dependency('fastercsv', ['>= 1.4.0'])
|
18
18
|
s.homepage = 'http://github.com/infused/dbf'
|
19
|
-
s.rubyforge_project = 'dbf'
|
20
19
|
end
|
21
20
|
|
22
21
|
Jeweler::GemcutterTasks.new
|
23
|
-
Jeweler::RubyforgeTasks.new
|
24
22
|
|
25
23
|
task :default => :spec
|
26
24
|
|
data/VERSION.yml
CHANGED
data/dbf.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dbf}
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.0.11"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Keith Morrison"]
|
12
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-12-05}
|
13
13
|
s.default_executable = %q{dbf}
|
14
14
|
s.description = %q{A small fast library for reading dBase, xBase, Clipper and FoxPro database files.}
|
15
15
|
s.email = %q{keithm@infused.org}
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".autotest",
|
22
|
+
".gitignore",
|
22
23
|
"History.txt",
|
23
24
|
"README.markdown",
|
24
25
|
"Rakefile",
|
@@ -54,7 +55,6 @@ Gem::Specification.new do |s|
|
|
54
55
|
s.homepage = %q{http://github.com/infused/dbf}
|
55
56
|
s.rdoc_options = ["--charset=UTF-8"]
|
56
57
|
s.require_paths = ["lib"]
|
57
|
-
s.rubyforge_project = %q{dbf}
|
58
58
|
s.rubygems_version = %q{1.3.5}
|
59
59
|
s.summary = %q{Read xBase files}
|
60
60
|
s.test_files = [
|
@@ -86,3 +86,4 @@ Gem::Specification.new do |s|
|
|
86
86
|
s.add_dependency(%q<fastercsv>, [">= 1.4.0"])
|
87
87
|
end
|
88
88
|
end
|
89
|
+
|
data/lib/dbf.rb
CHANGED
data/lib/dbf/column.rb
CHANGED
@@ -13,15 +13,13 @@ module DBF
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def type_cast(value)
|
16
|
-
value = value.is_a?(Array) ? value.first : value
|
17
|
-
|
18
16
|
case type
|
19
17
|
when 'N' # number
|
20
|
-
|
18
|
+
unpack_number(value)
|
21
19
|
when 'D' # date
|
22
20
|
value.to_date unless value.blank?
|
23
21
|
when 'L' # logical
|
24
|
-
value
|
22
|
+
boolean(value)
|
25
23
|
when 'I' # integer
|
26
24
|
unpack_integer(value)
|
27
25
|
when 'T' # datetime
|
@@ -37,33 +35,39 @@ module DBF
|
|
37
35
|
DateTime.jd(days, seconds/3600, seconds/60 % 60, seconds % 60)
|
38
36
|
end
|
39
37
|
|
38
|
+
def unpack_number(value)
|
39
|
+
decimal.zero? ? unpack_integer(value) : value.to_f
|
40
|
+
end
|
41
|
+
|
40
42
|
def unpack_integer(value)
|
41
43
|
value.to_i
|
42
44
|
end
|
43
45
|
|
46
|
+
def boolean(value)
|
47
|
+
value.strip =~ /^(y|t)$/i ? true : false
|
48
|
+
end
|
49
|
+
|
44
50
|
def schema_definition
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
when "I"
|
51
|
+
"\"#{name.underscore}\", #{schema_data_type}\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
def schema_data_type
|
55
|
+
case type
|
56
|
+
when "N"
|
57
|
+
decimal > 0 ? ":float" : ":integer"
|
58
|
+
when "I"
|
53
59
|
":integer"
|
54
|
-
when "D"
|
60
|
+
when "D"
|
55
61
|
":date"
|
56
|
-
when "T"
|
62
|
+
when "T"
|
57
63
|
":datetime"
|
58
|
-
when "L"
|
64
|
+
when "L"
|
59
65
|
":boolean"
|
60
|
-
when "M"
|
66
|
+
when "M"
|
61
67
|
":text"
|
62
68
|
else
|
63
69
|
":string, :limit => #{length}"
|
64
70
|
end
|
65
|
-
|
66
|
-
"\"#{name.underscore}\", #{data_type}\n"
|
67
71
|
end
|
68
72
|
|
69
73
|
# strip all non-ascii and non-printable characters
|
data/lib/dbf/record.rb
CHANGED
@@ -14,12 +14,7 @@ module DBF
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def to_a
|
17
|
-
columns.map
|
18
|
-
underscored_column_name = column.name.underscore
|
19
|
-
value = @attributes[column.name.underscore]
|
20
|
-
|
21
|
-
value.is_a?(String) ? value.strip : value
|
22
|
-
end
|
17
|
+
columns.map { |column| @attributes[column.name.underscore] }
|
23
18
|
end
|
24
19
|
|
25
20
|
private
|
@@ -38,22 +33,20 @@ module DBF
|
|
38
33
|
def initialize_values
|
39
34
|
@attributes = columns.inject({}) do |hash, column|
|
40
35
|
if column.type == 'M'
|
41
|
-
starting_block =
|
36
|
+
starting_block = unpack_data(column.length).to_i
|
37
|
+
hash[column.name] = read_memo(starting_block)
|
42
38
|
hash[column.name.underscore] = read_memo(starting_block)
|
43
39
|
else
|
44
|
-
value =
|
40
|
+
value = unpack_data(column.length)
|
41
|
+
hash[column.name] = column.type_cast(value)
|
45
42
|
hash[column.name.underscore] = column.type_cast(value)
|
46
43
|
end
|
47
44
|
hash
|
48
45
|
end
|
49
46
|
end
|
50
47
|
|
51
|
-
def
|
52
|
-
@data.read(
|
53
|
-
end
|
54
|
-
|
55
|
-
def unpack_string(column)
|
56
|
-
unpack_column(column).to_s
|
48
|
+
def unpack_data(length)
|
49
|
+
@data.read(length).unpack("a#{length}").first
|
57
50
|
end
|
58
51
|
|
59
52
|
def read_memo(start_block)
|
@@ -65,7 +58,7 @@ module DBF
|
|
65
58
|
def build_fpt_memo(start_block)
|
66
59
|
@memo.seek(start_block * memo_block_size)
|
67
60
|
|
68
|
-
memo_type, memo_size, memo_string = @memo.read(memo_block_size).unpack("
|
61
|
+
memo_type, memo_size, memo_string = @memo.read(memo_block_size).unpack("NNa*")
|
69
62
|
return nil unless memo_type == 1 and memo_size > 0
|
70
63
|
|
71
64
|
if memo_size > memo_block_content_size
|
@@ -83,7 +76,8 @@ module DBF
|
|
83
76
|
when "83" # dbase iii
|
84
77
|
memo_string = ""
|
85
78
|
loop do
|
86
|
-
|
79
|
+
block = @memo.read(memo_block_size)
|
80
|
+
memo_string << block
|
87
81
|
break if block.rstrip.size < memo_block_size
|
88
82
|
end
|
89
83
|
when "8b" # dbase iv
|
@@ -37,9 +37,16 @@ describe DBF, :shared => true do
|
|
37
37
|
|
38
38
|
specify "column read accessors should return the attribute after typecast" do
|
39
39
|
@table.columns do |column|
|
40
|
-
record = table.records.first
|
40
|
+
record = @table.records.first
|
41
41
|
record.send(column.name).should == record[column.name]
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
specify "column attributes should be accessible in underscored form" do
|
46
|
+
@table.columns do |column|
|
47
|
+
record = @table.records.first
|
48
|
+
record.send(column_name).should == record.send(column_name.underscore)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
45
52
|
end
|
data/spec/unit/record_spec.rb
CHANGED
@@ -8,18 +8,20 @@ describe DBF::Record do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def mock_table(data = '')
|
11
|
+
@column1 = DBF::Column.new 'ColumnName', 'N', 1, 0
|
12
|
+
|
11
13
|
returning mock('table') do |table|
|
12
14
|
table.stub!(:memo_block_size).and_return(8)
|
13
15
|
table.stub!(:memo).and_return(nil)
|
14
|
-
table.stub!(:columns).and_return([])
|
15
|
-
table.stub!(:data)
|
16
|
+
table.stub!(:columns).and_return([@column1])
|
17
|
+
table.stub!(:data).and_return(data)
|
16
18
|
table.stub!(:has_memo_file?).and_return(true)
|
17
19
|
table.data.stub!(:read).and_return(data)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
23
|
context "when initialized" do
|
22
|
-
it "should typecast number columns
|
24
|
+
it "should typecast number columns no decimal places to Integer" do
|
23
25
|
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
24
26
|
table.column("ID").type.should == "N"
|
25
27
|
table.column("ID").decimal.should == 0
|
@@ -90,5 +92,33 @@ describe DBF::Record do
|
|
90
92
|
record.to_a.should == ["Ten records stored in this database", 10.0, nil, false, "0.100000000000000000", nil]
|
91
93
|
end
|
92
94
|
end
|
95
|
+
|
96
|
+
describe '#==' do
|
97
|
+
before do
|
98
|
+
@record = example_record
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should be false if other does not have attributes' do
|
102
|
+
other = mock('object')
|
103
|
+
(@record == other).should be_false
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should be true if other attributes match' do
|
107
|
+
attributes = {:x => 1, :y => 2}
|
108
|
+
@record.stub!(:attributes).and_return(attributes)
|
109
|
+
other = mock('object', :attributes => attributes)
|
110
|
+
(@record == other).should be_true
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe 'unpack_data' do
|
115
|
+
before do
|
116
|
+
@record = example_record('abc')
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should unpack the data' do
|
120
|
+
@record.send(:unpack_data, 3).should == 'abc'
|
121
|
+
end
|
122
|
+
end
|
93
123
|
|
94
124
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Morrison
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-05 00:00:00 -08:00
|
13
13
|
default_executable: dbf
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -42,6 +42,7 @@ extra_rdoc_files:
|
|
42
42
|
- README.markdown
|
43
43
|
files:
|
44
44
|
- .autotest
|
45
|
+
- .gitignore
|
45
46
|
- History.txt
|
46
47
|
- README.markdown
|
47
48
|
- Rakefile
|
@@ -96,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
97
|
version:
|
97
98
|
requirements: []
|
98
99
|
|
99
|
-
rubyforge_project:
|
100
|
+
rubyforge_project:
|
100
101
|
rubygems_version: 1.3.5
|
101
102
|
signing_key:
|
102
103
|
specification_version: 3
|