dbf 3.1.3 → 4.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +5 -1
- data/Gemfile.lock +33 -25
- data/Guardfile +1 -1
- data/LICENSE +1 -1
- data/README.md +9 -6
- data/bin/dbf +3 -3
- data/dbf.gemspec +2 -1
- data/docs/supported_types.markdown +16 -31
- data/lib/dbf.rb +3 -2
- data/lib/dbf/column.rb +3 -8
- data/lib/dbf/column_type.rb +12 -17
- data/lib/dbf/database/foxpro.rb +1 -3
- data/lib/dbf/memo/base.rb +1 -0
- data/lib/dbf/memo/dbase4.rb +1 -1
- data/lib/dbf/memo/foxpro.rb +2 -3
- data/lib/dbf/record.rb +1 -1
- data/lib/dbf/table.rb +23 -21
- data/lib/dbf/version.rb +1 -1
- data/spec/dbf/column_spec.rb +23 -23
- data/spec/dbf/{database_spec.rb → database/foxpro_spec.rb} +0 -0
- data/spec/dbf/file_formats_spec.rb +29 -31
- data/spec/dbf/record_spec.rb +7 -7
- data/spec/dbf/table_spec.rb +25 -23
- data/spec/fixtures/polygon.dbf +0 -0
- data/spec/spec_helper.rb +0 -2
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c2ff24f150ee6567de0532c773695f8b47c0db811660e0f5f3be3d53a7cc053
|
4
|
+
data.tar.gz: 7006275aff71326833452546657bb07520d53024b6846b0c982121a488cd8235
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2d4bffa8201a8fa07abf642e721523e7ed59bb2b3e9a0fae31fe5ce6a4a3078f1520eb09e350d979dd88a68801c7c52d3236c402f83a41da1e5e6c4bca39911
|
7
|
+
data.tar.gz: fddf6622338b52b4bb6006513a92d9fd44c8f1e864738fd597d7e9f3fc21bcd86707f5e68e76216bd4c6d3be7dad9a48e5e19d55a6728691f6d90d86ec6db25e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# 4.1.3
|
2
|
+
- Raise DBF::NoColumnsDefined error when attempting to read records if no columns are defined
|
3
|
+
|
4
|
+
# 4.1.1
|
5
|
+
- Add required_ruby_version to gemspec
|
6
|
+
|
7
|
+
# 4.1.0
|
8
|
+
- Return Time instead of DateTime
|
9
|
+
|
10
|
+
# 4.0.0
|
11
|
+
- Drop support for ruby-2.2 and earlier
|
12
|
+
|
13
|
+
# 3.1.3
|
14
|
+
- Ensure malformed dates return nil
|
15
|
+
|
1
16
|
# 3.1.2
|
2
17
|
- Fix incorrect columns list when StringIO and encoding set
|
3
18
|
|
data/Gemfile
CHANGED
@@ -4,9 +4,13 @@ source 'https://rubygems.org'
|
|
4
4
|
group :development, :test do
|
5
5
|
gem 'awesome_print'
|
6
6
|
gem 'byebug'
|
7
|
+
gem 'e2mmap'
|
7
8
|
gem 'guard'
|
8
9
|
gem 'guard-rspec'
|
10
|
+
gem 'irb'
|
9
11
|
gem 'rake'
|
10
|
-
gem 'rubocop'
|
11
12
|
gem 'rspec'
|
13
|
+
gem 'rubocop'
|
14
|
+
gem 'rubocop-performance'
|
15
|
+
gem 'rubocop-rspec'
|
12
16
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dbf (
|
4
|
+
dbf (4.1.3)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
ast (2.4.0)
|
10
10
|
awesome_print (1.8.0)
|
11
|
-
byebug (
|
11
|
+
byebug (11.0.1)
|
12
12
|
coderay (1.1.2)
|
13
13
|
diff-lcs (1.3)
|
14
|
-
|
14
|
+
e2mmap (0.1.0)
|
15
|
+
ffi (1.11.1)
|
15
16
|
formatador (0.2.5)
|
16
|
-
guard (2.
|
17
|
+
guard (2.15.0)
|
17
18
|
formatador (>= 0.2.4)
|
18
19
|
listen (>= 2.7, < 4.0)
|
19
20
|
lumberjack (>= 1.0.12, < 2.0)
|
@@ -27,55 +28,58 @@ GEM
|
|
27
28
|
guard (~> 2.1)
|
28
29
|
guard-compat (~> 1.1)
|
29
30
|
rspec (>= 2.99.0, < 4.0)
|
30
|
-
|
31
|
+
irb (1.0.0)
|
32
|
+
jaro_winkler (1.5.3)
|
31
33
|
listen (3.1.5)
|
32
34
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
33
35
|
rb-inotify (~> 0.9, >= 0.9.7)
|
34
36
|
ruby_dep (~> 1.2)
|
35
37
|
lumberjack (1.0.13)
|
36
|
-
method_source (0.9.
|
38
|
+
method_source (0.9.2)
|
37
39
|
nenv (0.3.0)
|
38
40
|
notiffany (0.1.1)
|
39
41
|
nenv (~> 0.1)
|
40
42
|
shellany (~> 0.0)
|
41
|
-
parallel (1.
|
42
|
-
parser (2.
|
43
|
+
parallel (1.17.0)
|
44
|
+
parser (2.6.3.0)
|
43
45
|
ast (~> 2.4.0)
|
44
|
-
|
45
|
-
pry (0.11.3)
|
46
|
+
pry (0.12.2)
|
46
47
|
coderay (~> 1.1.0)
|
47
48
|
method_source (~> 0.9.0)
|
48
49
|
rainbow (3.0.0)
|
49
|
-
rake (12.3.
|
50
|
+
rake (12.3.2)
|
50
51
|
rb-fsevent (0.10.3)
|
51
|
-
rb-inotify (0.
|
52
|
-
ffi (
|
52
|
+
rb-inotify (0.10.0)
|
53
|
+
ffi (~> 1.0)
|
53
54
|
rspec (3.8.0)
|
54
55
|
rspec-core (~> 3.8.0)
|
55
56
|
rspec-expectations (~> 3.8.0)
|
56
57
|
rspec-mocks (~> 3.8.0)
|
57
|
-
rspec-core (3.8.
|
58
|
+
rspec-core (3.8.2)
|
58
59
|
rspec-support (~> 3.8.0)
|
59
|
-
rspec-expectations (3.8.
|
60
|
+
rspec-expectations (3.8.4)
|
60
61
|
diff-lcs (>= 1.2.0, < 2.0)
|
61
62
|
rspec-support (~> 3.8.0)
|
62
|
-
rspec-mocks (3.8.
|
63
|
+
rspec-mocks (3.8.1)
|
63
64
|
diff-lcs (>= 1.2.0, < 2.0)
|
64
65
|
rspec-support (~> 3.8.0)
|
65
|
-
rspec-support (3.8.
|
66
|
-
rubocop (0.
|
66
|
+
rspec-support (3.8.2)
|
67
|
+
rubocop (0.73.0)
|
67
68
|
jaro_winkler (~> 1.5.1)
|
68
69
|
parallel (~> 1.10)
|
69
|
-
parser (>= 2.
|
70
|
-
powerpack (~> 0.1)
|
70
|
+
parser (>= 2.6)
|
71
71
|
rainbow (>= 2.2.2, < 4.0)
|
72
72
|
ruby-progressbar (~> 1.7)
|
73
|
-
unicode-display_width (
|
74
|
-
|
73
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
74
|
+
rubocop-performance (1.4.0)
|
75
|
+
rubocop (>= 0.71.0)
|
76
|
+
rubocop-rspec (1.33.0)
|
77
|
+
rubocop (>= 0.60.0)
|
78
|
+
ruby-progressbar (1.10.1)
|
75
79
|
ruby_dep (1.5.0)
|
76
80
|
shellany (0.0.1)
|
77
|
-
thor (0.20.
|
78
|
-
unicode-display_width (1.
|
81
|
+
thor (0.20.3)
|
82
|
+
unicode-display_width (1.6.0)
|
79
83
|
|
80
84
|
PLATFORMS
|
81
85
|
ruby
|
@@ -84,11 +88,15 @@ DEPENDENCIES
|
|
84
88
|
awesome_print
|
85
89
|
byebug
|
86
90
|
dbf!
|
91
|
+
e2mmap
|
87
92
|
guard
|
88
93
|
guard-rspec
|
94
|
+
irb
|
89
95
|
rake
|
90
96
|
rspec
|
91
97
|
rubocop
|
98
|
+
rubocop-performance
|
99
|
+
rubocop-rspec
|
92
100
|
|
93
101
|
BUNDLED WITH
|
94
|
-
1.
|
102
|
+
2.1.4
|
data/Guardfile
CHANGED
@@ -6,6 +6,6 @@ guard :rspec, cmd: 'bundle exec rspec' do
|
|
6
6
|
# watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
7
|
watch(%r{^lib/(.+)\.rb$}) { 'spec' }
|
8
8
|
watch('spec/spec_helper.rb') { 'spec' }
|
9
|
-
watch(/
|
9
|
+
watch(%r{spec/fixtures/(.+)}) { 'spec' }
|
10
10
|
watch('Guardfile') { 'spec' }
|
11
11
|
end
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -16,14 +16,17 @@ database files
|
|
16
16
|
subject line
|
17
17
|
* Change log: <https://github.com/infused/dbf/blob/master/CHANGELOG.md>
|
18
18
|
|
19
|
-
NOTE:
|
19
|
+
NOTE: Beginning with version 4 we have dropped support for Ruby 2.0, 2.1, 2.2, and 2.3. If you need support for these older Rubies,
|
20
|
+
please use 3.0.x (https://github.com/infused.org/dbf/tree/3_stable)
|
21
|
+
|
22
|
+
NOTE: Beginning with version 3 we have dropped support for Ruby 1.8 and 1.9. If you need support for older Rubies,
|
23
|
+
please use 2.0.x (https://github.com/infused/dbf/tree/2_stable)
|
20
24
|
|
21
25
|
## Compatibility
|
22
26
|
|
23
27
|
DBF is tested to work with the following versions of Ruby:
|
24
28
|
|
25
|
-
*
|
26
|
-
* JRuby 1.7.x
|
29
|
+
* Ruby 2.4.x, 2.5.x, 2.6.x, 2.7.x
|
27
30
|
|
28
31
|
## Installation
|
29
32
|
|
@@ -208,7 +211,7 @@ Sequel.migration do
|
|
208
211
|
end
|
209
212
|
```
|
210
213
|
|
211
|
-
If you have
|
214
|
+
If you have initialized the DBF::Table with raw data, you will need to set the
|
212
215
|
exported table name manually:
|
213
216
|
|
214
217
|
```ruby
|
@@ -226,7 +229,7 @@ A small command-line utility called dbf is installed along with the gem.
|
|
226
229
|
-s = print summary information
|
227
230
|
-a = create an ActiveRecord::Schema
|
228
231
|
-r = create a Sequel Migration
|
229
|
-
-c =
|
232
|
+
-c = export as CSV
|
230
233
|
|
231
234
|
Create an executable ActiveRecord schema:
|
232
235
|
|
@@ -272,7 +275,7 @@ for a full list of supported column types.
|
|
272
275
|
|
273
276
|
## License
|
274
277
|
|
275
|
-
Copyright (c) 2006-
|
278
|
+
Copyright (c) 2006-2020 Keith Morrison <<keithm@infused.org>>
|
276
279
|
|
277
280
|
Permission is hereby granted, free of charge, to any person
|
278
281
|
obtaining a copy of this software and associated documentation
|
data/bin/dbf
CHANGED
@@ -9,14 +9,14 @@ params = ARGV.getopts('h', 's', 'a', 'c', 'r', 'v')
|
|
9
9
|
if params['v']
|
10
10
|
puts "dbf version: #{DBF::VERSION}"
|
11
11
|
|
12
|
-
elsif params['h']
|
12
|
+
elsif params['h']
|
13
13
|
puts "usage: #{File.basename(__FILE__)} [-h|-s|-a|-c|-r] filename"
|
14
14
|
puts ' -h = print this message'
|
15
15
|
puts ' -v = print the DBF gem version'
|
16
16
|
puts ' -s = print summary information'
|
17
17
|
puts ' -a = create an ActiveRecord::Schema'
|
18
18
|
puts ' -r = create a Sequel migration'
|
19
|
-
puts ' -c =
|
19
|
+
puts ' -c = export as CSV'
|
20
20
|
else
|
21
21
|
|
22
22
|
filename = ARGV.shift
|
@@ -46,7 +46,7 @@ else
|
|
46
46
|
puts 'Name Type Length Decimal'
|
47
47
|
puts '-' * 78
|
48
48
|
table.columns.each do |f|
|
49
|
-
puts '%-16s %-10s %-10s %-10s'
|
49
|
+
puts format('%-16s %-10s %-10s %-10s', f.name, f.type, f.length, f.decimal)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
data/dbf.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
lib = File.expand_path('
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
2
|
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
3
3
|
require 'dbf/version'
|
4
4
|
|
@@ -19,4 +19,5 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.test_files = Dir.glob('spec/**/*_spec.rb')
|
20
20
|
s.require_paths = ['lib']
|
21
21
|
s.required_rubygems_version = Gem::Requirement.new('>= 1.3.0')
|
22
|
+
s.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
22
23
|
end
|
@@ -1,36 +1,21 @@
|
|
1
1
|
# DBF supported data types
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
20
|
-
| 7b | dBase IV with memo file | Y | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - |
|
21
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
22
|
-
| 83 | dBase III with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - |
|
23
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
24
|
-
| 87 | Visual Objects 1.x with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - |
|
25
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
26
|
-
| 8b | dBase IV with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
|
27
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
28
|
-
| 8e | dBase IV with SQL table | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
|
29
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
30
|
-
| f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
31
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
32
|
-
| fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
33
|
-
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
3
|
+
| Version | Description | C | N | L | D | M | F | B | G | P | Y | T | I | V | X | @ | O | + |
|
4
|
+
|---------|-----------------------------------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
5
|
+
| 02 | FoxBase | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
6
|
+
| 03 | dBase III without memo file | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
7
|
+
| 04 | dBase IV without memo file | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
8
|
+
| 05 | dBase V without memo file | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
9
|
+
| 07 | Visual Objects 1.x | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
10
|
+
| 30 | Visual FoxPro | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | - |
|
11
|
+
| 31 | Visual FoxPro with AutoIncrement | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
12
|
+
| 7b | dBase IV with memo file | Y | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - |
|
13
|
+
| 83 | dBase III with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - |
|
14
|
+
| 87 | Visual Objects 1.x with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - |
|
15
|
+
| 8b | dBase IV with memo file | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
|
16
|
+
| 8e | dBase IV with SQL table | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
|
17
|
+
| f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
18
|
+
| fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
34
19
|
|
35
20
|
Data type descriptions
|
36
21
|
|
data/lib/dbf.rb
CHANGED
data/lib/dbf/column.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module DBF
|
2
2
|
class Column
|
3
|
+
extend Forwardable
|
4
|
+
|
3
5
|
class LengthError < StandardError
|
4
6
|
end
|
5
7
|
|
@@ -7,6 +9,7 @@ module DBF
|
|
7
9
|
end
|
8
10
|
|
9
11
|
attr_reader :table, :name, :type, :length, :decimal
|
12
|
+
def_delegator :type_cast_class, :type_cast
|
10
13
|
|
11
14
|
TYPE_CAST_CLASS = {
|
12
15
|
N: ColumnType::Number,
|
@@ -56,14 +59,6 @@ module DBF
|
|
56
59
|
{name: name, type: type, length: length, decimal: decimal}
|
57
60
|
end
|
58
61
|
|
59
|
-
# Cast value to native type
|
60
|
-
#
|
61
|
-
# @param [String] value
|
62
|
-
# @return [Integer, Float, Date, DateTime, Boolean, String]
|
63
|
-
def type_cast(value)
|
64
|
-
type_cast_class.type_cast(value)
|
65
|
-
end
|
66
|
-
|
67
62
|
# Underscored name
|
68
63
|
#
|
69
64
|
# This is the column name converted to underscore format.
|
data/lib/dbf/column_type.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
module DBF
|
2
2
|
module ColumnType
|
3
3
|
class Base
|
4
|
-
ENCODING_ARGS = [
|
5
|
-
Encoding.default_external,
|
6
|
-
{undef: :replace, invalid: :replace}
|
7
|
-
].freeze
|
8
|
-
|
9
4
|
attr_reader :decimal, :encoding
|
10
5
|
|
11
6
|
def initialize(decimal, encoding)
|
@@ -15,7 +10,7 @@ module DBF
|
|
15
10
|
end
|
16
11
|
|
17
12
|
class Nil < Base
|
18
|
-
def type_cast(
|
13
|
+
def type_cast(_value)
|
19
14
|
nil
|
20
15
|
end
|
21
16
|
end
|
@@ -23,19 +18,20 @@ module DBF
|
|
23
18
|
class Number < Base
|
24
19
|
def type_cast(value)
|
25
20
|
return nil if value.strip.empty?
|
21
|
+
|
26
22
|
@decimal.zero? ? value.to_i : value.to_f
|
27
23
|
end
|
28
24
|
end
|
29
25
|
|
30
26
|
class Currency < Base
|
31
27
|
def type_cast(value)
|
32
|
-
(value.
|
28
|
+
(value.unpack1('q<') / 10_000.0).to_f
|
33
29
|
end
|
34
30
|
end
|
35
31
|
|
36
32
|
class SignedLong < Base
|
37
33
|
def type_cast(value)
|
38
|
-
value.
|
34
|
+
value.unpack1('l<')
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
@@ -47,20 +43,20 @@ module DBF
|
|
47
43
|
|
48
44
|
class Double < Base
|
49
45
|
def type_cast(value)
|
50
|
-
value.
|
46
|
+
value.unpack1('E')
|
51
47
|
end
|
52
48
|
end
|
53
49
|
|
54
50
|
class Boolean < Base
|
55
51
|
def type_cast(value)
|
56
|
-
value.strip
|
52
|
+
value.strip.match?(/^(y|t)$/i)
|
57
53
|
end
|
58
54
|
end
|
59
55
|
|
60
56
|
class Date < Base
|
61
57
|
def type_cast(value)
|
62
|
-
value
|
63
|
-
rescue
|
58
|
+
value.match?(/\d{8}/) && ::Date.strptime(value, '%Y%m%d')
|
59
|
+
rescue StandardError
|
64
60
|
nil
|
65
61
|
end
|
66
62
|
end
|
@@ -69,8 +65,8 @@ module DBF
|
|
69
65
|
def type_cast(value)
|
70
66
|
days, msecs = value.unpack('l2')
|
71
67
|
secs = (msecs / 1000).to_i
|
72
|
-
::DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60)
|
73
|
-
rescue
|
68
|
+
::DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60).to_time
|
69
|
+
rescue StandardError
|
74
70
|
nil
|
75
71
|
end
|
76
72
|
end
|
@@ -78,7 +74,7 @@ module DBF
|
|
78
74
|
class Memo < Base
|
79
75
|
def type_cast(value)
|
80
76
|
if encoding && !value.nil?
|
81
|
-
value.force_encoding(@encoding).encode(
|
77
|
+
value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace)
|
82
78
|
else
|
83
79
|
value
|
84
80
|
end
|
@@ -94,9 +90,8 @@ module DBF
|
|
94
90
|
class String < Base
|
95
91
|
def type_cast(value)
|
96
92
|
value = value.strip
|
97
|
-
@encoding ? value.force_encoding(@encoding).encode(
|
93
|
+
@encoding ? value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace) : value
|
98
94
|
end
|
99
95
|
end
|
100
|
-
|
101
96
|
end
|
102
97
|
end
|
data/lib/dbf/database/foxpro.rb
CHANGED
@@ -45,9 +45,7 @@ module DBF
|
|
45
45
|
glob = File.join(@dirname, "#{name}.dbf")
|
46
46
|
path = Dir.glob(glob, File::FNM_CASEFOLD).first
|
47
47
|
|
48
|
-
unless path && File.exist?(path)
|
49
|
-
raise DBF::FileNotFoundError, "related table not found: #{name}"
|
50
|
-
end
|
48
|
+
raise DBF::FileNotFoundError, "related table not found: #{name}" unless path && File.exist?(path)
|
51
49
|
|
52
50
|
path
|
53
51
|
end
|
data/lib/dbf/memo/base.rb
CHANGED
data/lib/dbf/memo/dbase4.rb
CHANGED
data/lib/dbf/memo/foxpro.rb
CHANGED
@@ -15,8 +15,7 @@ module DBF
|
|
15
15
|
memo_string = memo_string[0, memo_size]
|
16
16
|
end
|
17
17
|
memo_string
|
18
|
-
|
19
|
-
rescue
|
18
|
+
rescue StandardError
|
20
19
|
nil
|
21
20
|
end
|
22
21
|
|
@@ -25,7 +24,7 @@ module DBF
|
|
25
24
|
def block_size # :nodoc:
|
26
25
|
@block_size ||= begin
|
27
26
|
@data.rewind
|
28
|
-
@data.read(FPT_HEADER_SIZE).
|
27
|
+
@data.read(FPT_HEADER_SIZE).unpack1('x6n') || 0
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
data/lib/dbf/record.rb
CHANGED
data/lib/dbf/table.rb
CHANGED
@@ -2,9 +2,13 @@ module DBF
|
|
2
2
|
class FileNotFoundError < StandardError
|
3
3
|
end
|
4
4
|
|
5
|
+
class NoColumnsDefined < StandardError
|
6
|
+
end
|
7
|
+
|
5
8
|
# DBF::Table is the primary interface to a single DBF file and provides
|
6
9
|
# methods for enumerating and searching the records.
|
7
10
|
class Table
|
11
|
+
extend Forwardable
|
8
12
|
include Enumerable
|
9
13
|
include ::DBF::Schema
|
10
14
|
|
@@ -40,6 +44,11 @@ module DBF
|
|
40
44
|
attr_accessor :encoding
|
41
45
|
attr_writer :name
|
42
46
|
|
47
|
+
def_delegator :header, :header_length
|
48
|
+
def_delegator :header, :record_count
|
49
|
+
def_delegator :header, :record_length
|
50
|
+
def_delegator :header, :version
|
51
|
+
|
43
52
|
# Opens a DBF::Table
|
44
53
|
# Examples:
|
45
54
|
# # working with a file stored on the filesystem
|
@@ -104,12 +113,14 @@ module DBF
|
|
104
113
|
#
|
105
114
|
# @yield [nil, DBF::Record]
|
106
115
|
def each
|
107
|
-
|
116
|
+
record_count.times { |i| yield record(i) }
|
108
117
|
end
|
109
118
|
|
110
119
|
# @return [String]
|
111
120
|
def filename
|
112
|
-
|
121
|
+
return unless @data.respond_to?(:path)
|
122
|
+
|
123
|
+
File.basename(@data.path)
|
113
124
|
end
|
114
125
|
|
115
126
|
# Find records using a simple ActiveRecord-like syntax.
|
@@ -167,20 +178,17 @@ module DBF
|
|
167
178
|
# @param [Integer] index
|
168
179
|
# @return [DBF::Record, NilClass]
|
169
180
|
def record(index)
|
181
|
+
raise DBF::NoColumnsDefined, 'The DBF file has no columns defined' if columns.empty?
|
182
|
+
|
170
183
|
seek_to_record(index)
|
171
184
|
return nil if deleted_record?
|
172
|
-
|
185
|
+
|
186
|
+
record_data = @data.read(record_length)
|
187
|
+
DBF::Record.new(record_data, columns, version, @memo)
|
173
188
|
end
|
174
189
|
|
175
190
|
alias row record
|
176
191
|
|
177
|
-
# Total number of records
|
178
|
-
#
|
179
|
-
# @return [Integer]
|
180
|
-
def record_count
|
181
|
-
@record_count ||= header.record_count
|
182
|
-
end
|
183
|
-
|
184
192
|
# Dumps all records to a CSV file. If no filename is given then CSV is
|
185
193
|
# output to STDOUT.
|
186
194
|
#
|
@@ -192,13 +200,6 @@ module DBF
|
|
192
200
|
each { |record| csv << record.to_a }
|
193
201
|
end
|
194
202
|
|
195
|
-
# Internal dBase version number
|
196
|
-
#
|
197
|
-
# @return [String]
|
198
|
-
def version
|
199
|
-
@version ||= header.version
|
200
|
-
end
|
201
|
-
|
202
203
|
# Human readable version description
|
203
204
|
#
|
204
205
|
# @return [String]
|
@@ -222,7 +223,7 @@ module DBF
|
|
222
223
|
|
223
224
|
def deleted_record? # :nodoc:
|
224
225
|
flag = @data.read(1)
|
225
|
-
flag ? flag.
|
226
|
+
flag ? flag.unpack1('a') == '*' : true
|
226
227
|
end
|
227
228
|
|
228
229
|
def end_of_record? # :nodoc:
|
@@ -232,6 +233,7 @@ module DBF
|
|
232
233
|
def find_all(options) # :nodoc:
|
233
234
|
select do |record|
|
234
235
|
next unless record && record.match?(options)
|
236
|
+
|
235
237
|
yield record if block_given?
|
236
238
|
record
|
237
239
|
end
|
@@ -242,7 +244,7 @@ module DBF
|
|
242
244
|
end
|
243
245
|
|
244
246
|
def foxpro? # :nodoc:
|
245
|
-
FOXPRO_VERSIONS.
|
247
|
+
FOXPRO_VERSIONS.key?(version)
|
246
248
|
end
|
247
249
|
|
248
250
|
def header # :nodoc:
|
@@ -290,11 +292,11 @@ module DBF
|
|
290
292
|
end
|
291
293
|
|
292
294
|
def seek(offset) # :nodoc:
|
293
|
-
@data.seek(
|
295
|
+
@data.seek(header_length + offset)
|
294
296
|
end
|
295
297
|
|
296
298
|
def seek_to_record(index) # :nodoc:
|
297
|
-
seek(index *
|
299
|
+
seek(index * record_length)
|
298
300
|
end
|
299
301
|
end
|
300
302
|
end
|
data/lib/dbf/version.rb
CHANGED
data/spec/dbf/column_spec.rb
CHANGED
@@ -42,7 +42,7 @@ RSpec.describe DBF::Column do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
describe '#type_cast' do
|
46
46
|
context 'with type N (number)' do
|
47
47
|
context 'when value is empty' do
|
48
48
|
it 'returns nil' do
|
@@ -52,14 +52,14 @@ RSpec.describe DBF::Column do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
context '
|
55
|
+
context 'with 0 length' do
|
56
56
|
it 'returns nil' do
|
57
57
|
column = DBF::Column.new table, 'ColumnName', 'N', 0, 0
|
58
58
|
expect(column.type_cast('')).to be_nil
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
context '
|
62
|
+
context 'with 0 decimals' do
|
63
63
|
it 'casts value to Integer' do
|
64
64
|
value = '135'
|
65
65
|
column = DBF::Column.new table, 'ColumnName', 'N', 3, 0
|
@@ -69,11 +69,11 @@ RSpec.describe DBF::Column do
|
|
69
69
|
it 'supports negative Integer' do
|
70
70
|
value = '-135'
|
71
71
|
column = DBF::Column.new table, 'ColumnName', 'N', 3, 0
|
72
|
-
expect(column.type_cast(value)).to eq
|
72
|
+
expect(column.type_cast(value)).to eq(-135)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
context '
|
76
|
+
context 'with more than 0 decimals' do
|
77
77
|
it 'casts value to Float' do
|
78
78
|
value = '13.5'
|
79
79
|
column = DBF::Column.new table, 'ColumnName', 'N', 2, 1
|
@@ -83,13 +83,13 @@ RSpec.describe DBF::Column do
|
|
83
83
|
it 'casts negative value to Float' do
|
84
84
|
value = '-13.5'
|
85
85
|
column = DBF::Column.new table, 'ColumnName', 'N', 2, 1
|
86
|
-
expect(column.type_cast(value)).to eq
|
86
|
+
expect(column.type_cast(value)).to eq(-13.5)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
context 'with type F (float)' do
|
92
|
-
context '
|
92
|
+
context 'with 0 length' do
|
93
93
|
it 'returns nil' do
|
94
94
|
column = DBF::Column.new table, 'ColumnName', 'F', 0, 0
|
95
95
|
expect(column.type_cast('')).to be_nil
|
@@ -105,7 +105,7 @@ RSpec.describe DBF::Column do
|
|
105
105
|
it 'casts negative value to Float' do
|
106
106
|
value = '-135'
|
107
107
|
column = DBF::Column.new table, 'ColumnName', 'F', 3, 0
|
108
|
-
expect(column.type_cast(value)).to eq
|
108
|
+
expect(column.type_cast(value)).to eq(-135.0)
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
@@ -126,13 +126,13 @@ RSpec.describe DBF::Column do
|
|
126
126
|
it 'supports negative binary' do
|
127
127
|
column = DBF::Column.new table, 'ColumnName', 'B', 1, 2
|
128
128
|
expect(column.type_cast("\x00\x00\x00\x00\x00\xC0\x65\xC0")).to be_a(Float)
|
129
|
-
expect(column.type_cast("\x00\x00\x00\x00\x00\xC0\x65\xC0")).to eq
|
129
|
+
expect(column.type_cast("\x00\x00\x00\x00\x00\xC0\x65\xC0")).to eq(-174.0)
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
134
|
context 'with type I (integer)' do
|
135
|
-
context '
|
135
|
+
context 'with 0 length' do
|
136
136
|
it 'returns nil' do
|
137
137
|
column = DBF::Column.new table, 'ColumnName', 'I', 0, 0
|
138
138
|
expect(column.type_cast('')).to be_nil
|
@@ -142,13 +142,13 @@ RSpec.describe DBF::Column do
|
|
142
142
|
it 'casts value to Integer' do
|
143
143
|
value = "\203\171\001\000"
|
144
144
|
column = DBF::Column.new table, 'ColumnName', 'I', 3, 0
|
145
|
-
expect(column.type_cast(value)).to eq
|
145
|
+
expect(column.type_cast(value)).to eq 96_643
|
146
146
|
end
|
147
147
|
|
148
148
|
it 'supports negative Integer' do
|
149
149
|
value = "\x24\xE1\xFF\xFF"
|
150
150
|
column = DBF::Column.new table, 'ColumnName', 'I', 3, 0
|
151
|
-
expect(column.type_cast(value)).to eq
|
151
|
+
expect(column.type_cast(value)).to eq(-7900)
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
@@ -167,7 +167,7 @@ RSpec.describe DBF::Column do
|
|
167
167
|
expect(column.type_cast('n')).to be false
|
168
168
|
end
|
169
169
|
|
170
|
-
context '
|
170
|
+
context 'with 0 length' do
|
171
171
|
it 'returns nil' do
|
172
172
|
column = DBF::Column.new table, 'ColumnName', 'L', 0, 0
|
173
173
|
expect(column.type_cast('')).to be_nil
|
@@ -180,7 +180,7 @@ RSpec.describe DBF::Column do
|
|
180
180
|
|
181
181
|
context 'with valid datetime' do
|
182
182
|
it 'casts to DateTime' do
|
183
|
-
expect(column.type_cast("Nl%\000\300Z\252\003")).to eq
|
183
|
+
expect(column.type_cast("Nl%\000\300Z\252\003")).to eq Time.parse('2002-10-10T17:04:56+00:00')
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
@@ -190,7 +190,7 @@ RSpec.describe DBF::Column do
|
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
193
|
-
context '
|
193
|
+
context 'with 0 length' do
|
194
194
|
it 'returns nil' do
|
195
195
|
column = DBF::Column.new table, 'ColumnName', 'T', 0, 0
|
196
196
|
expect(column.type_cast('')).to be_nil
|
@@ -203,7 +203,7 @@ RSpec.describe DBF::Column do
|
|
203
203
|
|
204
204
|
context 'with valid date' do
|
205
205
|
it 'casts to Date' do
|
206
|
-
expect(column.type_cast('20050712')).to eq Date.new(2005,7,12)
|
206
|
+
expect(column.type_cast('20050712')).to eq Date.new(2005, 7, 12)
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
@@ -213,7 +213,7 @@ RSpec.describe DBF::Column do
|
|
213
213
|
end
|
214
214
|
end
|
215
215
|
|
216
|
-
context '
|
216
|
+
context 'with 0 length' do
|
217
217
|
it 'returns nil' do
|
218
218
|
column = DBF::Column.new table, 'ColumnName', 'D', 0, 0
|
219
219
|
expect(column.type_cast('')).to be_nil
|
@@ -232,7 +232,7 @@ RSpec.describe DBF::Column do
|
|
232
232
|
expect(column.type_cast(nil)).to be_nil
|
233
233
|
end
|
234
234
|
|
235
|
-
context '
|
235
|
+
context 'with 0 length' do
|
236
236
|
it 'returns nil' do
|
237
237
|
column = DBF::Column.new table, 'ColumnName', 'M', 0, 0
|
238
238
|
expect(column.type_cast('')).to be_nil
|
@@ -252,7 +252,7 @@ RSpec.describe DBF::Column do
|
|
252
252
|
expect(column.type_cast(nil)).to be_nil
|
253
253
|
end
|
254
254
|
|
255
|
-
context '
|
255
|
+
context 'with 0 length' do
|
256
256
|
it 'returns nil' do
|
257
257
|
column = DBF::Column.new table, 'ColumnName', 'G', 0, 0
|
258
258
|
expect(column.type_cast('')).to be_nil
|
@@ -269,14 +269,14 @@ RSpec.describe DBF::Column do
|
|
269
269
|
end
|
270
270
|
|
271
271
|
it 'supports negative currency' do
|
272
|
-
expect(column.type_cast("\xFC\xF0\xF0\xFE\xFF\xFF\xFF\xFF")).to eq
|
272
|
+
expect(column.type_cast("\xFC\xF0\xF0\xFE\xFF\xFF\xFF\xFF")).to eq(-1776.41)
|
273
273
|
end
|
274
274
|
|
275
275
|
it 'supports 64bit negative currency' do
|
276
|
-
expect(column.type_cast("pN'9\xFF\xFF\xFF\xFF")).to eq
|
276
|
+
expect(column.type_cast("pN'9\xFF\xFF\xFF\xFF")).to eq(-333_609.0)
|
277
277
|
end
|
278
278
|
|
279
|
-
context '
|
279
|
+
context 'with 0 length' do
|
280
280
|
it 'returns nil' do
|
281
281
|
column = DBF::Column.new table, 'ColumnName', 'Y', 0, 0
|
282
282
|
expect(column.type_cast('')).to be_nil
|
@@ -284,7 +284,7 @@ RSpec.describe DBF::Column do
|
|
284
284
|
end
|
285
285
|
end
|
286
286
|
|
287
|
-
|
287
|
+
describe '#name' do
|
288
288
|
it 'contains only ASCII characters' do
|
289
289
|
column = DBF::Column.new table, "--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--", 'N', 1, 0
|
290
290
|
expect(column.name).to eq '---hello world---'
|
File without changes
|
@@ -1,17 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.shared_examples_for 'DBF' do
|
4
|
-
|
5
|
-
|
6
|
-
sum_of_column_lengths = table.columns.inject(1) { |sum, column| sum += column.length }
|
4
|
+
let(:header_record_length) { table.instance_eval { header.record_length } }
|
5
|
+
let(:sum_of_column_lengths) { table.columns.inject(1) { |sum, column| sum + column.length } }
|
7
6
|
|
7
|
+
specify 'sum of column lengths should equal record length specified in header plus one' do
|
8
8
|
expect(header_record_length).to eq sum_of_column_lengths
|
9
9
|
end
|
10
10
|
|
11
11
|
specify 'records should be instances of DBF::Record' do
|
12
|
-
table.
|
13
|
-
expect(record).to be_kind_of(DBF::Record)
|
14
|
-
end
|
12
|
+
expect(table).to all be_kind_of(DBF::Record)
|
15
13
|
end
|
16
14
|
|
17
15
|
specify 'record count should be the same as reported in the header' do
|
@@ -20,7 +18,7 @@ RSpec.shared_examples_for 'DBF' do
|
|
20
18
|
|
21
19
|
specify 'column names should not be blank' do
|
22
20
|
table.columns.each do |column|
|
23
|
-
expect(column.name).
|
21
|
+
expect(column.name).to_not be_empty
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
@@ -53,17 +51,17 @@ end
|
|
53
51
|
RSpec.describe DBF, 'of type 03 (dBase III without memo file)' do
|
54
52
|
let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
|
55
53
|
|
56
|
-
|
54
|
+
it_behaves_like 'DBF'
|
57
55
|
|
58
|
-
it '
|
56
|
+
it 'reports the correct version number' do
|
59
57
|
expect(table.version).to eq '03'
|
60
58
|
end
|
61
59
|
|
62
|
-
it '
|
60
|
+
it 'reports the correct version description' do
|
63
61
|
expect(table.version_description).to eq 'dBase III without memo file'
|
64
62
|
end
|
65
63
|
|
66
|
-
it '
|
64
|
+
it 'determines the number of records' do
|
67
65
|
expect(table.record_count).to eq 14
|
68
66
|
end
|
69
67
|
end
|
@@ -71,17 +69,17 @@ end
|
|
71
69
|
RSpec.describe DBF, 'of type 30 (Visual FoxPro)' do
|
72
70
|
let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
|
73
71
|
|
74
|
-
|
72
|
+
it_behaves_like 'DBF'
|
75
73
|
|
76
|
-
it '
|
74
|
+
it 'reports the correct version number' do
|
77
75
|
expect(table.version).to eq '30'
|
78
76
|
end
|
79
77
|
|
80
|
-
it '
|
78
|
+
it 'reports the correct version description' do
|
81
79
|
expect(table.version_description).to eq 'Visual FoxPro'
|
82
80
|
end
|
83
81
|
|
84
|
-
it '
|
82
|
+
it 'determines the number of records' do
|
85
83
|
expect(table.record_count).to eq 34
|
86
84
|
end
|
87
85
|
|
@@ -93,17 +91,17 @@ end
|
|
93
91
|
RSpec.describe DBF, 'of type 31 (Visual FoxPro with AutoIncrement field)' do
|
94
92
|
let(:table) { DBF::Table.new fixture('dbase_31.dbf') }
|
95
93
|
|
96
|
-
|
94
|
+
it_behaves_like 'DBF'
|
97
95
|
|
98
|
-
it '
|
96
|
+
it 'has a dBase version of 31' do
|
99
97
|
expect(table.version).to eq '31'
|
100
98
|
end
|
101
99
|
|
102
|
-
it '
|
100
|
+
it 'reports the correct version description' do
|
103
101
|
expect(table.version_description).to eq 'Visual FoxPro with AutoIncrement field'
|
104
102
|
end
|
105
103
|
|
106
|
-
it '
|
104
|
+
it 'determines the number of records' do
|
107
105
|
expect(table.record_count).to eq 77
|
108
106
|
end
|
109
107
|
end
|
@@ -111,17 +109,17 @@ end
|
|
111
109
|
RSpec.describe DBF, 'of type 83 (dBase III with memo file)' do
|
112
110
|
let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
|
113
111
|
|
114
|
-
|
112
|
+
it_behaves_like 'DBF'
|
115
113
|
|
116
|
-
it '
|
114
|
+
it 'reports the correct version number' do
|
117
115
|
expect(table.version).to eq '83'
|
118
116
|
end
|
119
117
|
|
120
|
-
it '
|
118
|
+
it 'reports the correct version description' do
|
121
119
|
expect(table.version_description).to eq 'dBase III with memo file'
|
122
120
|
end
|
123
121
|
|
124
|
-
it '
|
122
|
+
it 'determines the number of records' do
|
125
123
|
expect(table.record_count).to eq 67
|
126
124
|
end
|
127
125
|
end
|
@@ -129,17 +127,17 @@ end
|
|
129
127
|
RSpec.describe DBF, 'of type 8b (dBase IV with memo file)' do
|
130
128
|
let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
|
131
129
|
|
132
|
-
|
130
|
+
it_behaves_like 'DBF'
|
133
131
|
|
134
|
-
it '
|
132
|
+
it 'reports the correct version number' do
|
135
133
|
expect(table.version).to eq '8b'
|
136
134
|
end
|
137
135
|
|
138
|
-
it '
|
136
|
+
it 'reports the correct version description' do
|
139
137
|
expect(table.version_description).to eq 'dBase IV with memo file'
|
140
138
|
end
|
141
139
|
|
142
|
-
it '
|
140
|
+
it 'determines the number of records' do
|
143
141
|
expect(table.record_count).to eq 10
|
144
142
|
end
|
145
143
|
end
|
@@ -147,17 +145,17 @@ end
|
|
147
145
|
RSpec.describe DBF, 'of type f5 (FoxPro with memo file)' do
|
148
146
|
let(:table) { DBF::Table.new fixture('dbase_f5.dbf') }
|
149
147
|
|
150
|
-
|
148
|
+
it_behaves_like 'DBF'
|
151
149
|
|
152
|
-
it '
|
150
|
+
it 'reports the correct version number' do
|
153
151
|
expect(table.version).to eq 'f5'
|
154
152
|
end
|
155
153
|
|
156
|
-
it '
|
154
|
+
it 'reports the correct version description' do
|
157
155
|
expect(table.version_description).to eq 'FoxPro with memo file'
|
158
156
|
end
|
159
157
|
|
160
|
-
it '
|
158
|
+
it 'determines the number of records' do
|
161
159
|
expect(table.record_count).to eq 975
|
162
160
|
end
|
163
161
|
|
data/spec/dbf/record_spec.rb
CHANGED
@@ -6,7 +6,7 @@ RSpec.describe DBF::Record do
|
|
6
6
|
let(:record_0) { YAML.load_file(fixture('dbase_83_record_0.yml')) }
|
7
7
|
let(:record_9) { YAML.load_file(fixture('dbase_83_record_9.yml')) }
|
8
8
|
|
9
|
-
it '
|
9
|
+
it 'returns an ordered array of attribute values' do
|
10
10
|
record = table.record(0)
|
11
11
|
expect(record.to_a).to eq record_0
|
12
12
|
|
@@ -44,13 +44,13 @@ RSpec.describe DBF::Record do
|
|
44
44
|
|
45
45
|
describe 'when other does not have attributes' do
|
46
46
|
it 'returns false' do
|
47
|
-
expect((record ==
|
47
|
+
expect((record == instance_double('DBF::Record'))).to be_falsey
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
describe 'if other attributes match' do
|
52
52
|
let(:attributes) { {x: 1, y: 2} }
|
53
|
-
let(:other) {
|
53
|
+
let(:other) { instance_double('DBF::Record', attributes: attributes) }
|
54
54
|
|
55
55
|
before do
|
56
56
|
allow(record).to receive(:attributes).and_return(attributes)
|
@@ -80,11 +80,11 @@ RSpec.describe DBF::Record do
|
|
80
80
|
let(:table) { DBF::Table.new fixture('cp1251.dbf') }
|
81
81
|
let(:record) { table.find(0) }
|
82
82
|
|
83
|
-
it '
|
83
|
+
it 'encodes to default system encoding' do
|
84
84
|
expect(record.name.encoding).to eq Encoding.default_external
|
85
85
|
|
86
86
|
# russian a
|
87
|
-
expect(record.name.encode('UTF-8').
|
87
|
+
expect(record.name.encode('UTF-8').unpack1('H4')).to eq 'd0b0'
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
@@ -92,11 +92,11 @@ RSpec.describe DBF::Record do
|
|
92
92
|
let(:table) { DBF::Table.new fixture('cp1251.dbf'), nil, 'cp866' }
|
93
93
|
let(:record) { table.find(0) }
|
94
94
|
|
95
|
-
it '
|
95
|
+
it 'transcodes from manually specified encoding to default system encoding' do
|
96
96
|
expect(record.name.encoding).to eq Encoding.default_external
|
97
97
|
|
98
98
|
# russian а encoded in cp1251 and read as if it was encoded in cp866
|
99
|
-
expect(record.name.encode('UTF-8').
|
99
|
+
expect(record.name.encode('UTF-8').unpack1('H4')).to eq 'd180'
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
data/spec/dbf/table_spec.rb
CHANGED
@@ -44,7 +44,7 @@ RSpec.describe DBF::Table do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
|
47
|
+
describe '#close' do
|
48
48
|
before { table.close }
|
49
49
|
|
50
50
|
it 'closes the io' do
|
@@ -83,12 +83,12 @@ RSpec.describe DBF::Table do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
describe '#sequel_schema' do
|
86
|
-
it '
|
86
|
+
it 'returns a valid Sequel migration by default' do
|
87
87
|
control_schema = File.read(fixture('dbase_83_schema_sq.txt'))
|
88
88
|
expect(table.sequel_schema).to eq control_schema
|
89
89
|
end
|
90
90
|
|
91
|
-
it '
|
91
|
+
it 'returns a limited Sequel migration when passed true' do
|
92
92
|
control_schema = File.read(fixture('dbase_83_schema_sq_lim.txt'))
|
93
93
|
expect(table.sequel_schema).to eq control_schema
|
94
94
|
end
|
@@ -129,13 +129,7 @@ RSpec.describe DBF::Table do
|
|
129
129
|
|
130
130
|
describe 'when no path param passed' do
|
131
131
|
it 'writes to STDOUT' do
|
132
|
-
|
133
|
-
$stdout = StringIO.new
|
134
|
-
table.to_csv
|
135
|
-
expect($stdout.string).not_to be_empty
|
136
|
-
ensure
|
137
|
-
$stdout = STDOUT
|
138
|
-
end
|
132
|
+
expect { table.to_csv }.to output.to_stdout
|
139
133
|
end
|
140
134
|
end
|
141
135
|
|
@@ -143,7 +137,7 @@ RSpec.describe DBF::Table do
|
|
143
137
|
before { table.to_csv('test.csv') }
|
144
138
|
|
145
139
|
it 'creates a custom csv file' do
|
146
|
-
expect(File.
|
140
|
+
expect(File).to be_exist('test.csv')
|
147
141
|
end
|
148
142
|
end
|
149
143
|
end
|
@@ -153,10 +147,18 @@ RSpec.describe DBF::Table do
|
|
153
147
|
allow(table).to receive(:deleted_record?).and_return(true)
|
154
148
|
expect(table.record(5)).to be_nil
|
155
149
|
end
|
150
|
+
|
151
|
+
describe 'when dbf has no column definitions' do
|
152
|
+
let(:dbf_path) { fixture('polygon.dbf') }
|
153
|
+
|
154
|
+
it 'raises a DBF::NoColumnsDefined error' do
|
155
|
+
expect { DBF::Table.new(dbf_path).record(1) }.to raise_error(DBF::NoColumnsDefined, 'The DBF file has no columns defined')
|
156
|
+
end
|
157
|
+
end
|
156
158
|
end
|
157
159
|
|
158
160
|
describe '#current_record' do
|
159
|
-
it '
|
161
|
+
it 'returns nil for deleted records' do
|
160
162
|
allow(table).to receive(:deleted_record?).and_return(true)
|
161
163
|
expect(table.record(0)).to be_nil
|
162
164
|
end
|
@@ -201,24 +203,24 @@ RSpec.describe DBF::Table do
|
|
201
203
|
expect(table.find(:all, 'WEIGHT' => 0.0)).to eq table.select { |r| r['weight'] == 0.0 }
|
202
204
|
end
|
203
205
|
|
204
|
-
it '
|
206
|
+
it 'ANDS multiple search terms' do
|
205
207
|
expect(table.find(:all, 'ID' => 30, :IMAGE => 'graphics/00000001/TBC01.jpg')).to be_empty
|
206
208
|
end
|
207
209
|
|
208
|
-
it '
|
209
|
-
expect(table.find(:all, 'WEIGHT' => 0.0)).
|
210
|
+
it 'matches original column names' do
|
211
|
+
expect(table.find(:all, 'WEIGHT' => 0.0)).to_not be_empty
|
210
212
|
end
|
211
213
|
|
212
214
|
it 'matches symbolized column names' do
|
213
|
-
expect(table.find(:all, :
|
215
|
+
expect(table.find(:all, WEIGHT: 0.0)).to_not be_empty
|
214
216
|
end
|
215
217
|
|
216
218
|
it 'matches downcased column names' do
|
217
|
-
expect(table.find(:all, 'weight' => 0.0)).
|
219
|
+
expect(table.find(:all, 'weight' => 0.0)).to_not be_empty
|
218
220
|
end
|
219
221
|
|
220
222
|
it 'matches symbolized downcased column names' do
|
221
|
-
expect(table.find(:all, :
|
223
|
+
expect(table.find(:all, weight: 0.0)).to_not be_empty
|
222
224
|
end
|
223
225
|
end
|
224
226
|
|
@@ -276,13 +278,13 @@ RSpec.describe DBF::Table do
|
|
276
278
|
let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
|
277
279
|
|
278
280
|
it 'is false' do
|
279
|
-
expect(table
|
281
|
+
expect(table).to_not have_memo_file
|
280
282
|
end
|
281
283
|
end
|
282
284
|
|
283
285
|
describe 'with a memo file' do
|
284
286
|
it 'is true' do
|
285
|
-
expect(table
|
287
|
+
expect(table).to have_memo_file
|
286
288
|
end
|
287
289
|
end
|
288
290
|
end
|
@@ -293,7 +295,7 @@ RSpec.describe DBF::Table do
|
|
293
295
|
it 'is an array of Columns' do
|
294
296
|
expect(columns).to be_an(Array)
|
295
297
|
expect(columns).to_not be_empty
|
296
|
-
expect(columns.
|
298
|
+
expect(columns).to be_all { |c| c.is_a? DBF::Column }
|
297
299
|
end
|
298
300
|
end
|
299
301
|
|
@@ -318,7 +320,7 @@ RSpec.describe DBF::Table do
|
|
318
320
|
end
|
319
321
|
end
|
320
322
|
|
321
|
-
|
323
|
+
describe '#activerecord_schema_definition' do
|
322
324
|
context 'with type N (number)' do
|
323
325
|
it 'outputs an integer column' do
|
324
326
|
column = DBF::Column.new table, 'ColumnName', 'N', 1, 0
|
@@ -326,7 +328,7 @@ RSpec.describe DBF::Table do
|
|
326
328
|
end
|
327
329
|
end
|
328
330
|
|
329
|
-
|
331
|
+
describe 'with type B (binary)' do
|
330
332
|
context 'with Foxpro dbf' do
|
331
333
|
it 'outputs a float column' do
|
332
334
|
column = DBF::Column.new table, 'ColumnName', 'B', 1, 2
|
Binary file
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Morrison
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A small fast library for reading dBase, xBase, Clipper and FoxPro database
|
14
14
|
files.
|
@@ -48,7 +48,7 @@ files:
|
|
48
48
|
- lib/dbf/table.rb
|
49
49
|
- lib/dbf/version.rb
|
50
50
|
- spec/dbf/column_spec.rb
|
51
|
-
- spec/dbf/
|
51
|
+
- spec/dbf/database/foxpro_spec.rb
|
52
52
|
- spec/dbf/file_formats_spec.rb
|
53
53
|
- spec/dbf/record_spec.rb
|
54
54
|
- spec/dbf/table_spec.rb
|
@@ -90,12 +90,13 @@ files:
|
|
90
90
|
- spec/fixtures/foxprodb/setup.dbf
|
91
91
|
- spec/fixtures/foxprodb/types.CDX
|
92
92
|
- spec/fixtures/foxprodb/types.dbf
|
93
|
+
- spec/fixtures/polygon.dbf
|
93
94
|
- spec/spec_helper.rb
|
94
95
|
homepage: http://github.com/infused/dbf
|
95
96
|
licenses:
|
96
97
|
- MIT
|
97
98
|
metadata: {}
|
98
|
-
post_install_message:
|
99
|
+
post_install_message:
|
99
100
|
rdoc_options:
|
100
101
|
- "--charset=UTF-8"
|
101
102
|
require_paths:
|
@@ -104,21 +105,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
105
|
requirements:
|
105
106
|
- - ">="
|
106
107
|
- !ruby/object:Gem::Version
|
107
|
-
version:
|
108
|
+
version: 2.4.0
|
108
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
110
|
requirements:
|
110
111
|
- - ">="
|
111
112
|
- !ruby/object:Gem::Version
|
112
113
|
version: 1.3.0
|
113
114
|
requirements: []
|
114
|
-
|
115
|
-
|
116
|
-
signing_key:
|
115
|
+
rubygems_version: 3.1.2
|
116
|
+
signing_key:
|
117
117
|
specification_version: 4
|
118
118
|
summary: Read xBase files
|
119
119
|
test_files:
|
120
|
+
- spec/dbf/database/foxpro_spec.rb
|
120
121
|
- spec/dbf/file_formats_spec.rb
|
121
|
-
- spec/dbf/database_spec.rb
|
122
122
|
- spec/dbf/column_spec.rb
|
123
123
|
- spec/dbf/record_spec.rb
|
124
124
|
- spec/dbf/table_spec.rb
|