dbf 0.5.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/Manifest.txt +4 -7
- data/README.txt +16 -13
- data/Rakefile +1 -1
- data/bin/dbf +1 -1
- data/lib/dbf.rb +2 -2
- data/lib/dbf/{field.rb → column.rb} +3 -3
- data/lib/dbf/record.rb +82 -51
- data/lib/dbf/{reader.rb → table.rb} +71 -75
- data/spec/functional/dbf_shared.rb +27 -18
- data/spec/functional/format_03_spec.rb +4 -4
- data/spec/functional/format_30_spec.rb +4 -4
- data/spec/functional/format_83_spec.rb +4 -4
- data/spec/functional/format_8b_spec.rb +4 -4
- data/spec/functional/format_f5_spec.rb +4 -4
- data/spec/unit/column_spec.rb +34 -0
- data/spec/unit/record_spec.rb +18 -18
- data/spec/unit/table_spec.rb +168 -0
- metadata +6 -9
- data/.DS_Store +0 -0
- data/spec/.DS_Store +0 -0
- data/spec/fixtures/.DS_Store +0 -0
- data/spec/unit/field_spec.rb +0 -34
- data/spec/unit/reader_spec.rb +0 -173
@@ -1,38 +1,47 @@
|
|
1
1
|
describe DBF, :shared => true do
|
2
|
-
specify "sum of
|
3
|
-
header_record_length = @
|
4
|
-
|
2
|
+
specify "sum of column lengths should equal record length specified in header" do
|
3
|
+
header_record_length = @table.instance_eval {@record_length}
|
4
|
+
sum_of_column_lengths = @table.columns.inject(1) {|sum, column| sum + column.length}
|
5
5
|
|
6
|
-
header_record_length.should ==
|
6
|
+
header_record_length.should == sum_of_column_lengths
|
7
7
|
end
|
8
8
|
|
9
9
|
specify "records should be instances of DBF::Record" do
|
10
|
-
@
|
10
|
+
@table.records.all? {|record| record.should be_an_instance_of(DBF::Record)}
|
11
11
|
end
|
12
12
|
|
13
|
-
specify "
|
14
|
-
@
|
13
|
+
specify "columns should be instances of DBF::Column" do
|
14
|
+
@table.columns.all? {|column| column.should be_an_instance_of(DBF::Column)}
|
15
15
|
end
|
16
16
|
|
17
|
-
specify "
|
18
|
-
@
|
17
|
+
specify "column names should not be blank" do
|
18
|
+
@table.columns.all? {|column| column.name.should_not be_empty}
|
19
19
|
end
|
20
20
|
|
21
|
-
specify "
|
22
|
-
|
23
|
-
@
|
21
|
+
specify "column types should be valid" do
|
22
|
+
valid_column_types = %w(C N L D M F B G P Y T I V X @ O +)
|
23
|
+
@table.columns.all? {|column| valid_column_types.should include(column.type)}
|
24
24
|
end
|
25
25
|
|
26
|
-
specify "
|
27
|
-
@
|
26
|
+
specify "column lengths should be instances of Fixnum" do
|
27
|
+
@table.columns.all? {|column| column.length.should be_an_instance_of(Fixnum)}
|
28
28
|
end
|
29
29
|
|
30
|
-
specify "
|
31
|
-
@
|
30
|
+
specify "column lengths should be larger than 0" do
|
31
|
+
@table.columns.all? {|column| column.length.should > 0}
|
32
32
|
end
|
33
33
|
|
34
|
-
specify "
|
35
|
-
@
|
34
|
+
specify "column decimals should be instances of Fixnum" do
|
35
|
+
@table.columns.all? {|column| column.decimal.should be_an_instance_of(Fixnum)}
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "column read accessors should return the attribute after typecast" do
|
39
|
+
if @table.options[:accessors]
|
40
|
+
@table.columns do |column|
|
41
|
+
record = table.records.first
|
42
|
+
record.send(column.name).should == record[column.name]
|
43
|
+
end
|
44
|
+
end
|
36
45
|
end
|
37
46
|
|
38
47
|
end
|
@@ -3,21 +3,21 @@ require File.dirname(__FILE__) + "/dbf_shared"
|
|
3
3
|
|
4
4
|
describe DBF, "of type 03 (dBase III without memo file)" do
|
5
5
|
before(:each) do
|
6
|
-
@
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
|
7
7
|
end
|
8
8
|
|
9
9
|
it_should_behave_like "DBF"
|
10
10
|
|
11
11
|
it "should report the correct version number" do
|
12
|
-
@
|
12
|
+
@table.version.should == "03"
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should have a memo file" do
|
16
|
-
@
|
16
|
+
@table.should_not have_memo_file
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should report the correct memo type" do
|
20
|
-
@
|
20
|
+
@table.memo_file_format.should be_nil
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
@@ -3,21 +3,21 @@ require File.dirname(__FILE__) + "/dbf_shared"
|
|
3
3
|
|
4
4
|
describe DBF, "of type 30 (Visual FoxPro)" do
|
5
5
|
before(:each) do
|
6
|
-
@
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
|
7
7
|
end
|
8
8
|
|
9
9
|
it_should_behave_like "DBF"
|
10
10
|
|
11
11
|
it "should report the correct version number" do
|
12
|
-
@
|
12
|
+
@table.version.should == "30"
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should have a memo file" do
|
16
|
-
@
|
16
|
+
@table.should have_memo_file
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should report the correct memo type" do
|
20
|
-
@
|
20
|
+
@table.memo_file_format.should == :fpt
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
@@ -3,21 +3,21 @@ require File.dirname(__FILE__) + "/dbf_shared"
|
|
3
3
|
|
4
4
|
describe DBF, "of type 83 (dBase III with memo file)" do
|
5
5
|
before(:each) do
|
6
|
-
@
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
7
7
|
end
|
8
8
|
|
9
9
|
it_should_behave_like "DBF"
|
10
10
|
|
11
11
|
it "should report the correct version number" do
|
12
|
-
@
|
12
|
+
@table.version.should == "83"
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should have a memo file" do
|
16
|
-
@
|
16
|
+
@table.should have_memo_file
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should report the correct memo type" do
|
20
|
-
@
|
20
|
+
@table.memo_file_format.should == :dbt
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
@@ -3,21 +3,21 @@ require File.dirname(__FILE__) + "/dbf_shared"
|
|
3
3
|
|
4
4
|
describe DBF, "of type 8b (dBase IV with memo file)" do
|
5
5
|
before(:each) do
|
6
|
-
@
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
|
7
7
|
end
|
8
8
|
|
9
9
|
it_should_behave_like "DBF"
|
10
10
|
|
11
11
|
it "should report the correct version number" do
|
12
|
-
@
|
12
|
+
@table.version.should == "8b"
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should have a memo file" do
|
16
|
-
@
|
16
|
+
@table.should have_memo_file
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should report the correct memo type" do
|
20
|
-
@
|
20
|
+
@table.memo_file_format.should == :dbt
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
@@ -3,21 +3,21 @@ require File.dirname(__FILE__) + "/dbf_shared"
|
|
3
3
|
|
4
4
|
describe DBF, "of type f5 (FoxPro with memo file)" do
|
5
5
|
before(:each) do
|
6
|
-
@
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_f5.dbf"
|
7
7
|
end
|
8
8
|
|
9
9
|
it_should_behave_like "DBF"
|
10
10
|
|
11
11
|
it "should report the correct version number" do
|
12
|
-
@
|
12
|
+
@table.version.should == "f5"
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should have a memo file" do
|
16
|
-
@
|
16
|
+
@table.should have_memo_file
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should report the correct memo type" do
|
20
|
-
@
|
20
|
+
@table.memo_file_format.should == :fpt
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DBF::Column, "when initialized" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@column = DBF::Column.new "ColumnName", "N", 1, 0
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should set the #name accessor" do
|
10
|
+
@column.name.should == "ColumnName"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should set the #type accessor" do
|
14
|
+
@column.type.should == "N"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should set the #length accessor" do
|
18
|
+
@column.length.should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should set the #decimal accessor" do
|
22
|
+
@column.decimal.should == 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should raise an error if length is greater than 0" do
|
26
|
+
lambda { column = DBF::Column.new "ColumnName", "N", -1, 0 }.should raise_error(DBF::ColumnLengthError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should strip null characters from the name" do
|
30
|
+
column = DBF::Column.new "Column\0Name\0", "N", 1, 0
|
31
|
+
column.name.should == "ColumnName"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/spec/unit/record_spec.rb
CHANGED
@@ -2,30 +2,30 @@ require File.dirname(__FILE__) + "/../spec_helper"
|
|
2
2
|
|
3
3
|
describe DBF::Record, "when initialized" do
|
4
4
|
|
5
|
-
it "should typecast number
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
it "should typecast number columns with decimals == 0 to Integer" do
|
6
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
7
|
+
table.column("ID").type.should == "N"
|
8
|
+
table.column("ID").decimal.should == 0
|
9
|
+
table.records.all? {|record| record.attributes['ID'].should be_kind_of(Integer)}
|
10
10
|
end
|
11
11
|
|
12
|
-
it "should typecast number
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
it "should typecast number columns with decimals > 0 to Float" do
|
13
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
14
|
+
table.column("ID").type.should == "N"
|
15
|
+
table.column("COST").decimal.should == 2
|
16
|
+
table.records.all? {|record| record.attributes['COST'].should be_kind_of(Float)}
|
17
17
|
end
|
18
18
|
|
19
|
-
it "should typecast memo
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
it "should typecast memo columns to String" do
|
20
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
21
|
+
table.column("DESC").type.should == "M"
|
22
|
+
table.records.all? {|record| record.attributes['DESC'].should be_kind_of(String)}
|
23
23
|
end
|
24
24
|
|
25
|
-
it "should typecast logical
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
it "should typecast logical columns to True or False" do
|
26
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
|
27
|
+
table.column("WEBINCLUDE").type.should == "L"
|
28
|
+
table.records.all? {|record| record.attributes["WEBINCLUDE"].should satisfy {|v| v == true || v == false}}
|
29
29
|
end
|
30
30
|
|
31
31
|
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DBF::Table, "when initialized" do
|
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 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
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
describe DBF::Table, "when the in_memory flag is true" do
|
50
|
+
|
51
|
+
before(:each) do
|
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
|
69
|
+
|
70
|
+
describe DBF::Table, "when the in_memory flag is false" do
|
71
|
+
|
72
|
+
it "should read the records from disk on every request" do
|
73
|
+
table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf", :in_memory => false
|
74
|
+
table.expects(:get_all_records_from_file).times(3).returns([])
|
75
|
+
3.times { table.records }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe DBF::Table, "schema" do
|
80
|
+
|
81
|
+
it "should match test schema " do
|
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
|
89
|
+
|
90
|
+
describe DBF::Table, "find(index)" do
|
91
|
+
|
92
|
+
before(:all) do
|
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
|
101
|
+
|
102
|
+
describe DBF::Table, "find(:all)" do
|
103
|
+
|
104
|
+
before(:all) do
|
105
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return all records if options are empty" do
|
109
|
+
@table.find(:all).should == @table.records
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should return matching records when used with options" do
|
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
|
120
|
+
|
121
|
+
describe DBF::Table, "find(:first)" do
|
122
|
+
|
123
|
+
before(:all) do
|
124
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should return the first record if options are empty" do
|
128
|
+
@table.find(:first).should == @table.records.first
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should return the first matching record when used with options" do
|
132
|
+
@table.find(:first, "CODE" => "C").should == @table.record(5)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "with multiple options should search for all search terms as if using AND" do
|
136
|
+
@table.find(:first, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should be_nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe DBF::Table do
|
141
|
+
|
142
|
+
before(:each) do
|
143
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should reload all data when sent #reload!" do
|
147
|
+
@table.records
|
148
|
+
@table.instance_eval("@records").should be_kind_of(Array)
|
149
|
+
@table.reload!
|
150
|
+
@table.instance_eval("@records").should be_nil
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should return a DBF::Field object when sent #column with a valid column_name given as a string or symbol" do
|
154
|
+
@table.column("IMAGE").should be_kind_of(DBF::Column)
|
155
|
+
@table.column(:IMAGE).should be_kind_of(DBF::Column)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should return nil when sent #column with an invalid column_name given as a string or symbol" do
|
159
|
+
@table.column("NOTANIMAGE").should be_nil
|
160
|
+
@table.column(:NOTANIMAGE).should be_nil
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should return a text description of the database type when sent #version_description" do
|
164
|
+
@table.version_description.should == "dBase III with memo file"
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|