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