dbf 0.5.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|