dbf 1.3.0 → 1.5.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.
@@ -1,3 +1,3 @@
1
1
  module DBF
2
- VERSION = '1.3.0'
2
+ VERSION = '1.5.0'
3
3
  end
@@ -1,37 +1,35 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "../spec_helper"))
1
+ require "spec_helper"
2
2
 
3
3
  describe DBF::Column do
4
4
 
5
5
  context "when initialized" do
6
- before do
7
- @column = DBF::Column.new "ColumnName", "N", 1, 0
8
- end
6
+ let(:column) { DBF::Column.new "ColumnName", "N", 1, 0 }
9
7
 
10
8
  it "sets the #name accessor" do
11
- @column.name.should == "ColumnName"
9
+ column.name.should == "ColumnName"
12
10
  end
13
11
 
14
12
  it "sets the #type accessor" do
15
- @column.type.should == "N"
13
+ column.type.should == "N"
16
14
  end
17
15
 
18
16
  it "sets the #length accessor" do
19
- @column.length.should == 1
17
+ column.length.should == 1
20
18
  end
21
19
 
22
20
  it "sets the #decimal accessor" do
23
- @column.decimal.should == 0
21
+ column.decimal.should == 0
24
22
  end
25
23
 
26
- context 'with length of 0' do
24
+ describe 'with length of 0' do
27
25
  specify { lambda { DBF::Column.new "ColumnName", "N", 0, 0 }.should raise_error(DBF::ColumnLengthError) }
28
26
  end
29
27
 
30
- context 'with length less than 0' do
28
+ describe 'with length less than 0' do
31
29
  specify { lambda { DBF::Column.new "ColumnName", "N", -1, 0 }.should raise_error(DBF::ColumnLengthError) }
32
30
  end
33
31
 
34
- context 'with empty column name' do
32
+ describe 'with empty column name' do
35
33
  specify { lambda { DBF::Column.new "\xFF\xFC", "N", 1, 0 }.should raise_error(DBF::ColumnNameError) }
36
34
  end
37
35
  end
@@ -75,60 +73,57 @@ describe DBF::Column do
75
73
  end
76
74
 
77
75
  context 'with type L (logical/boolean)' do
76
+ let(:column) { DBF::Column.new "ColumnName", "L", 1, 0 }
77
+
78
78
  it "casts 'y' to true" do
79
- value = "y"
80
- column = DBF::Column.new "ColumnName", "L", 1, 0
81
- column.type_cast(value).should == true
79
+ column.type_cast('y').should == true
82
80
  end
83
81
 
84
- it "casts 'n' to false" do
85
- value = "n"
86
- column = DBF::Column.new "ColumnName", "L", 1, 0
87
- column.type_cast(value).should == false
82
+ it "casts 't' to true" do
83
+ column.type_cast('t').should == true
84
+ end
85
+
86
+ it "casts value other than 't' or 'y' to false" do
87
+ column.type_cast('n').should == false
88
88
  end
89
89
  end
90
90
 
91
91
  context 'with type T (datetime)' do
92
+ let(:column) { DBF::Column.new "ColumnName", "T", 16, 0 }
93
+
92
94
  context 'with valid datetime' do
93
95
  it "casts to DateTime" do
94
- value = "Nl%\000\300Z\252\003"
95
- column = DBF::Column.new "ColumnName", "T", 16, 0
96
- column.type_cast(value).should == "2002-10-10T17:04:56+00:00"
96
+ column.type_cast("Nl%\000\300Z\252\003").should == "2002-10-10T17:04:56+00:00"
97
97
  end
98
98
  end
99
99
 
100
100
  context 'with invalid datetime' do
101
101
  it "casts to nil" do
102
- value = "Nl%\000\000A\000\999"
103
- column = DBF::Column.new "ColumnName", "T", 16, 0
104
- column.type_cast(value).should be_nil
102
+ column.type_cast("Nl%\000\000A\000\999").should be_nil
105
103
  end
106
104
  end
107
105
  end
108
106
 
109
107
  context 'with type D (date)' do
108
+ let(:column) { DBF::Column.new "ColumnName", "D", 8, 0 }
109
+
110
110
  context 'with valid date' do
111
111
  it "casts to Date" do
112
- value = "20050712"
113
- column = DBF::Column.new "ColumnName", "D", 8, 0
114
- column.type_cast(value).should == Date.new(2005,7,12)
112
+ column.type_cast("20050712").should == Date.new(2005,7,12)
115
113
  end
116
114
  end
117
115
 
118
116
  context 'with invalid date' do
119
117
  it "casts to nil" do
120
- value = "0"
121
- column = DBF::Column.new "ColumnName", "D", 8, 0
122
- column.type_cast(value).should be_nil
118
+ column.type_cast("0").should be_nil
123
119
  end
124
120
  end
125
121
  end
126
122
 
127
123
  context 'with type M (memo)' do
128
124
  it "casts to string" do
129
- value = 'abc'
130
125
  column = DBF::Column.new "ColumnName", "M", 3, 0
131
- column.type_cast(value).should be_a String
126
+ column.type_cast('abc').should be_a String
132
127
  end
133
128
  end
134
129
  end
@@ -172,31 +167,27 @@ describe DBF::Column do
172
167
  end
173
168
  end
174
169
 
175
- context "#strip_non_ascii_chars" do
176
- before do
177
- @column = DBF::Column.new "ColumnName", "N", 1, 0
178
- end
179
-
180
- it "strips characters below decimal 32 and above decimal 127" do
181
- @column.strip_non_ascii_chars("--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--").should == "---hello world---"
170
+ context "#name" do
171
+ it "contains only ASCII characters" do
172
+ column = DBF::Column.new "--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--", "N", 1, 0
173
+ column.name.should == "---hello world---"
182
174
  end
183
175
 
184
- it "truncates characters with decimal 0" do
185
- @column.strip_non_ascii_chars("--\x1F-\x68\x65\x6C\x6C\x6F \x00 world-\x80--").should == "---hello "
176
+ it "is truncated at the null character" do
177
+ column = DBF::Column.new "--\x1F-\x68\x65\x6C\x6C\x6F \x00 world-\x80--", "N", 1, 0
178
+ column.name.should == "---hello "
186
179
  end
187
180
  end
188
181
 
189
182
  context '#decode_date' do
190
- before do
191
- @column = DBF::Column.new "ColumnName", "N", 1, 0
192
- end
183
+ let(:column) { DBF::Column.new "ColumnName", "N", 1, 0 }
193
184
 
194
185
  it 'is nil if value is blank' do
195
- @column.decode_date('').should be_nil
186
+ column.send(:decode_date, '').should be_nil
196
187
  end
197
188
 
198
189
  it 'interperets spaces as zeros' do
199
- @column.decode_date('2010 715').should == Date.parse('20100715')
190
+ column.send(:decode_date, '2010 715').should == Date.parse('20100715')
200
191
  end
201
192
  end
202
193
 
@@ -0,0 +1,178 @@
1
+ require "spec_helper"
2
+
3
+ shared_examples_for 'DBF' do
4
+ specify "sum of column lengths should equal record length specified in header plus one" do
5
+ header_record_length = @table.instance_eval {@record_length}
6
+ sum_of_column_lengths = 1 + @table.columns.sum {|column| column.length}
7
+
8
+ header_record_length.should == sum_of_column_lengths
9
+ end
10
+
11
+ specify "records should be instances of DBF::Record" do
12
+ @table.all? {|record| record.should be_an_instance_of(DBF::Record)}
13
+ end
14
+
15
+ specify "record count should be the same as reported in the header" do
16
+ @table.count.should == @table.record_count
17
+ end
18
+
19
+ specify "columns should be instances of DBF::Column" do
20
+ @table.columns.all? {|column| column.should be_an_instance_of(DBF::Column)}
21
+ end
22
+
23
+ specify "column names should not be blank" do
24
+ @table.columns.all? {|column| column.name.should_not be_empty}
25
+ end
26
+
27
+ specify "column types should be valid" do
28
+ valid_column_types = %w(C N L D M F B G P Y T I V X @ O + 0)
29
+ @table.columns.all? {|column| valid_column_types.should include(column.type)}
30
+ end
31
+
32
+ specify "column lengths should be instances of Fixnum" do
33
+ @table.columns.all? {|column| column.length.should be_an_instance_of(Fixnum)}
34
+ end
35
+
36
+ specify "column lengths should be larger than 0" do
37
+ @table.columns.all? {|column| column.length.should > 0}
38
+ end
39
+
40
+ specify "column decimals should be instances of Fixnum" do
41
+ @table.columns.all? {|column| column.decimal.should be_an_instance_of(Fixnum)}
42
+ end
43
+
44
+ specify "column read accessors should return the attribute after typecast" do
45
+ @table.columns do |column|
46
+ record = @table.records.first
47
+ record.send(column.name).should == record[column.name]
48
+ end
49
+ end
50
+
51
+ specify "column attributes should be accessible in underscored form" do
52
+ @table.columns do |column|
53
+ record = @table.records.first
54
+ record.send(column_name).should == record.send(column_name.underscore)
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ describe DBF, "of type 03 (dBase III without memo file)" do
61
+ before do
62
+ @table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
63
+ end
64
+
65
+ it_should_behave_like "DBF"
66
+
67
+ it "should report the correct version number" do
68
+ @table.version.should == "03"
69
+ end
70
+
71
+ it "should report the correct version description" do
72
+ @table.version_description.should == "dBase III without memo file"
73
+ end
74
+
75
+ it "should determine the number of records" do
76
+ @table.record_count.should == 14
77
+ end
78
+ end
79
+
80
+ describe DBF, "of type 30 (Visual FoxPro)" do
81
+ before do
82
+ @table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
83
+ end
84
+
85
+ it_should_behave_like "DBF"
86
+
87
+ it "should report the correct version number" do
88
+ @table.version.should == "30"
89
+ end
90
+
91
+ it "should report the correct version description" do
92
+ @table.version_description.should == "Visual FoxPro"
93
+ end
94
+
95
+ it "should determine the number of records" do
96
+ @table.record_count.should == 34
97
+ end
98
+ end
99
+
100
+ describe DBF, "of type 31 (Visual FoxPro with AutoIncrement field)" do
101
+ before do
102
+ @table = DBF::Table.new "#{DB_PATH}/dbase_31.dbf"
103
+ end
104
+
105
+ it_should_behave_like "DBF"
106
+
107
+ it "should have a dBase version of 31" do
108
+ @table.version.should == "31"
109
+ end
110
+
111
+ it "should report the correct version description" do
112
+ @table.version_description.should == "Visual FoxPro with AutoIncrement field"
113
+ end
114
+
115
+ it "should determine the number of records" do
116
+ @table.record_count.should == 77
117
+ end
118
+ end
119
+
120
+ describe DBF, "of type 83 (dBase III with memo file)" do
121
+ before do
122
+ @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
123
+ end
124
+
125
+ it_should_behave_like "DBF"
126
+
127
+ it "should report the correct version number" do
128
+ @table.version.should == "83"
129
+ end
130
+
131
+ it "should report the correct version description" do
132
+ @table.version_description.should == "dBase III with memo file"
133
+ end
134
+
135
+ it "should determine the number of records" do
136
+ @table.record_count.should == 67
137
+ end
138
+ end
139
+
140
+ describe DBF, "of type 8b (dBase IV with memo file)" do
141
+ before do
142
+ @table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
143
+ end
144
+
145
+ it_should_behave_like "DBF"
146
+
147
+ it "should report the correct version number" do
148
+ @table.version.should == "8b"
149
+ end
150
+
151
+ it "should report the correct version description" do
152
+ @table.version_description.should == "dBase IV with memo file"
153
+ end
154
+
155
+ it "should determine the number of records" do
156
+ @table.record_count.should == 10
157
+ end
158
+ end
159
+
160
+ describe DBF, "of type f5 (FoxPro with memo file)" do
161
+ before do
162
+ @table = DBF::Table.new "#{DB_PATH}/dbase_f5.dbf"
163
+ end
164
+
165
+ it_should_behave_like "DBF"
166
+
167
+ it "should report the correct version number" do
168
+ @table.version.should == "f5"
169
+ end
170
+
171
+ it "should report the correct version description" do
172
+ @table.version_description.should == "FoxPro with memo file"
173
+ end
174
+
175
+ it "should determine the number of records" do
176
+ @table.record_count.should == 975
177
+ end
178
+ end
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+
3
+ describe DBF::Record do
4
+
5
+ describe '#to_a' do
6
+ it 'should return an ordered array of attribute values' do
7
+ table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
8
+
9
+ record = table.record(0)
10
+ record.to_a.should == ["One", 1.0, Date.new(1970, 1, 1), true, 1.23456789012346, "First memo\r\n\037 \037 \037 \037 "]
11
+
12
+ record = table.record(9)
13
+ record.to_a.should == ["Ten records stored in this database", 10.0, nil, false, 0.1, nil]
14
+ end
15
+ end
16
+
17
+ describe '#==' do
18
+ before do
19
+ table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
20
+ @record = table.record(9)
21
+ end
22
+
23
+ it 'should be false if other does not have attributes' do
24
+ (@record == mock('other')).should be_false
25
+ end
26
+
27
+ it 'should be true if other attributes match' do
28
+ attributes = {:x => 1, :y => 2}
29
+ @record.stub!(:attributes).and_return(attributes)
30
+ other = mock('object', :attributes => attributes)
31
+ (@record == other).should be_true
32
+ end
33
+ end
34
+
35
+ describe 'column accessors' do
36
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"}
37
+
38
+ it 'should define accessor methods for each column' do
39
+ record = table.find(0)
40
+ record.should respond_to(:character)
41
+ record.character.should == 'One'
42
+ end
43
+ end
44
+
45
+ end
@@ -1,67 +1,18 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "../spec_helper"))
1
+ require "spec_helper"
2
2
 
3
- describe DBF::Table do
4
- context "when initialized" do
5
- before 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 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 determine the number of columns in each record" do
27
- @table.columns.size.should == 15
28
- end
29
-
30
- it "should determine the number of records in the database" do
31
- @table.record_count.should == 67
32
- end
33
-
34
- it "should determine the database version" do
35
- @table.version.should == "83"
36
- end
37
- end
38
-
3
+ describe DBF::Table do
39
4
  context "when closed" do
40
5
  before do
41
6
  @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
7
+ @table.close
42
8
  end
43
9
 
44
10
  it "should close the data file" do
45
- @table.close
46
- lambda { @table.record(1) }.should raise_error(IOError)
47
- end
48
- end
49
-
50
- describe "#column" do
51
- it "should accept a string or symbol as input" do
52
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
53
- table.column(:IMAGE).should be_kind_of(DBF::Column)
54
- table.column("IMAGE").should be_kind_of(DBF::Column)
11
+ @table.instance_eval { @data }.should be_closed
55
12
  end
56
13
 
57
- it "should return a DBF::Field object when the column_name exists" do
58
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
59
- table.column(:IMAGE).should be_kind_of(DBF::Column)
60
- end
61
-
62
- it "should return nil when the column_name does not exist" do
63
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
64
- table.column(:NOTANIMAGE).should be_nil
14
+ it "should close the memo file" do
15
+ @table.instance_eval { @memo }.instance_eval { @data }.should be_closed
65
16
  end
66
17
  end
67
18
 
@@ -74,33 +25,6 @@ describe DBF::Table do
74
25
  end
75
26
  end
76
27
 
77
- describe "#version_description" do
78
- it "should return a text description of the database type" do
79
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
80
- table.version_description.should == "dBase III with memo file"
81
- end
82
- end
83
-
84
- describe '#replace_extname' do
85
- it "should change the file extension" do
86
- table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
87
- table.send(:replace_extname, 'dbase_83.dbf', 'fpt').should == 'dbase_83.fpt'
88
- end
89
- end
90
-
91
- describe '#to_a' do
92
- before do
93
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
94
-
95
- @records = []
96
- @table.each {|record| @records << record}
97
- end
98
-
99
- it 'should return an array of records' do
100
- @table.to_a.should == @records
101
- end
102
- end
103
-
104
28
  describe '#to_csv' do
105
29
  before do
106
30
  @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
@@ -115,22 +39,21 @@ describe DBF::Table do
115
39
  @table.to_csv
116
40
  File.exists?('dbase_83.csv').should be_true
117
41
  end
118
-
119
- it 'should create custom csv file' do
120
- @table.to_csv('test.csv')
121
- File.exists?('test.csv').should be_true
42
+
43
+ describe 'when path param passed' do
44
+ it 'should create custom csv file' do
45
+ @table.to_csv('test.csv')
46
+ File.exists?('test.csv').should be_true
47
+ end
122
48
  end
123
49
  end
124
50
 
125
51
  describe "#record" do
126
52
  before do
127
53
  @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
128
-
129
- @records = []
130
- @table.each {|record| @records << record}
131
54
  end
132
55
 
133
- it "should nullify deleted records" do
56
+ it "return nil for deleted records" do
134
57
  @table.stub!(:deleted_record?).and_return(true)
135
58
  @table.record(5).should be_nil
136
59
  end
@@ -145,10 +68,6 @@ describe DBF::Table do
145
68
  @table.stub!(:deleted_record?).and_return(true)
146
69
  @table.record(0).should be_nil
147
70
  end
148
-
149
- it 'should return a DBF::Record' do
150
- @table.record(0).should be_kind_of(DBF::Record)
151
- end
152
71
  end
153
72
 
154
73
  describe "#find" do
@@ -175,9 +94,6 @@ describe DBF::Table do
175
94
  describe "with :all" do
176
95
  before do
177
96
  @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
178
-
179
- @records = []
180
- @table.each {|record| @records << record}
181
97
  end
182
98
 
183
99
  it "should accept a block" do
@@ -189,11 +105,11 @@ describe DBF::Table do
189
105
  end
190
106
 
191
107
  it "should return all records if options are empty" do
192
- @table.find(:all).should == @records
108
+ @table.find(:all).should == @table.to_a
193
109
  end
194
110
 
195
111
  it "should return matching records when used with options" do
196
- @table.find(:all, "WEIGHT" => 0.0).should == @records.select {|r| r.attributes["weight"] == 0.0}
112
+ @table.find(:all, "WEIGHT" => 0.0).should == @table.select {|r| r.attributes["weight"] == 0.0}
197
113
  end
198
114
 
199
115
  it "should AND multiple search terms" do
@@ -220,17 +136,14 @@ describe DBF::Table do
220
136
  describe "with :first" do
221
137
  before do
222
138
  @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
223
-
224
- @records = []
225
- @table.each {|record| @records << record}
226
139
  end
227
140
 
228
141
  it "should return the first record if options are empty" do
229
- @table.find(:first).should == @records.first
142
+ @table.find(:first).should == @table.first
230
143
  end
231
144
 
232
145
  it "should return the first matching record when used with options" do
233
- @table.find(:first, "CODE" => "C").should == @records[5]
146
+ @table.find(:first, "CODE" => "C").should == @table.record(5)
234
147
  end
235
148
 
236
149
  it "should AND multiple search terms" do