dbf 5.1.1 → 5.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +4 -2
- data/dbf.gemspec +6 -9
- data/lib/dbf/column.rb +17 -15
- data/lib/dbf/column_builder.rb +31 -0
- data/lib/dbf/column_type.rb +58 -14
- data/lib/dbf/database/foxpro.rb +19 -32
- data/lib/dbf/file_handler.rb +36 -0
- data/lib/dbf/find.rb +54 -0
- data/lib/dbf/header.rb +5 -8
- data/lib/dbf/memo/base.rb +2 -0
- data/lib/dbf/memo/dbase3.rb +2 -2
- data/lib/dbf/memo/dbase4.rb +2 -2
- data/lib/dbf/memo/foxpro.rb +14 -7
- data/lib/dbf/record.rb +60 -34
- data/lib/dbf/record_context.rb +5 -0
- data/lib/dbf/record_iterator.rb +35 -0
- data/lib/dbf/schema.rb +21 -21
- data/lib/dbf/table.rb +42 -178
- data/lib/dbf/version.rb +1 -1
- data/lib/dbf/version_config.rb +79 -0
- data/lib/dbf.rb +6 -0
- metadata +15 -64
- data/spec/dbf/column_spec.rb +0 -287
- data/spec/dbf/database/foxpro_spec.rb +0 -53
- data/spec/dbf/encoding_spec.rb +0 -49
- data/spec/dbf/file_formats_spec.rb +0 -221
- data/spec/dbf/record_spec.rb +0 -116
- data/spec/dbf/table_spec.rb +0 -377
- data/spec/fixtures/cp1251.dbf +0 -0
- data/spec/fixtures/cp1251_summary.txt +0 -12
- data/spec/fixtures/dbase_02.dbf +0 -0
- data/spec/fixtures/dbase_02_summary.txt +0 -23
- data/spec/fixtures/dbase_03.dbf +0 -0
- data/spec/fixtures/dbase_03_cyrillic.dbf +0 -0
- data/spec/fixtures/dbase_03_cyrillic_summary.txt +0 -11
- data/spec/fixtures/dbase_03_summary.txt +0 -40
- data/spec/fixtures/dbase_30.dbf +0 -0
- data/spec/fixtures/dbase_30.fpt +0 -0
- data/spec/fixtures/dbase_30_summary.txt +0 -154
- data/spec/fixtures/dbase_31.dbf +0 -0
- data/spec/fixtures/dbase_31_summary.txt +0 -20
- data/spec/fixtures/dbase_32.dbf +0 -0
- data/spec/fixtures/dbase_32_summary.txt +0 -11
- data/spec/fixtures/dbase_83.dbf +0 -0
- data/spec/fixtures/dbase_83.dbt +0 -0
- data/spec/fixtures/dbase_83_missing_memo.dbf +0 -0
- data/spec/fixtures/dbase_83_missing_memo_record_0.yml +0 -16
- data/spec/fixtures/dbase_83_record_0.yml +0 -16
- data/spec/fixtures/dbase_83_record_9.yml +0 -23
- data/spec/fixtures/dbase_83_schema_ar.txt +0 -19
- data/spec/fixtures/dbase_83_schema_sq.txt +0 -21
- data/spec/fixtures/dbase_83_schema_sq_lim.txt +0 -21
- data/spec/fixtures/dbase_83_summary.txt +0 -24
- data/spec/fixtures/dbase_8b.dbf +0 -0
- data/spec/fixtures/dbase_8b.dbt +0 -0
- data/spec/fixtures/dbase_8b_summary.txt +0 -15
- data/spec/fixtures/dbase_8c.dbf +0 -0
- data/spec/fixtures/dbase_f5.dbf +0 -0
- data/spec/fixtures/dbase_f5.fpt +0 -0
- data/spec/fixtures/dbase_f5_summary.txt +0 -68
- data/spec/fixtures/foxprodb/FOXPRO-DB-TEST.DBC +0 -0
- data/spec/fixtures/foxprodb/FOXPRO-DB-TEST.DCT +0 -0
- data/spec/fixtures/foxprodb/FOXPRO-DB-TEST.DCX +0 -0
- data/spec/fixtures/foxprodb/calls.CDX +0 -0
- data/spec/fixtures/foxprodb/calls.FPT +0 -0
- data/spec/fixtures/foxprodb/calls.dbf +0 -0
- data/spec/fixtures/foxprodb/contacts.CDX +0 -0
- data/spec/fixtures/foxprodb/contacts.FPT +0 -0
- data/spec/fixtures/foxprodb/contacts.dbf +0 -0
- data/spec/fixtures/foxprodb/setup.CDX +0 -0
- data/spec/fixtures/foxprodb/setup.dbf +0 -0
- data/spec/fixtures/foxprodb/types.CDX +0 -0
- data/spec/fixtures/foxprodb/types.dbf +0 -0
- data/spec/fixtures/polygon.dbf +0 -0
- data/spec/spec_helper.rb +0 -35
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
RSpec.shared_examples_for 'DBF' do
|
|
6
|
-
let(:header_record_length) { table.instance_eval { header.record_length } }
|
|
7
|
-
let(:sum_of_column_lengths) { table.columns.inject(1) { |sum, column| sum + column.length } }
|
|
8
|
-
|
|
9
|
-
specify 'sum of column lengths should equal record length specified in header plus one' do
|
|
10
|
-
expect(header_record_length).to eq sum_of_column_lengths
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
specify 'records should be instances of DBF::Record' do
|
|
14
|
-
expect(table).to all be_a(DBF::Record)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
specify 'record count should be the same as reported in the header' do
|
|
18
|
-
expect(table.entries.size).to eq table.record_count
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
specify 'column names should not be blank' do
|
|
22
|
-
table.columns.each do |column|
|
|
23
|
-
expect(column.name).to_not be_empty
|
|
24
|
-
end
|
|
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.each do |column|
|
|
30
|
-
expect(valid_column_types).to include(column.type)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
specify 'column lengths should be instances of Integer' do
|
|
35
|
-
table.columns.each do |column|
|
|
36
|
-
expect(column.length).to be_a(Integer)
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
specify 'column lengths should be larger than 0' do
|
|
41
|
-
table.columns.each do |column|
|
|
42
|
-
expect(column.length).to be > 0
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
specify 'column decimals should be instances of Integer' do
|
|
47
|
-
table.columns.each do |column|
|
|
48
|
-
expect(column.decimal).to be_a(Integer)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
RSpec.describe DBF, 'of type 02 (FoxBase)' do
|
|
54
|
-
let(:table) { DBF::Table.new fixture('dbase_02.dbf') }
|
|
55
|
-
|
|
56
|
-
it_behaves_like 'DBF'
|
|
57
|
-
|
|
58
|
-
it 'reports the correct version number' do
|
|
59
|
-
expect(table.version).to eq '02'
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'reports the correct version description' do
|
|
63
|
-
expect(table.version_description).to eq 'FoxBase'
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it 'determines the number of records' do
|
|
67
|
-
expect(table.record_count).to eq 9
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
RSpec.describe DBF, 'of type 03 (dBase III without memo file)' do
|
|
72
|
-
let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
|
|
73
|
-
|
|
74
|
-
it_behaves_like 'DBF'
|
|
75
|
-
|
|
76
|
-
it 'reports the correct version number' do
|
|
77
|
-
expect(table.version).to eq '03'
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it 'reports the correct version description' do
|
|
81
|
-
expect(table.version_description).to eq 'dBase III without memo file'
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it 'determines the number of records' do
|
|
85
|
-
expect(table.record_count).to eq 14
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
RSpec.describe DBF, 'of type 30 (Visual FoxPro)' do
|
|
90
|
-
let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
|
|
91
|
-
|
|
92
|
-
it_behaves_like 'DBF'
|
|
93
|
-
|
|
94
|
-
it 'reports the correct version number' do
|
|
95
|
-
expect(table.version).to eq '30'
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
it 'reports the correct version description' do
|
|
99
|
-
expect(table.version_description).to eq 'Visual FoxPro'
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it 'determines the number of records' do
|
|
103
|
-
expect(table.record_count).to eq 34
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
it 'reads memo data' do
|
|
107
|
-
expect(table.record(3).classes).to match(/\AAgriculture.*Farming\r\n\Z/m)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
RSpec.describe DBF, 'of type 31 (Visual FoxPro with AutoIncrement field)' do
|
|
112
|
-
let(:table) { DBF::Table.new fixture('dbase_31.dbf') }
|
|
113
|
-
|
|
114
|
-
it_behaves_like 'DBF'
|
|
115
|
-
|
|
116
|
-
it 'has a dBase version of 31' do
|
|
117
|
-
expect(table.version).to eq '31'
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
it 'reports the correct version description' do
|
|
121
|
-
expect(table.version_description).to eq 'Visual FoxPro with AutoIncrement field'
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it 'determines the number of records' do
|
|
125
|
-
expect(table.record_count).to eq 77
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
RSpec.describe DBF, 'of type 32 (Visual FoxPro with field type Varchar or Varbinary)' do
|
|
130
|
-
let(:table) { DBF::Table.new fixture('dbase_32.dbf') }
|
|
131
|
-
|
|
132
|
-
it_behaves_like 'DBF'
|
|
133
|
-
|
|
134
|
-
it 'has a dBase version of 32' do
|
|
135
|
-
expect(table.version).to eq '32'
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
it 'reports the correct version description' do
|
|
139
|
-
expect(table.version_description).to eq 'Visual FoxPro with field type Varchar or Varbinary'
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
it 'determines the number of records' do
|
|
143
|
-
expect(table.record_count).to eq 1
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
RSpec.describe DBF, 'of type 83 (dBase III with memo file)' do
|
|
148
|
-
let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
|
|
149
|
-
|
|
150
|
-
it_behaves_like 'DBF'
|
|
151
|
-
|
|
152
|
-
it 'reports the correct version number' do
|
|
153
|
-
expect(table.version).to eq '83'
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
it 'reports the correct version description' do
|
|
157
|
-
expect(table.version_description).to eq 'dBase III with memo file'
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
it 'determines the number of records' do
|
|
161
|
-
expect(table.record_count).to eq 67
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
RSpec.describe DBF, 'of type 8b (dBase IV with memo file)' do
|
|
166
|
-
let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
|
|
167
|
-
|
|
168
|
-
it_behaves_like 'DBF'
|
|
169
|
-
|
|
170
|
-
it 'reports the correct version number' do
|
|
171
|
-
expect(table.version).to eq '8b'
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
it 'reports the correct version description' do
|
|
175
|
-
expect(table.version_description).to eq 'dBase IV with memo file'
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
it 'determines the number of records' do
|
|
179
|
-
expect(table.record_count).to eq 10
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
RSpec.describe DBF, 'of type 8c (unknown)' do
|
|
184
|
-
let(:table) { DBF::Table.new fixture('dbase_8c.dbf') }
|
|
185
|
-
|
|
186
|
-
it_behaves_like 'DBF'
|
|
187
|
-
|
|
188
|
-
it 'reports the correct version number' do
|
|
189
|
-
expect(table.version).to eq '8c'
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
it 'reports the correct version description' do
|
|
193
|
-
expect(table.version_description).to eq 'dBase 7'
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
it 'determines the number of records' do
|
|
197
|
-
expect(table.record_count).to eq 10
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
RSpec.describe DBF, 'of type f5 (FoxPro with memo file)' do
|
|
202
|
-
let(:table) { DBF::Table.new fixture('dbase_f5.dbf') }
|
|
203
|
-
|
|
204
|
-
it_behaves_like 'DBF'
|
|
205
|
-
|
|
206
|
-
it 'reports the correct version number' do
|
|
207
|
-
expect(table.version).to eq 'f5'
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
it 'reports the correct version description' do
|
|
211
|
-
expect(table.version_description).to eq 'FoxPro with memo file'
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it 'determines the number of records' do
|
|
215
|
-
expect(table.record_count).to eq 975
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
it 'reads memo data' do
|
|
219
|
-
expect(table.record(3).datn.to_s).to eq '1870-06-30'
|
|
220
|
-
end
|
|
221
|
-
end
|
data/spec/dbf/record_spec.rb
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
RSpec.describe DBF::Record do
|
|
6
|
-
describe '#to_a' do
|
|
7
|
-
let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
|
|
8
|
-
|
|
9
|
-
it 'returns an ordered array of attribute values' do
|
|
10
|
-
record = table.record(0)
|
|
11
|
-
expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_record_0.yml'))
|
|
12
|
-
|
|
13
|
-
record = table.record(9)
|
|
14
|
-
expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_record_9.yml'))
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
describe 'with missing memo file' do
|
|
18
|
-
describe 'when opening a path' do
|
|
19
|
-
let(:table) { DBF::Table.new fixture('dbase_83_missing_memo.dbf') }
|
|
20
|
-
|
|
21
|
-
it 'returns nil values for memo fields' do
|
|
22
|
-
record = table.record(0)
|
|
23
|
-
expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml'))
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
describe 'when opening StringIO' do
|
|
29
|
-
let(:data) { StringIO.new(File.read(fixture('dbase_83_missing_memo.dbf'))) }
|
|
30
|
-
let(:table) { DBF::Table.new(data) }
|
|
31
|
-
|
|
32
|
-
it 'returns nil values for memo fields' do
|
|
33
|
-
record = table.record(0)
|
|
34
|
-
expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml'))
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
describe '#==' do
|
|
40
|
-
let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
|
|
41
|
-
let(:record) { table.record(9) }
|
|
42
|
-
|
|
43
|
-
describe 'when other does not have attributes' do
|
|
44
|
-
it 'returns false' do
|
|
45
|
-
expect(record == instance_double(DBF::Record)).to be_falsey
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
describe 'if other attributes match' do
|
|
50
|
-
let(:attributes) { {x: 1, y: 2} }
|
|
51
|
-
let(:other) { instance_double(DBF::Record, attributes: attributes) }
|
|
52
|
-
|
|
53
|
-
before do
|
|
54
|
-
allow(record).to receive(:attributes).and_return(attributes)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
it 'returns true' do
|
|
58
|
-
expect(record == other).to be_truthy
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
describe 'column accessors' do
|
|
65
|
-
let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
|
|
66
|
-
let(:record) { table.find(0) }
|
|
67
|
-
|
|
68
|
-
%w[character numerical date logical float memo].each do |column_name|
|
|
69
|
-
it "defines accessor method for '#{column_name}' column" do
|
|
70
|
-
expect(record).to respond_to(column_name.to_sym)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe 'column data for table' do
|
|
77
|
-
describe 'using specified in dbf encoding' do
|
|
78
|
-
let(:table) { DBF::Table.new fixture('cp1251.dbf') }
|
|
79
|
-
let(:record) { table.find(0) }
|
|
80
|
-
|
|
81
|
-
it 'encodes to default system encoding' do
|
|
82
|
-
expect(record.name.encoding).to eq Encoding.default_external
|
|
83
|
-
|
|
84
|
-
# russian a
|
|
85
|
-
expect(record.name.encode('UTF-8').unpack1('H4')).to eq 'd0b0'
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
describe 'overriding specified in dbf encoding' do
|
|
90
|
-
let(:table) { DBF::Table.new fixture('cp1251.dbf'), nil, 'cp866' }
|
|
91
|
-
let(:record) { table.find(0) }
|
|
92
|
-
|
|
93
|
-
it 'transcodes from manually specified encoding to default system encoding' do
|
|
94
|
-
expect(record.name.encoding).to eq Encoding.default_external
|
|
95
|
-
|
|
96
|
-
# russian а encoded in cp1251 and read as if it was encoded in cp866
|
|
97
|
-
expect(record.name.encode('UTF-8').unpack1('H4')).to eq 'd180'
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
describe '#attributes' do
|
|
103
|
-
let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
|
|
104
|
-
let(:record) { table.find(0) }
|
|
105
|
-
|
|
106
|
-
it 'is a hash of attribute name/value pairs' do
|
|
107
|
-
expect(record.attributes).to be_a(Hash)
|
|
108
|
-
expect(record.attributes['CHARACTER']).to eq 'One'
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it 'has only original field names as keys' do
|
|
112
|
-
original_field_names = %w[CHARACTER DATE FLOAT LOGICAL MEMO NUMERICAL]
|
|
113
|
-
expect(record.attributes.keys.sort).to eq original_field_names
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|