dbf 2.0.13 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/dbf.gemspec +1 -4
- data/lib/dbf.rb +1 -3
- data/lib/dbf/column/base.rb +6 -2
- data/lib/dbf/record.rb +6 -0
- data/lib/dbf/schema.rb +16 -2
- data/lib/dbf/table.rb +13 -15
- data/lib/dbf/version.rb +1 -1
- data/spec/dbf/column_spec.rb +80 -80
- data/spec/dbf/table_spec.rb +27 -0
- data/spec/spec_helper.rb +0 -7
- metadata +2 -17
- data/Gemfile.travis18 +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7051deaf2cb9c0f103fe91bb811ac576453e1c4
|
4
|
+
data.tar.gz: f0c06a0ca2a0cd6b2ac8e29eaa8d14f2b496a5d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a963a10bdb5e02c5dce9b8b7f5d8446e636b4f07ab20a07c19611617edd5d3b5f461df2537ce8be97473a1ceafc2f9cd94a9f8bfcaff6fe3e1acbf4828bfab6
|
7
|
+
data.tar.gz: 1dbcdf3f5a5a84cc7fba894556f120e0fc23b4ca46572cf2ef9cb8fe4dac04a24ecf009f9e34786a75be8ec38f9144b38682496c22a3d4d61aee66d0ad97ab10
|
data/README.md
CHANGED
@@ -17,14 +17,14 @@ database files
|
|
17
17
|
subject line
|
18
18
|
* Change log: <https://github.com/infused/dbf/blob/master/CHANGELOG.md>
|
19
19
|
|
20
|
+
NOTE: beginning with version 3 we have dropped support for Ruby 1.8 and 1.9. If you need support for older Rubies, please use 2.0.x (https://github.com/infused/dbf/tree/2_stable)
|
21
|
+
|
20
22
|
## Compatibility
|
21
23
|
|
22
24
|
DBF is tested to work with the following versions of ruby:
|
23
25
|
|
24
|
-
* MRI Ruby
|
25
|
-
* JRuby
|
26
|
-
* REE 1.8.7
|
27
|
-
* Rubinius 2.1+
|
26
|
+
* MRI Ruby 2.0.x, 2.1.x, 2.2.x
|
27
|
+
* JRuby head
|
28
28
|
|
29
29
|
## Installation
|
30
30
|
|
data/dbf.gemspec
CHANGED
@@ -11,7 +11,6 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.summary = 'Read xBase files'
|
12
12
|
s.description = 'A small fast library for reading dBase, xBase, Clipper and FoxPro database files.'
|
13
13
|
s.license = 'MIT'
|
14
|
-
|
15
14
|
s.bindir = 'bin'
|
16
15
|
s.executables = ['dbf']
|
17
16
|
s.rdoc_options = ['--charset=UTF-8']
|
@@ -19,7 +18,5 @@ Gem::Specification.new do |s|
|
|
19
18
|
s.files = Dir['[A-Z]*', '{bin,docs,lib,spec}/**/*', 'dbf.gemspec']
|
20
19
|
s.test_files = Dir.glob('spec/**/*_spec.rb')
|
21
20
|
s.require_paths = ['lib']
|
22
|
-
|
23
|
-
s.required_rubygems_version = '>= 1.3.0'
|
24
|
-
s.add_dependency 'fastercsv', '~> 1.5'
|
21
|
+
s.required_rubygems_version = Gem::Requirement.new('>= 1.3.0')
|
25
22
|
end
|
data/lib/dbf.rb
CHANGED
data/lib/dbf/column/base.rb
CHANGED
@@ -71,6 +71,10 @@ module DBF
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
+
def to_hash
|
75
|
+
{name: name, type: type, length: length, decimal: decimal}
|
76
|
+
end
|
77
|
+
|
74
78
|
private
|
75
79
|
|
76
80
|
def type_cast_methods # nodoc
|
@@ -88,8 +92,8 @@ module DBF
|
|
88
92
|
end
|
89
93
|
|
90
94
|
def decode_date(value) # nodoc
|
91
|
-
value.
|
92
|
-
|
95
|
+
v = value.tr(' ', '0')
|
96
|
+
v !~ /\S/ ? nil : Date.parse(v)
|
93
97
|
rescue
|
94
98
|
nil
|
95
99
|
end
|
data/lib/dbf/record.rb
CHANGED
@@ -72,6 +72,12 @@ module DBF
|
|
72
72
|
|
73
73
|
private
|
74
74
|
|
75
|
+
def file_offset(attribute_name)
|
76
|
+
column = @columns.detect { |c| c.name == attribute_name.to_s }
|
77
|
+
index = @columns.index(column)
|
78
|
+
@columns[0, index + 1].compact.reduce(0) { |x, c| x += c.length }
|
79
|
+
end
|
80
|
+
|
75
81
|
def method_missing(method, *args) # nodoc
|
76
82
|
if (index = underscored_column_names.index(method.to_s))
|
77
83
|
attributes[@columns[index].name]
|
data/lib/dbf/schema.rb
CHANGED
@@ -21,15 +21,29 @@ module DBF
|
|
21
21
|
# t.column :notes, :text
|
22
22
|
# end
|
23
23
|
#
|
24
|
+
# @param [Symbol] format Valid options are :activerecord and :json
|
24
25
|
# @return [String]
|
25
|
-
def schema
|
26
|
+
def schema(format = :activerecord)
|
27
|
+
supported_formats = [:activerecord, :json]
|
28
|
+
if supported_formats.include?(format)
|
29
|
+
send "#{format}_schema"
|
30
|
+
else
|
31
|
+
raise ArgumentError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def activerecord_schema
|
26
36
|
s = "ActiveRecord::Schema.define do\n"
|
27
|
-
s << " create_table \"#{File.basename(@data.path,
|
37
|
+
s << " create_table \"#{File.basename(@data.path, '.*')}\" do |t|\n"
|
28
38
|
columns.each do |column|
|
29
39
|
s << " t.column #{column.schema_definition}"
|
30
40
|
end
|
31
41
|
s << " end\nend"
|
32
42
|
s
|
33
43
|
end
|
44
|
+
|
45
|
+
def json_schema
|
46
|
+
columns.map(&:to_hash).to_json
|
47
|
+
end
|
34
48
|
end
|
35
49
|
end
|
data/lib/dbf/table.rb
CHANGED
@@ -38,7 +38,7 @@ module DBF
|
|
38
38
|
}
|
39
39
|
|
40
40
|
attr_reader :header
|
41
|
-
attr_accessor :encoding
|
41
|
+
attr_accessor :encoding
|
42
42
|
|
43
43
|
# Opens a DBF::Table
|
44
44
|
# Examples:
|
@@ -148,7 +148,7 @@ module DBF
|
|
148
148
|
# @param [optional String] path Defaults to STDOUT
|
149
149
|
def to_csv(path = nil)
|
150
150
|
out_io = path ? File.open(path, 'w') : $stdout
|
151
|
-
csv =
|
151
|
+
csv = CSV.new(out_io, force_quotes: true)
|
152
152
|
csv << column_names
|
153
153
|
each { |record| csv << record.to_a }
|
154
154
|
end
|
@@ -227,9 +227,9 @@ module DBF
|
|
227
227
|
private
|
228
228
|
|
229
229
|
def build_columns # nodoc
|
230
|
-
columns = []
|
231
230
|
@data.seek(DBF_HEADER_SIZE)
|
232
|
-
|
231
|
+
columns = []
|
232
|
+
until end_of_record?
|
233
233
|
column_data = @data.read(DBF_HEADER_SIZE)
|
234
234
|
name, type, length, decimal = column_data.unpack('a10 x a x4 C2')
|
235
235
|
columns << column_class.new(self, name, type, length, decimal)
|
@@ -238,10 +238,10 @@ module DBF
|
|
238
238
|
end
|
239
239
|
|
240
240
|
def end_of_record? # nodoc
|
241
|
-
|
241
|
+
original_pos = @data.pos
|
242
242
|
byte = @data.read(1)
|
243
|
-
@data.seek(
|
244
|
-
byte
|
243
|
+
@data.seek(original_pos)
|
244
|
+
byte.ord == 13
|
245
245
|
end
|
246
246
|
|
247
247
|
def foxpro? # nodoc
|
@@ -253,10 +253,12 @@ module DBF
|
|
253
253
|
end
|
254
254
|
|
255
255
|
def memo_class # nodoc
|
256
|
-
@memo_class ||=
|
257
|
-
|
258
|
-
|
259
|
-
|
256
|
+
@memo_class ||= begin
|
257
|
+
if foxpro?
|
258
|
+
Memo::Foxpro
|
259
|
+
else
|
260
|
+
version == '83' ? Memo::Dbase3 : Memo::Dbase4
|
261
|
+
end
|
260
262
|
end
|
261
263
|
end
|
262
264
|
|
@@ -304,9 +306,5 @@ module DBF
|
|
304
306
|
def seek_to_record(index) # nodoc
|
305
307
|
seek(index * header.record_length)
|
306
308
|
end
|
307
|
-
|
308
|
-
def csv_class # nodoc
|
309
|
-
@csv_class ||= CSV.const_defined?(:Reader) ? FCSV : CSV
|
310
|
-
end
|
311
309
|
end
|
312
310
|
end
|
data/lib/dbf/version.rb
CHANGED
data/spec/dbf/column_spec.rb
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'spec_helper'
|
4
4
|
|
5
5
|
describe DBF::Column::Dbase do
|
6
6
|
let(:table) { DBF::Table.new fixture_path('dbase_30.dbf')}
|
7
7
|
|
8
|
-
context
|
9
|
-
let(:column) { DBF::Column::Dbase.new table,
|
8
|
+
context 'when initialized' do
|
9
|
+
let(:column) { DBF::Column::Dbase.new table, 'ColumnName', 'N', 1, 0 }
|
10
10
|
|
11
|
-
it
|
12
|
-
expect(column.name).to eq
|
11
|
+
it 'sets :name accessor' do
|
12
|
+
expect(column.name).to eq 'ColumnName'
|
13
13
|
end
|
14
14
|
|
15
|
-
it
|
16
|
-
expect(column.type).to eq
|
15
|
+
it 'sets :type accessor' do
|
16
|
+
expect(column.type).to eq 'N'
|
17
17
|
end
|
18
18
|
|
19
|
-
it
|
19
|
+
it 'sets the #length accessor' do
|
20
20
|
expect(column.length).to eq 1
|
21
21
|
end
|
22
22
|
|
23
|
-
it
|
23
|
+
it 'sets the #decimal accessor' do
|
24
24
|
expect(column.decimal).to eq 0
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'accepts length of 0' do
|
28
|
-
column = DBF::Column::Dbase.new table,
|
28
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 0, 0
|
29
29
|
expect(column.length).to eq 0
|
30
30
|
end
|
31
31
|
|
32
32
|
describe 'with length less than 0' do
|
33
33
|
it 'raises DBF::Column::LengthError' do
|
34
|
-
expect { DBF::Column::Dbase.new table,
|
34
|
+
expect { DBF::Column::Dbase.new table, 'ColumnName', 'N', -1, 0 }.to raise_error(DBF::Column::LengthError)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
describe 'with empty column name' do
|
39
39
|
it 'raises DBF::Column::NameError' do
|
40
|
-
expect { DBF::Column::Dbase.new table, "\xFF\xFC",
|
40
|
+
expect { DBF::Column::Dbase.new table, "\xFF\xFC", 'N', 1, 0 }.to raise_error(DBF::Column::NameError)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -46,7 +46,7 @@ describe DBF::Column::Dbase do
|
|
46
46
|
context 'with type N (number)' do
|
47
47
|
context 'and 0 length' do
|
48
48
|
it 'returns nil' do
|
49
|
-
column = DBF::Column::Dbase.new table,
|
49
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 0, 0
|
50
50
|
expect(column.type_cast('')).to be_nil
|
51
51
|
end
|
52
52
|
end
|
@@ -54,14 +54,14 @@ describe DBF::Column::Dbase do
|
|
54
54
|
context 'and 0 decimals' do
|
55
55
|
it 'casts value to Fixnum' do
|
56
56
|
value = '135'
|
57
|
-
column = DBF::Column::Dbase.new table,
|
57
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 3, 0
|
58
58
|
expect(column.type_cast(value)).to be_a(Fixnum)
|
59
59
|
expect(column.type_cast(value)).to eq 135
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'supports negative Fixnum' do
|
63
63
|
value = '-135'
|
64
|
-
column = DBF::Column::Dbase.new table,
|
64
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 3, 0
|
65
65
|
expect(column.type_cast(value)).to be_a(Fixnum)
|
66
66
|
expect(column.type_cast(value)).to eq -135
|
67
67
|
end
|
@@ -70,14 +70,14 @@ describe DBF::Column::Dbase do
|
|
70
70
|
context 'and more than 0 decimals' do
|
71
71
|
it 'casts value to Float' do
|
72
72
|
value = '13.5'
|
73
|
-
column = DBF::Column::Dbase.new table,
|
73
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 2, 1
|
74
74
|
expect(column.type_cast(value)).to be_a(Float)
|
75
75
|
expect(column.type_cast(value)).to eq 13.5
|
76
76
|
end
|
77
77
|
|
78
78
|
it 'casts negative value to Float' do
|
79
79
|
value = '-13.5'
|
80
|
-
column = DBF::Column::Dbase.new table,
|
80
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 2, 1
|
81
81
|
expect(column.type_cast(value)).to be_a(Float)
|
82
82
|
expect(column.type_cast(value)).to eq -13.5
|
83
83
|
end
|
@@ -87,42 +87,42 @@ describe DBF::Column::Dbase do
|
|
87
87
|
context 'with type F (float)' do
|
88
88
|
context 'and 0 length' do
|
89
89
|
it 'returns nil' do
|
90
|
-
column = DBF::Column::Dbase.new table,
|
90
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'F', 0, 0
|
91
91
|
expect(column.type_cast('')).to be_nil
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
95
|
it 'casts value to Float' do
|
96
96
|
value = '135'
|
97
|
-
column = DBF::Column::Dbase.new table,
|
97
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'F', 3, 0
|
98
98
|
expect(column.type_cast(value)).to be_a(Float)
|
99
99
|
expect(column.type_cast(value)).to eq 135.0
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'casts negative value to Float' do
|
103
103
|
value = '-135'
|
104
|
-
column = DBF::Column::Dbase.new table,
|
104
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'F', 3, 0
|
105
105
|
expect(column.type_cast(value)).to be_a(Float)
|
106
106
|
expect(column.type_cast(value)).to eq -135.0
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
context
|
111
|
-
context
|
110
|
+
context 'with type B (binary)' do
|
111
|
+
context 'with Foxpro dbf' do
|
112
112
|
it 'casts to float' do
|
113
|
-
column = DBF::Column::Dbase.new table,
|
113
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'B', 1, 2
|
114
114
|
expect(column.type_cast("\xEC\x51\xB8\x1E\x85\x6B\x31\x40")).to be_a(Float)
|
115
115
|
expect(column.type_cast("\xEC\x51\xB8\x1E\x85\x6B\x31\x40")).to eq 17.42
|
116
116
|
end
|
117
117
|
|
118
118
|
it 'stores original precision' do
|
119
|
-
column = DBF::Column::Dbase.new table,
|
119
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'B', 1, 0
|
120
120
|
expect(column.type_cast("\xEC\x51\xB8\x1E\x85\x6B\x31\x40")).to be_a(Float)
|
121
121
|
expect(column.type_cast("\xEC\x51\xB8\x1E\x85\x6B\x31\x40")).to eq 17.42
|
122
122
|
end
|
123
123
|
|
124
124
|
it 'supports negative binary' do
|
125
|
-
column = DBF::Column::Dbase.new table,
|
125
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'B', 1, 2
|
126
126
|
expect(column.type_cast("\x00\x00\x00\x00\x00\xC0\x65\xC0")).to be_a(Float)
|
127
127
|
expect(column.type_cast("\x00\x00\x00\x00\x00\xC0\x65\xC0")).to eq -174.0
|
128
128
|
end
|
@@ -132,28 +132,28 @@ describe DBF::Column::Dbase do
|
|
132
132
|
context 'with type I (integer)' do
|
133
133
|
context 'and 0 length' do
|
134
134
|
it 'returns nil' do
|
135
|
-
column = DBF::Column::Dbase.new table,
|
135
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'I', 0, 0
|
136
136
|
expect(column.type_cast('')).to be_nil
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
|
-
it
|
140
|
+
it 'casts value to Fixnum' do
|
141
141
|
value = "\203\171\001\000"
|
142
|
-
column = DBF::Column::Dbase.new table,
|
142
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'I', 3, 0
|
143
143
|
expect(column.type_cast(value)).to be_a(Fixnum)
|
144
144
|
expect(column.type_cast(value)).to eq 96643
|
145
145
|
end
|
146
146
|
|
147
|
-
it
|
147
|
+
it 'supports negative Fixnum' do
|
148
148
|
value = "\x24\xE1\xFF\xFF"
|
149
|
-
column = DBF::Column::Dbase.new table,
|
149
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'I', 3, 0
|
150
150
|
expect(column.type_cast(value)).to be_a(Fixnum)
|
151
151
|
expect(column.type_cast(value)).to eq -7900
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
155
155
|
context 'with type L (logical/boolean)' do
|
156
|
-
let(:column) { DBF::Column::Dbase.new table,
|
156
|
+
let(:column) { DBF::Column::Dbase.new table, 'ColumnName', 'L', 1, 0 }
|
157
157
|
|
158
158
|
it "casts 'y' to true" do
|
159
159
|
expect(column.type_cast('y')).to be true
|
@@ -169,37 +169,37 @@ describe DBF::Column::Dbase do
|
|
169
169
|
|
170
170
|
context 'and 0 length' do
|
171
171
|
it 'returns nil' do
|
172
|
-
column = DBF::Column::Dbase.new table,
|
172
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'L', 0, 0
|
173
173
|
expect(column.type_cast('')).to be_nil
|
174
174
|
end
|
175
175
|
end
|
176
176
|
end
|
177
177
|
|
178
178
|
context 'with type T (datetime)' do
|
179
|
-
let(:column) { DBF::Column::Dbase.new table,
|
179
|
+
let(:column) { DBF::Column::Dbase.new table, 'ColumnName', 'T', 16, 0 }
|
180
180
|
|
181
181
|
context 'with valid datetime' do
|
182
|
-
it
|
182
|
+
it 'casts to DateTime' do
|
183
183
|
expect(column.type_cast("Nl%\000\300Z\252\003")).to be_a(DateTime)
|
184
|
-
expect(column.type_cast("Nl%\000\300Z\252\003")).to eq DateTime.parse(
|
184
|
+
expect(column.type_cast("Nl%\000\300Z\252\003")).to eq DateTime.parse('2002-10-10T17:04:56+00:00')
|
185
185
|
end
|
186
186
|
end
|
187
187
|
|
188
188
|
if ruby_supports_mathn?
|
189
189
|
context 'when requiring mathn' do
|
190
|
-
it
|
190
|
+
it 'casts to DateTime' do
|
191
191
|
with_mathn = lambda do
|
192
192
|
require 'mathn'
|
193
193
|
column.type_cast("Nl%\000\300Z\252\003")
|
194
194
|
end
|
195
195
|
expect(with_mathn.call).to be_a(DateTime)
|
196
|
-
expect(with_mathn.call).to eq DateTime.parse(
|
196
|
+
expect(with_mathn.call).to eq DateTime.parse('2002-10-10T17:04:56+00:00')
|
197
197
|
end
|
198
198
|
end
|
199
199
|
end
|
200
200
|
|
201
201
|
context 'with invalid datetime' do
|
202
|
-
it
|
202
|
+
it 'casts to nil' do
|
203
203
|
expect(column.type_cast("Nl%\000\000A\000\999")).to be_a(NilClass)
|
204
204
|
expect(column.type_cast("Nl%\000\000A\000\999")).to be_nil
|
205
205
|
end
|
@@ -207,53 +207,53 @@ describe DBF::Column::Dbase do
|
|
207
207
|
|
208
208
|
context 'and 0 length' do
|
209
209
|
it 'returns nil' do
|
210
|
-
column = DBF::Column::Dbase.new table,
|
210
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'T', 0, 0
|
211
211
|
expect(column.type_cast('')).to be_nil
|
212
212
|
end
|
213
213
|
end
|
214
214
|
end
|
215
215
|
|
216
216
|
context 'with type D (date)' do
|
217
|
-
let(:column) { DBF::Column::Dbase.new table,
|
217
|
+
let(:column) { DBF::Column::Dbase.new table, 'ColumnName', 'D', 8, 0 }
|
218
218
|
|
219
219
|
context 'with valid date' do
|
220
|
-
it
|
221
|
-
expect(column.type_cast(
|
222
|
-
expect(column.type_cast(
|
220
|
+
it 'casts to Date' do
|
221
|
+
expect(column.type_cast('20050712')).to be_a(Date)
|
222
|
+
expect(column.type_cast('20050712')).to eq Date.new(2005,7,12)
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
226
|
context 'with invalid date' do
|
227
|
-
it
|
228
|
-
expect(column.type_cast(
|
229
|
-
expect(column.type_cast(
|
227
|
+
it 'casts to nil' do
|
228
|
+
expect(column.type_cast('0')).to be_a(NilClass)
|
229
|
+
expect(column.type_cast('0')).to be_nil
|
230
230
|
end
|
231
231
|
end
|
232
232
|
|
233
233
|
context 'and 0 length' do
|
234
234
|
it 'returns nil' do
|
235
|
-
column = DBF::Column::Dbase.new table,
|
235
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'D', 0, 0
|
236
236
|
expect(column.type_cast('')).to be_nil
|
237
237
|
end
|
238
238
|
end
|
239
239
|
end
|
240
240
|
|
241
241
|
context 'with type M (memo)' do
|
242
|
-
it
|
243
|
-
column = DBF::Column::Dbase.new table,
|
242
|
+
it 'casts to string' do
|
243
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'M', 3, 0
|
244
244
|
expect(column.type_cast('abc')).to be_a(String)
|
245
245
|
expect(column.type_cast('abc')).to eq 'abc'
|
246
246
|
end
|
247
247
|
|
248
248
|
it 'casts nil to nil' do
|
249
|
-
column = DBF::Column::Dbase.new table,
|
249
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'M', 3, 0
|
250
250
|
expect(column.type_cast(nil)).to be_a(NilClass)
|
251
251
|
expect(column.type_cast(nil)).to be_nil
|
252
252
|
end
|
253
253
|
|
254
254
|
context 'and 0 length' do
|
255
255
|
it 'returns nil' do
|
256
|
-
column = DBF::Column::Dbase.new table,
|
256
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'M', 0, 0
|
257
257
|
expect(column.type_cast('')).to be_nil
|
258
258
|
end
|
259
259
|
end
|
@@ -261,7 +261,7 @@ describe DBF::Column::Dbase do
|
|
261
261
|
end
|
262
262
|
|
263
263
|
context 'with type Y (currency)' do
|
264
|
-
let(:column) { DBF::Column::Dbase.new table,
|
264
|
+
let(:column) { DBF::Column::Dbase.new table, 'ColumnName', 'Y', 8, 4 }
|
265
265
|
|
266
266
|
it 'casts to float' do
|
267
267
|
expect(column.type_cast(" \xBF\x02\x00\x00\x00\x00\x00")).to be_a(Float)
|
@@ -280,74 +280,74 @@ describe DBF::Column::Dbase do
|
|
280
280
|
|
281
281
|
context 'and 0 length' do
|
282
282
|
it 'returns nil' do
|
283
|
-
column = DBF::Column::Dbase.new table,
|
283
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'Y', 0, 0
|
284
284
|
expect(column.type_cast('')).to be_nil
|
285
285
|
end
|
286
286
|
end
|
287
287
|
end
|
288
288
|
|
289
|
-
context
|
289
|
+
context '#schema_definition' do
|
290
290
|
context 'with type N (number)' do
|
291
|
-
it
|
292
|
-
column = DBF::Column::Dbase.new table,
|
291
|
+
it 'outputs an integer column' do
|
292
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 1, 0
|
293
293
|
expect(column.schema_definition).to eq "\"column_name\", :integer\n"
|
294
294
|
end
|
295
295
|
end
|
296
296
|
|
297
|
-
context
|
298
|
-
context
|
299
|
-
it
|
300
|
-
column = DBF::Column::Dbase.new table,
|
297
|
+
context 'with type B (binary)' do
|
298
|
+
context 'with Foxpro dbf' do
|
299
|
+
it 'outputs a float column' do
|
300
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'B', 1, 2
|
301
301
|
expect(column.schema_definition).to eq "\"column_name\", :float\n"
|
302
302
|
end
|
303
303
|
end
|
304
304
|
end
|
305
305
|
|
306
|
-
it
|
307
|
-
column = DBF::Column::Dbase.new table,
|
306
|
+
it 'defines a float colmn if type is (N)umber with more than 0 decimals' do
|
307
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'N', 1, 2
|
308
308
|
expect(column.schema_definition).to eq "\"column_name\", :float\n"
|
309
309
|
end
|
310
310
|
|
311
|
-
it
|
312
|
-
column = DBF::Column::Dbase.new table,
|
311
|
+
it 'defines a date column if type is (D)ate' do
|
312
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'D', 8, 0
|
313
313
|
expect(column.schema_definition).to eq "\"column_name\", :date\n"
|
314
314
|
end
|
315
315
|
|
316
|
-
it
|
317
|
-
column = DBF::Column::Dbase.new table,
|
316
|
+
it 'defines a datetime column if type is (D)ate' do
|
317
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'T', 16, 0
|
318
318
|
expect(column.schema_definition).to eq "\"column_name\", :datetime\n"
|
319
319
|
end
|
320
320
|
|
321
|
-
it
|
322
|
-
column = DBF::Column::Dbase.new table,
|
321
|
+
it 'defines a boolean column if type is (L)ogical' do
|
322
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'L', 1, 0
|
323
323
|
expect(column.schema_definition).to eq "\"column_name\", :boolean\n"
|
324
324
|
end
|
325
325
|
|
326
|
-
it
|
327
|
-
column = DBF::Column::Dbase.new table,
|
326
|
+
it 'defines a text column if type is (M)emo' do
|
327
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'M', 1, 0
|
328
328
|
expect(column.schema_definition).to eq "\"column_name\", :text\n"
|
329
329
|
end
|
330
330
|
|
331
|
-
it
|
332
|
-
column = DBF::Column::Dbase.new table,
|
331
|
+
it 'defines a string column with length for any other data types' do
|
332
|
+
column = DBF::Column::Dbase.new table, 'ColumnName', 'X', 20, 0
|
333
333
|
expect(column.schema_definition).to eq "\"column_name\", :string, :limit => 20\n"
|
334
334
|
end
|
335
335
|
end
|
336
336
|
|
337
|
-
context
|
338
|
-
it
|
339
|
-
column = DBF::Column::Dbase.new table, "--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--",
|
340
|
-
expect(column.name).to eq
|
337
|
+
context '#name' do
|
338
|
+
it 'contains only ASCII characters' do
|
339
|
+
column = DBF::Column::Dbase.new table, "--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--", 'N', 1, 0
|
340
|
+
expect(column.name).to eq '---hello world---'
|
341
341
|
end
|
342
342
|
|
343
|
-
it
|
344
|
-
column = DBF::Column::Dbase.new table, "--\x1F-\x68\x65\x6C\x6C\x6F \x00 world-\x80--",
|
345
|
-
expect(column.name).to eq
|
343
|
+
it 'is truncated at the null character' do
|
344
|
+
column = DBF::Column::Dbase.new table, "--\x1F-\x68\x65\x6C\x6C\x6F \x00 world-\x80--", 'N', 1, 0
|
345
|
+
expect(column.name).to eq '---hello '
|
346
346
|
end
|
347
347
|
end
|
348
348
|
|
349
349
|
context '#decode_date' do
|
350
|
-
let(:column) { DBF::Column::Dbase.new table,
|
350
|
+
let(:column) { DBF::Column::Dbase.new table, 'ColumnName', 'N', 1, 0 }
|
351
351
|
|
352
352
|
it 'is nil if value is blank' do
|
353
353
|
expect(column.send(:decode_date, '')).to be_nil
|
data/spec/dbf/table_spec.rb
CHANGED
@@ -54,6 +54,33 @@ describe DBF::Table do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
describe '#json_schema' do
|
58
|
+
it 'is valid JSON' do
|
59
|
+
expect { JSON.parse(table.json_schema) }.to_not raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'matches the test fixture' do
|
63
|
+
data = JSON.parse(table.json_schema)
|
64
|
+
expect(data).to eq [
|
65
|
+
{'name' => 'ID', 'type' => 'N', 'length' => 19, 'decimal' => 0},
|
66
|
+
{'name' => 'CATCOUNT', 'type' => 'N', 'length' => 19, 'decimal' => 0},
|
67
|
+
{'name' => 'AGRPCOUNT', 'type' => 'N', 'length' => 19, 'decimal' => 0},
|
68
|
+
{'name' => 'PGRPCOUNT', 'type' => 'N', 'length' => 19, 'decimal' => 0},
|
69
|
+
{'name' => 'ORDER', 'type' => 'N', 'length' => 19, 'decimal' => 0},
|
70
|
+
{'name' => 'CODE', 'type' => 'C', 'length' => 50, 'decimal' => 0},
|
71
|
+
{'name' => 'NAME', 'type' => 'C', 'length' => 100, 'decimal' => 0},
|
72
|
+
{'name' => 'THUMBNAIL', 'type' => 'C', 'length' => 254, 'decimal' => 0},
|
73
|
+
{'name' => 'IMAGE', 'type' => 'C', 'length' => 254, 'decimal' => 0},
|
74
|
+
{'name' => 'PRICE', 'type' => 'N', 'length' => 13, 'decimal' => 2},
|
75
|
+
{'name' => 'COST', 'type' => 'N', 'length' => 13, 'decimal' => 2},
|
76
|
+
{'name' => 'DESC', 'type' => 'M', 'length' => 10, 'decimal' => 0},
|
77
|
+
{'name' => 'WEIGHT', 'type' => 'N', 'length' => 13, 'decimal' => 2},
|
78
|
+
{'name' => 'TAXABLE', 'type' => 'L', 'length' => 1, 'decimal' => 0},
|
79
|
+
{'name' => 'ACTIVE', 'type' => 'L', 'length' => 1, 'decimal' => 0}
|
80
|
+
]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
57
84
|
describe '#to_csv' do
|
58
85
|
after do
|
59
86
|
FileUtils.rm_f 'test.csv'
|
data/spec/spec_helper.rb
CHANGED
@@ -13,13 +13,6 @@ Encoding.default_external = "UTF-8" if defined?(Encoding)
|
|
13
13
|
|
14
14
|
DB_PATH = File.dirname(__FILE__) + '/fixtures' unless defined?(DB_PATH)
|
15
15
|
|
16
|
-
if RUBY_VERSION == "1.8.6"
|
17
|
-
# warn 'ruby-1.8.6: defining Array#reduce as alias of Array#inject'
|
18
|
-
class Array
|
19
|
-
alias_method :reduce, :inject
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
16
|
RSpec.configure do |config|
|
24
17
|
def ruby_supports_mathn?
|
25
18
|
begin
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Morrison
|
@@ -9,21 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2015-10-23 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: fastercsv
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.5'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.5'
|
12
|
+
dependencies: []
|
27
13
|
description: A small fast library for reading dBase, xBase, Clipper and FoxPro database
|
28
14
|
files.
|
29
15
|
email: keithm@infused.org
|
@@ -39,7 +25,6 @@ files:
|
|
39
25
|
- Gemfile
|
40
26
|
- Gemfile.lock
|
41
27
|
- Gemfile.travis
|
42
|
-
- Gemfile.travis18
|
43
28
|
- Guardfile
|
44
29
|
- LICENSE
|
45
30
|
- README.md
|