dbf 3.1.1 → 3.1.2
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 +5 -5
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -2
- data/Gemfile.lock +55 -45
- data/LICENSE +1 -1
- data/README.md +4 -4
- data/Rakefile +4 -4
- data/bin/dbf +10 -10
- data/lib/dbf/column.rb +9 -35
- data/lib/dbf/column_type.rb +1 -1
- data/lib/dbf/database/foxpro.rb +9 -10
- data/lib/dbf/encodings.rb +2 -2
- data/lib/dbf/memo/base.rb +4 -4
- data/lib/dbf/memo/dbase3.rb +1 -1
- data/lib/dbf/memo/dbase4.rb +1 -1
- data/lib/dbf/memo/foxpro.rb +2 -2
- data/lib/dbf/record.rb +12 -19
- data/lib/dbf/schema.rb +42 -33
- data/lib/dbf/table.rb +37 -30
- data/lib/dbf/version.rb +1 -1
- data/spec/dbf/column_spec.rb +1 -1
- data/spec/dbf/database_spec.rb +2 -2
- data/spec/dbf/file_formats_spec.rb +56 -56
- data/spec/dbf/record_spec.rb +6 -7
- data/spec/dbf/table_spec.rb +34 -50
- data/spec/fixtures/{dbase_83_schema.txt → dbase_83_schema_ar.txt} +0 -0
- data/spec/fixtures/dbase_83_schema_sq.txt +21 -0
- data/spec/fixtures/dbase_83_schema_sq_lim.txt +21 -0
- metadata +6 -4
data/lib/dbf/memo/dbase3.rb
CHANGED
data/lib/dbf/memo/dbase4.rb
CHANGED
data/lib/dbf/memo/foxpro.rb
CHANGED
@@ -3,7 +3,7 @@ module DBF
|
|
3
3
|
class Foxpro < Base
|
4
4
|
FPT_HEADER_SIZE = 512
|
5
5
|
|
6
|
-
def build_memo(start_block) # nodoc
|
6
|
+
def build_memo(start_block) # :nodoc:
|
7
7
|
@data.seek offset(start_block)
|
8
8
|
|
9
9
|
memo_type, memo_size, memo_string = @data.read(block_size).unpack('NNa*')
|
@@ -22,7 +22,7 @@ module DBF
|
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
|
-
def block_size # nodoc
|
25
|
+
def block_size # :nodoc:
|
26
26
|
@block_size ||= begin
|
27
27
|
@data.rewind
|
28
28
|
@data.read(FPT_HEADER_SIZE).unpack('x6n').first || 0
|
data/lib/dbf/record.rb
CHANGED
@@ -49,15 +49,6 @@ module DBF
|
|
49
49
|
options.all? { |key, value| self[key] == value }
|
50
50
|
end
|
51
51
|
|
52
|
-
# Overrides standard Object.respond_to? to return true if a
|
53
|
-
# matching column name is found.
|
54
|
-
#
|
55
|
-
# @param [String, Symbol] method
|
56
|
-
# @return [Boolean]
|
57
|
-
def respond_to?(method, *args)
|
58
|
-
underscored_column_names.include?(method.to_s) || super
|
59
|
-
end
|
60
|
-
|
61
52
|
# Maps a row to an array of values
|
62
53
|
#
|
63
54
|
# @return [Array]
|
@@ -67,15 +58,15 @@ module DBF
|
|
67
58
|
|
68
59
|
private
|
69
60
|
|
70
|
-
def attribute_map # nodoc
|
61
|
+
def attribute_map # :nodoc:
|
71
62
|
@columns.map { |column| [column.name, init_attribute(column)] }
|
72
63
|
end
|
73
64
|
|
74
|
-
def get_data(column) # nodoc
|
65
|
+
def get_data(column) # :nodoc:
|
75
66
|
@data.read(column.length)
|
76
67
|
end
|
77
68
|
|
78
|
-
def get_memo(column) # nodoc
|
69
|
+
def get_memo(column) # :nodoc:
|
79
70
|
if @memo
|
80
71
|
@memo.get(memo_start_block(column))
|
81
72
|
else
|
@@ -85,20 +76,18 @@ module DBF
|
|
85
76
|
end
|
86
77
|
end
|
87
78
|
|
88
|
-
def init_attribute(column) # nodoc
|
79
|
+
def init_attribute(column) # :nodoc:
|
89
80
|
value = column.memo? ? get_memo(column) : get_data(column)
|
90
81
|
column.type_cast(value)
|
91
82
|
end
|
92
83
|
|
93
|
-
def memo_start_block(column) # nodoc
|
84
|
+
def memo_start_block(column) # :nodoc:
|
94
85
|
data = get_data(column)
|
95
|
-
if %w
|
96
|
-
data = data.unpack('V').first
|
97
|
-
end
|
86
|
+
data = data.unpack('V').first if %w[30 31].include?(@version)
|
98
87
|
data.to_i
|
99
88
|
end
|
100
89
|
|
101
|
-
def method_missing(method, *args) # nodoc
|
90
|
+
def method_missing(method, *args) # :nodoc:
|
102
91
|
if (index = underscored_column_names.index(method.to_s))
|
103
92
|
attributes[@columns[index].name]
|
104
93
|
else
|
@@ -106,7 +95,11 @@ module DBF
|
|
106
95
|
end
|
107
96
|
end
|
108
97
|
|
109
|
-
def
|
98
|
+
def respond_to_missing?(method, *) # :nodoc:
|
99
|
+
underscored_column_names.include?(method.to_s) || super
|
100
|
+
end
|
101
|
+
|
102
|
+
def underscored_column_names # :nodoc:
|
110
103
|
@underscored_column_names ||= @columns.map(&:underscored_name)
|
111
104
|
end
|
112
105
|
end
|
data/lib/dbf/schema.rb
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
module DBF
|
2
2
|
# The Schema module is mixin for the Table class
|
3
3
|
module Schema
|
4
|
+
FORMATS = [:activerecord, :json, :sequel].freeze
|
5
|
+
|
6
|
+
OTHER_DATA_TYPES = {
|
7
|
+
'Y' => ':decimal, :precision => 15, :scale => 4',
|
8
|
+
'D' => ':date',
|
9
|
+
'T' => ':datetime',
|
10
|
+
'L' => ':boolean',
|
11
|
+
'M' => ':text',
|
12
|
+
'B' => ':binary'
|
13
|
+
}.freeze
|
14
|
+
|
4
15
|
# Generate an ActiveRecord::Schema
|
5
16
|
#
|
6
17
|
# xBase data types are converted to generic types as follows:
|
@@ -24,15 +35,17 @@ module DBF
|
|
24
35
|
# @param [Symbol] format Valid options are :activerecord and :json
|
25
36
|
# @return [String]
|
26
37
|
def schema(format = :activerecord, table_only = false)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
38
|
+
schema_method_name = schema_name(format)
|
39
|
+
send(schema_method_name, table_only)
|
40
|
+
rescue NameError
|
41
|
+
raise ArgumentError, ":#{format} is not a valid schema. Valid schemas are: #{FORMATS.join(', ')}."
|
42
|
+
end
|
43
|
+
|
44
|
+
def schema_name(format) # :nodoc:
|
45
|
+
"#{format}_schema"
|
33
46
|
end
|
34
47
|
|
35
|
-
def activerecord_schema(_table_only = false) # nodoc
|
48
|
+
def activerecord_schema(_table_only = false) # :nodoc:
|
36
49
|
s = "ActiveRecord::Schema.define do\n"
|
37
50
|
s << " create_table \"#{name}\" do |t|\n"
|
38
51
|
columns.each do |column|
|
@@ -42,7 +55,7 @@ module DBF
|
|
42
55
|
s
|
43
56
|
end
|
44
57
|
|
45
|
-
def sequel_schema(table_only = false) # nodoc
|
58
|
+
def sequel_schema(table_only = false) # :nodoc:
|
46
59
|
s = ''
|
47
60
|
s << "Sequel.migration do\n" unless table_only
|
48
61
|
s << " change do\n " unless table_only
|
@@ -51,12 +64,12 @@ module DBF
|
|
51
64
|
s << " column #{sequel_schema_definition(column)}"
|
52
65
|
end
|
53
66
|
s << " end\n"
|
54
|
-
s << " end\n"
|
55
|
-
s << "end\n"
|
67
|
+
s << " end\n" unless table_only
|
68
|
+
s << "end\n" unless table_only
|
56
69
|
s
|
57
70
|
end
|
58
71
|
|
59
|
-
def json_schema(_table_only = false) # nodoc
|
72
|
+
def json_schema(_table_only = false) # :nodoc:
|
60
73
|
columns.map(&:to_hash).to_json
|
61
74
|
end
|
62
75
|
|
@@ -76,30 +89,26 @@ module DBF
|
|
76
89
|
":#{column.underscored_name}, #{schema_data_type(column, :sequel)}\n"
|
77
90
|
end
|
78
91
|
|
79
|
-
def schema_data_type(column, format = :activerecord) # nodoc
|
92
|
+
def schema_data_type(column, format = :activerecord) # :nodoc:
|
80
93
|
case column.type
|
81
|
-
when
|
82
|
-
column
|
83
|
-
when
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
94
|
+
when *%w[N F I]
|
95
|
+
number_data_type(column)
|
96
|
+
when *%w[Y D T L M B]
|
97
|
+
OTHER_DATA_TYPES[column.type]
|
98
|
+
else
|
99
|
+
string_data_format(format, column)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def number_data_type(column)
|
104
|
+
column.decimal > 0 ? ':float' : ':integer'
|
105
|
+
end
|
106
|
+
|
107
|
+
def string_data_format(format, column)
|
108
|
+
if format == :sequel
|
109
|
+
":varchar, :size => #{column.length}"
|
97
110
|
else
|
98
|
-
|
99
|
-
":varchar, :size => #{column.length}"
|
100
|
-
else
|
101
|
-
":string, :limit => #{column.length}"
|
102
|
-
end
|
111
|
+
":string, :limit => #{column.length}"
|
103
112
|
end
|
104
113
|
end
|
105
114
|
end
|
data/lib/dbf/table.rb
CHANGED
@@ -28,14 +28,14 @@ module DBF
|
|
28
28
|
'cb' => 'dBASE IV SQL table files, with memo',
|
29
29
|
'f5' => 'FoxPro with memo file',
|
30
30
|
'fb' => 'FoxPro without memo file'
|
31
|
-
}
|
31
|
+
}.freeze
|
32
32
|
|
33
33
|
FOXPRO_VERSIONS = {
|
34
34
|
'30' => 'Visual FoxPro',
|
35
35
|
'31' => 'Visual FoxPro with AutoIncrement field',
|
36
36
|
'f5' => 'FoxPro with memo file',
|
37
37
|
'fb' => 'FoxPro without memo file'
|
38
|
-
}
|
38
|
+
}.freeze
|
39
39
|
|
40
40
|
attr_accessor :encoding
|
41
41
|
attr_writer :name
|
@@ -157,7 +157,7 @@ module DBF
|
|
157
157
|
|
158
158
|
# @return [String]
|
159
159
|
def name
|
160
|
-
@name ||= filename && File.basename(filename,
|
160
|
+
@name ||= filename && File.basename(filename, '.*')
|
161
161
|
end
|
162
162
|
|
163
163
|
# Retrieve a record by index number.
|
@@ -172,7 +172,7 @@ module DBF
|
|
172
172
|
DBF::Record.new(@data.read(header.record_length), columns, version, @memo)
|
173
173
|
end
|
174
174
|
|
175
|
-
|
175
|
+
alias row record
|
176
176
|
|
177
177
|
# Total number of records
|
178
178
|
#
|
@@ -208,30 +208,29 @@ module DBF
|
|
208
208
|
|
209
209
|
private
|
210
210
|
|
211
|
-
def build_columns # nodoc
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
211
|
+
def build_columns # :nodoc:
|
212
|
+
safe_seek do
|
213
|
+
@data.seek(DBF_HEADER_SIZE)
|
214
|
+
columns = []
|
215
|
+
until end_of_record?
|
216
|
+
column_data = @data.read(DBF_HEADER_SIZE)
|
217
|
+
name, type, length, decimal = column_data.unpack('a10 x a x4 C2')
|
218
|
+
columns << Column.new(self, name, type, length, decimal)
|
219
|
+
end
|
220
|
+
columns
|
218
221
|
end
|
219
|
-
columns
|
220
222
|
end
|
221
223
|
|
222
|
-
def deleted_record? # nodoc
|
224
|
+
def deleted_record? # :nodoc:
|
223
225
|
flag = @data.read(1)
|
224
226
|
flag ? flag.unpack('a') == ['*'] : true
|
225
227
|
end
|
226
228
|
|
227
|
-
def end_of_record? # nodoc
|
228
|
-
|
229
|
-
byte = @data.read(1)
|
230
|
-
@data.seek(original_pos)
|
231
|
-
byte.ord == 13
|
229
|
+
def end_of_record? # :nodoc:
|
230
|
+
safe_seek { @data.read(1).ord == 13 }
|
232
231
|
end
|
233
232
|
|
234
|
-
def find_all(options) # nodoc
|
233
|
+
def find_all(options) # :nodoc:
|
235
234
|
map do |record|
|
236
235
|
if record && record.match?(options)
|
237
236
|
yield record if block_given?
|
@@ -240,19 +239,22 @@ module DBF
|
|
240
239
|
end.compact
|
241
240
|
end
|
242
241
|
|
243
|
-
def find_first(options) # nodoc
|
242
|
+
def find_first(options) # :nodoc:
|
244
243
|
detect { |record| record && record.match?(options) }
|
245
244
|
end
|
246
245
|
|
247
|
-
def foxpro? # nodoc
|
246
|
+
def foxpro? # :nodoc:
|
248
247
|
FOXPRO_VERSIONS.keys.include? version
|
249
248
|
end
|
250
249
|
|
251
|
-
def header
|
252
|
-
@header ||=
|
250
|
+
def header # :nodoc:
|
251
|
+
@header ||= safe_seek do
|
252
|
+
@data.seek(0)
|
253
|
+
Header.new(@data.read DBF_HEADER_SIZE)
|
254
|
+
end
|
253
255
|
end
|
254
256
|
|
255
|
-
def memo_class # nodoc
|
257
|
+
def memo_class # :nodoc:
|
256
258
|
@memo_class ||= begin
|
257
259
|
if foxpro?
|
258
260
|
Memo::Foxpro
|
@@ -262,33 +264,38 @@ module DBF
|
|
262
264
|
end
|
263
265
|
end
|
264
266
|
|
265
|
-
def memo_search_path(io) # nodoc
|
267
|
+
def memo_search_path(io) # :nodoc:
|
266
268
|
dirname = File.dirname(io)
|
267
269
|
basename = File.basename(io, '.*')
|
268
270
|
"#{dirname}/#{basename}*.{fpt,FPT,dbt,DBT}"
|
269
271
|
end
|
270
272
|
|
271
|
-
def open_data(data) # nodoc
|
273
|
+
def open_data(data) # :nodoc:
|
272
274
|
data.is_a?(StringIO) ? data : File.open(data, 'rb')
|
273
275
|
rescue Errno::ENOENT
|
274
276
|
raise DBF::FileNotFoundError, "file not found: #{data}"
|
275
277
|
end
|
276
278
|
|
277
|
-
def open_memo(data, memo = nil) # nodoc
|
279
|
+
def open_memo(data, memo = nil) # :nodoc:
|
278
280
|
if memo
|
279
281
|
meth = memo.is_a?(StringIO) ? :new : :open
|
280
282
|
memo_class.send(meth, memo, version)
|
281
283
|
elsif !data.is_a?(StringIO)
|
282
|
-
files = Dir.glob(memo_search_path
|
284
|
+
files = Dir.glob(memo_search_path(data))
|
283
285
|
files.any? ? memo_class.open(files.first, version) : nil
|
284
286
|
end
|
285
287
|
end
|
286
288
|
|
287
|
-
def
|
289
|
+
def safe_seek # :nodoc:
|
290
|
+
original_pos = @data.pos
|
291
|
+
yield.tap { @data.seek(original_pos) }
|
292
|
+
end
|
293
|
+
|
294
|
+
def seek(offset) # :nodoc:
|
288
295
|
@data.seek(header.header_length + offset)
|
289
296
|
end
|
290
297
|
|
291
|
-
def seek_to_record(index) # nodoc
|
298
|
+
def seek_to_record(index) # :nodoc:
|
292
299
|
seek(index * header.record_length)
|
293
300
|
end
|
294
301
|
end
|
data/lib/dbf/version.rb
CHANGED
data/spec/dbf/column_spec.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
RSpec.describe DBF::Column do
|
6
|
-
let(:table) { DBF::Table.new fixture('dbase_30.dbf')}
|
6
|
+
let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
|
7
7
|
|
8
8
|
context 'when initialized' do
|
9
9
|
let(:column) { DBF::Column.new table, 'ColumnName', 'N', 1, 0 }
|
data/spec/dbf/database_spec.rb
CHANGED
@@ -19,13 +19,13 @@ RSpec.describe DBF::Database::Foxpro do
|
|
19
19
|
|
20
20
|
describe 'it loads the example db correctly' do
|
21
21
|
it 'shows a correct list of tables' do
|
22
|
-
expect(db.table_names.sort).to eq(%w
|
22
|
+
expect(db.table_names.sort).to eq(%w[contacts calls setup types].sort)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
describe '#table' do
|
28
|
-
describe
|
28
|
+
describe 'when accessing related tables' do
|
29
29
|
let(:db) { DBF::Database::Foxpro.new(dbf_path) }
|
30
30
|
|
31
31
|
it 'loads an existing related table' do
|
@@ -1,167 +1,167 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.shared_examples_for 'DBF' do
|
4
|
-
specify
|
5
|
-
header_record_length = table.instance_eval {@header.record_length}
|
6
|
-
sum_of_column_lengths = table.columns.inject(1) {|sum, column| sum += column.length}
|
4
|
+
specify 'sum of column lengths should equal record length specified in header plus one' do
|
5
|
+
header_record_length = table.instance_eval { @header.record_length }
|
6
|
+
sum_of_column_lengths = table.columns.inject(1) { |sum, column| sum += column.length }
|
7
7
|
|
8
8
|
expect(header_record_length).to eq sum_of_column_lengths
|
9
9
|
end
|
10
10
|
|
11
|
-
specify
|
11
|
+
specify 'records should be instances of DBF::Record' do
|
12
12
|
table.each do |record|
|
13
13
|
expect(record).to be_kind_of(DBF::Record)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
specify
|
17
|
+
specify 'record count should be the same as reported in the header' do
|
18
18
|
expect(table.entries.size).to eq table.record_count
|
19
19
|
end
|
20
20
|
|
21
|
-
specify
|
21
|
+
specify 'column names should not be blank' do
|
22
22
|
table.columns.each do |column|
|
23
23
|
expect(column.name).not_to be_empty
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
specify
|
28
|
-
valid_column_types = %w
|
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
29
|
table.columns.each do |column|
|
30
30
|
expect(valid_column_types).to include(column.type)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
specify
|
34
|
+
specify 'column lengths should be instances of Integer' do
|
35
35
|
table.columns.each do |column|
|
36
36
|
expect(column.length).to be_kind_of(Integer)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
specify
|
40
|
+
specify 'column lengths should be larger than 0' do
|
41
41
|
table.columns.each do |column|
|
42
42
|
expect(column.length).to be > 0
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
specify
|
46
|
+
specify 'column decimals should be instances of Integer' do
|
47
47
|
table.columns.each do |column|
|
48
48
|
expect(column.decimal).to be_kind_of(Integer)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
RSpec.describe DBF,
|
53
|
+
RSpec.describe DBF, 'of type 03 (dBase III without memo file)' do
|
54
54
|
let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
|
55
55
|
|
56
|
-
it_should_behave_like
|
56
|
+
it_should_behave_like 'DBF'
|
57
57
|
|
58
|
-
it
|
59
|
-
expect(table.version).to eq
|
58
|
+
it 'should report the correct version number' do
|
59
|
+
expect(table.version).to eq '03'
|
60
60
|
end
|
61
61
|
|
62
|
-
it
|
63
|
-
expect(table.version_description).to eq
|
62
|
+
it 'should report the correct version description' do
|
63
|
+
expect(table.version_description).to eq 'dBase III without memo file'
|
64
64
|
end
|
65
65
|
|
66
|
-
it
|
66
|
+
it 'should determine the number of records' do
|
67
67
|
expect(table.record_count).to eq 14
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
RSpec.describe DBF,
|
71
|
+
RSpec.describe DBF, 'of type 30 (Visual FoxPro)' do
|
72
72
|
let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
|
73
73
|
|
74
|
-
it_should_behave_like
|
74
|
+
it_should_behave_like 'DBF'
|
75
75
|
|
76
|
-
it
|
77
|
-
expect(table.version).to eq
|
76
|
+
it 'should report the correct version number' do
|
77
|
+
expect(table.version).to eq '30'
|
78
78
|
end
|
79
79
|
|
80
|
-
it
|
81
|
-
expect(table.version_description).to eq
|
80
|
+
it 'should report the correct version description' do
|
81
|
+
expect(table.version_description).to eq 'Visual FoxPro'
|
82
82
|
end
|
83
83
|
|
84
|
-
it
|
84
|
+
it 'should determine the number of records' do
|
85
85
|
expect(table.record_count).to eq 34
|
86
86
|
end
|
87
87
|
|
88
|
-
it
|
88
|
+
it 'reads memo data' do
|
89
89
|
expect(table.record(3).classes).to match(/\AAgriculture.*Farming\r\n\Z/m)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
-
RSpec.describe DBF,
|
93
|
+
RSpec.describe DBF, 'of type 31 (Visual FoxPro with AutoIncrement field)' do
|
94
94
|
let(:table) { DBF::Table.new fixture('dbase_31.dbf') }
|
95
95
|
|
96
|
-
it_should_behave_like
|
96
|
+
it_should_behave_like 'DBF'
|
97
97
|
|
98
|
-
it
|
99
|
-
expect(table.version).to eq
|
98
|
+
it 'should have a dBase version of 31' do
|
99
|
+
expect(table.version).to eq '31'
|
100
100
|
end
|
101
101
|
|
102
|
-
it
|
103
|
-
expect(table.version_description).to eq
|
102
|
+
it 'should report the correct version description' do
|
103
|
+
expect(table.version_description).to eq 'Visual FoxPro with AutoIncrement field'
|
104
104
|
end
|
105
105
|
|
106
|
-
it
|
106
|
+
it 'should determine the number of records' do
|
107
107
|
expect(table.record_count).to eq 77
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
RSpec.describe DBF,
|
111
|
+
RSpec.describe DBF, 'of type 83 (dBase III with memo file)' do
|
112
112
|
let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
|
113
113
|
|
114
|
-
it_should_behave_like
|
114
|
+
it_should_behave_like 'DBF'
|
115
115
|
|
116
|
-
it
|
117
|
-
expect(table.version).to eq
|
116
|
+
it 'should report the correct version number' do
|
117
|
+
expect(table.version).to eq '83'
|
118
118
|
end
|
119
119
|
|
120
|
-
it
|
121
|
-
expect(table.version_description).to eq
|
120
|
+
it 'should report the correct version description' do
|
121
|
+
expect(table.version_description).to eq 'dBase III with memo file'
|
122
122
|
end
|
123
123
|
|
124
|
-
it
|
124
|
+
it 'should determine the number of records' do
|
125
125
|
expect(table.record_count).to eq 67
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
RSpec.describe DBF,
|
129
|
+
RSpec.describe DBF, 'of type 8b (dBase IV with memo file)' do
|
130
130
|
let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
|
131
131
|
|
132
|
-
it_should_behave_like
|
132
|
+
it_should_behave_like 'DBF'
|
133
133
|
|
134
|
-
it
|
135
|
-
expect(table.version).to eq
|
134
|
+
it 'should report the correct version number' do
|
135
|
+
expect(table.version).to eq '8b'
|
136
136
|
end
|
137
137
|
|
138
|
-
it
|
139
|
-
expect(table.version_description).to eq
|
138
|
+
it 'should report the correct version description' do
|
139
|
+
expect(table.version_description).to eq 'dBase IV with memo file'
|
140
140
|
end
|
141
141
|
|
142
|
-
it
|
142
|
+
it 'should determine the number of records' do
|
143
143
|
expect(table.record_count).to eq 10
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
-
RSpec.describe DBF,
|
147
|
+
RSpec.describe DBF, 'of type f5 (FoxPro with memo file)' do
|
148
148
|
let(:table) { DBF::Table.new fixture('dbase_f5.dbf') }
|
149
149
|
|
150
|
-
it_should_behave_like
|
150
|
+
it_should_behave_like 'DBF'
|
151
151
|
|
152
|
-
it
|
153
|
-
expect(table.version).to eq
|
152
|
+
it 'should report the correct version number' do
|
153
|
+
expect(table.version).to eq 'f5'
|
154
154
|
end
|
155
155
|
|
156
|
-
it
|
157
|
-
expect(table.version_description).to eq
|
156
|
+
it 'should report the correct version description' do
|
157
|
+
expect(table.version_description).to eq 'FoxPro with memo file'
|
158
158
|
end
|
159
159
|
|
160
|
-
it
|
160
|
+
it 'should determine the number of records' do
|
161
161
|
expect(table.record_count).to eq 975
|
162
162
|
end
|
163
163
|
|
164
|
-
it
|
164
|
+
it 'reads memo data' do
|
165
165
|
expect(table.record(3).datn.to_s).to eq '1870-06-30'
|
166
166
|
end
|
167
167
|
end
|