dbf 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +18 -26
- data/Gemfile.travis +1 -3
- data/README.md +11 -3
- data/Rakefile +1 -1
- data/docs/supported_types.markdown +5 -5
- data/lib/dbf.rb +2 -3
- data/lib/dbf/column.rb +161 -0
- data/lib/dbf/column_type.rb +85 -0
- data/lib/dbf/database/foxpro.rb +2 -2
- data/lib/dbf/header.rb +3 -4
- data/lib/dbf/record.rb +8 -9
- data/lib/dbf/table.rb +4 -27
- data/lib/dbf/version.rb +1 -1
- data/spec/dbf/column_spec.rb +68 -91
- data/spec/dbf/database_spec.rb +2 -2
- data/spec/dbf/file_formats_spec.rb +13 -22
- data/spec/dbf/record_spec.rb +21 -26
- data/spec/dbf/table_spec.rb +30 -20
- data/spec/spec_helper.rb +15 -11
- metadata +5 -6
- data/lib/dbf/column/base.rb +0 -199
- data/lib/dbf/column/dbase.rb +0 -7
- data/lib/dbf/column/foxpro.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5eb7b6698bb3117ab55add9db5b4b3cb768c82e8
|
4
|
+
data.tar.gz: bfbc755dbd887584c102fdc249583c859c14011c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84b5da89c2953f28a559d47271614afeb452f0fbd3d770881634a4731f86279f327a67095b6191fed3e7916d886a498b880f4d69a002264b9a5fee8f62d32e09
|
7
|
+
data.tar.gz: b67fa325a58fefd66b344bba83dc58718f4ecc07bdf5676e1f0ce2a698fce1907628069fb75a75d20ca6772988c6c21f1c242949f080786f3007f18d4762549b
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,17 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dbf (
|
5
|
-
fastercsv (~> 1.5)
|
4
|
+
dbf (3.0.0)
|
6
5
|
|
7
6
|
GEM
|
8
7
|
remote: https://rubygems.org/
|
9
8
|
specs:
|
10
9
|
coderay (1.1.0)
|
11
10
|
diff-lcs (1.2.5)
|
12
|
-
fastercsv (1.5.5)
|
13
11
|
ffi (1.9.10)
|
14
|
-
ffi (1.9.10-java)
|
15
12
|
formatador (0.2.5)
|
16
13
|
guard (2.13.0)
|
17
14
|
formatador (>= 0.2.4)
|
@@ -33,42 +30,34 @@ GEM
|
|
33
30
|
lumberjack (1.0.9)
|
34
31
|
method_source (0.8.2)
|
35
32
|
nenv (0.2.0)
|
36
|
-
notiffany (0.0.
|
33
|
+
notiffany (0.0.8)
|
37
34
|
nenv (~> 0.1)
|
38
35
|
shellany (~> 0.0)
|
39
|
-
pry (0.10.
|
36
|
+
pry (0.10.2)
|
40
37
|
coderay (~> 1.1.0)
|
41
38
|
method_source (~> 0.8.1)
|
42
39
|
slop (~> 3.4)
|
43
|
-
|
44
|
-
coderay (~> 1.1.0)
|
45
|
-
method_source (~> 0.8.1)
|
46
|
-
slop (~> 3.4)
|
47
|
-
spoon (~> 0.0)
|
48
|
-
rb-fsevent (0.9.5)
|
40
|
+
rb-fsevent (0.9.6)
|
49
41
|
rb-inotify (0.9.5)
|
50
42
|
ffi (>= 0.5.0)
|
51
|
-
rspec (3.
|
52
|
-
rspec-core (~> 3.
|
53
|
-
rspec-expectations (~> 3.
|
54
|
-
rspec-mocks (~> 3.
|
55
|
-
rspec-core (3.
|
56
|
-
rspec-support (~> 3.
|
57
|
-
rspec-expectations (3.
|
43
|
+
rspec (3.4.0)
|
44
|
+
rspec-core (~> 3.4.0)
|
45
|
+
rspec-expectations (~> 3.4.0)
|
46
|
+
rspec-mocks (~> 3.4.0)
|
47
|
+
rspec-core (3.4.1)
|
48
|
+
rspec-support (~> 3.4.0)
|
49
|
+
rspec-expectations (3.4.0)
|
58
50
|
diff-lcs (>= 1.2.0, < 2.0)
|
59
|
-
rspec-support (~> 3.
|
60
|
-
rspec-mocks (3.
|
51
|
+
rspec-support (~> 3.4.0)
|
52
|
+
rspec-mocks (3.4.0)
|
61
53
|
diff-lcs (>= 1.2.0, < 2.0)
|
62
|
-
rspec-support (~> 3.
|
63
|
-
rspec-support (3.
|
54
|
+
rspec-support (~> 3.4.0)
|
55
|
+
rspec-support (3.4.0)
|
64
56
|
shellany (0.0.1)
|
65
57
|
slop (3.6.0)
|
66
|
-
spoon (0.0.4)
|
67
|
-
ffi
|
68
58
|
thor (0.19.1)
|
69
59
|
|
70
60
|
PLATFORMS
|
71
|
-
java
|
72
61
|
ruby
|
73
62
|
|
74
63
|
DEPENDENCIES
|
@@ -76,3 +65,6 @@ DEPENDENCIES
|
|
76
65
|
guard
|
77
66
|
guard-rspec
|
78
67
|
rspec
|
68
|
+
|
69
|
+
BUNDLED WITH
|
70
|
+
1.10.6
|
data/Gemfile.travis
CHANGED
data/README.md
CHANGED
@@ -28,10 +28,18 @@ DBF is tested to work with the following versions of ruby:
|
|
28
28
|
|
29
29
|
## Installation
|
30
30
|
|
31
|
+
Install the gem manually:
|
32
|
+
|
31
33
|
```
|
32
34
|
gem install dbf
|
33
35
|
```
|
34
36
|
|
37
|
+
Or add to your Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'dbf'
|
41
|
+
```
|
42
|
+
|
35
43
|
## Basic Usage
|
36
44
|
|
37
45
|
Open a DBF file:
|
@@ -87,7 +95,7 @@ all records will be loaded into memory.
|
|
87
95
|
|
88
96
|
```ruby
|
89
97
|
# find all records with slot_number equal to s42
|
90
|
-
widgets.find(:all, :
|
98
|
+
widgets.find(:all, slot_number: 's42') do |widget|
|
91
99
|
# the record will be nil if deleted, but not yet pruned from the database
|
92
100
|
if widget
|
93
101
|
puts widget.serial_number
|
@@ -95,7 +103,7 @@ widgets.find(:all, :slot_number => 's42') do |widget|
|
|
95
103
|
end
|
96
104
|
|
97
105
|
# find the first record with slot_number equal to s42
|
98
|
-
widgets.find :first, :
|
106
|
+
widgets.find :first, slot_number: 's42'
|
99
107
|
|
100
108
|
# find record number 10
|
101
109
|
widgets.find(10)
|
@@ -149,7 +157,7 @@ class CreateBooks < ActiveRecord::Migration
|
|
149
157
|
|
150
158
|
Book.reset_column_information
|
151
159
|
table.each do |record|
|
152
|
-
Book.create(:
|
160
|
+
Book.create(title: record.title, author: record.author)
|
153
161
|
end
|
154
162
|
end
|
155
163
|
|
data/Rakefile
CHANGED
@@ -13,9 +13,9 @@
|
|
13
13
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
14
14
|
| 07 | Visual Objects 1.x | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
15
15
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
16
|
-
| 30 | Visual FoxPro | Y | Y | Y | Y | - | Y | Y |
|
16
|
+
| 30 | Visual FoxPro | Y | Y | Y | Y | - | Y | Y | Y | N | Y | N | Y | N | N | N | N | - |
|
17
17
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
18
|
-
| 31 | Visual FoxPro with AutoIncrement | Y | Y | Y | Y | Y | Y | Y |
|
18
|
+
| 31 | Visual FoxPro with AutoIncrement | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
19
19
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
20
20
|
| 7b | dBase IV with memo file | Y | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - |
|
21
21
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
@@ -27,9 +27,9 @@
|
|
27
27
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
28
28
|
| 8e | dBase IV with SQL table | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
|
29
29
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
30
|
-
| f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y |
|
30
|
+
| f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
31
31
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
32
|
-
| fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y |
|
32
|
+
| fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
|
33
33
|
+---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
34
34
|
|
35
35
|
Data type descriptions
|
@@ -50,4 +50,4 @@ Data type descriptions
|
|
50
50
|
* X = SQL compat
|
51
51
|
* @ = Timestamp
|
52
52
|
* O = Double
|
53
|
-
* + = Autoincrement
|
53
|
+
* + = Autoincrement
|
data/lib/dbf.rb
CHANGED
@@ -5,9 +5,8 @@ require 'json'
|
|
5
5
|
|
6
6
|
require 'dbf/schema'
|
7
7
|
require 'dbf/record'
|
8
|
-
require 'dbf/column
|
9
|
-
require 'dbf/
|
10
|
-
require 'dbf/column/foxpro'
|
8
|
+
require 'dbf/column'
|
9
|
+
require 'dbf/column_type'
|
11
10
|
require 'dbf/encodings'
|
12
11
|
require 'dbf/header'
|
13
12
|
require 'dbf/table'
|
data/lib/dbf/column.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
module DBF
|
2
|
+
class Column
|
3
|
+
# Raised if length is less than 1
|
4
|
+
class LengthError < StandardError; end
|
5
|
+
|
6
|
+
# Raised if name is empty
|
7
|
+
class NameError < StandardError; end
|
8
|
+
|
9
|
+
attr_reader :table, :name, :type, :length, :decimal
|
10
|
+
|
11
|
+
# Initialize a new DBF::Column
|
12
|
+
#
|
13
|
+
# @param [String] name
|
14
|
+
# @param [String] type
|
15
|
+
# @param [Fixnum] length
|
16
|
+
# @param [Fixnum] decimal
|
17
|
+
def initialize(table, name, type, length, decimal)
|
18
|
+
@table = table
|
19
|
+
@name = clean(name)
|
20
|
+
@type = type
|
21
|
+
@length = length
|
22
|
+
@decimal = decimal
|
23
|
+
@version = table.version
|
24
|
+
@encoding = table.encoding
|
25
|
+
|
26
|
+
validate_length
|
27
|
+
validate_name
|
28
|
+
end
|
29
|
+
|
30
|
+
# Cast value to native type
|
31
|
+
#
|
32
|
+
# @param [String] value
|
33
|
+
# @return [Fixnum, Float, Date, DateTime, Boolean, String]
|
34
|
+
def type_cast(value)
|
35
|
+
return nil if length == 0
|
36
|
+
|
37
|
+
klass = type_cast_class[type.to_sym]
|
38
|
+
cast_value = klass.new(value, decimal).type_cast
|
39
|
+
binary? ? cast_value : encode(cast_value)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns true if the column is a memo
|
43
|
+
#
|
44
|
+
# @return [Boolean]
|
45
|
+
def memo?
|
46
|
+
@memo ||= type == 'M'
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns true if the column contains binary data
|
50
|
+
#
|
51
|
+
# @return [Boolean]
|
52
|
+
def binary?
|
53
|
+
@binary ||= type == 'G'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Schema definition
|
57
|
+
#
|
58
|
+
# @return [String]
|
59
|
+
def schema_definition
|
60
|
+
"\"#{underscored_name}\", #{schema_data_type}\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Underscored name
|
64
|
+
#
|
65
|
+
# This is the column name converted to underscore format.
|
66
|
+
# For example, MyColumn will be returned as my_column.
|
67
|
+
#
|
68
|
+
# @return [String]
|
69
|
+
def underscored_name
|
70
|
+
@underscored_name ||= begin
|
71
|
+
un = name.dup
|
72
|
+
un.gsub!(/::/, '/')
|
73
|
+
un.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
74
|
+
un.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
75
|
+
un.tr!('-', '_')
|
76
|
+
un.downcase!
|
77
|
+
un
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_hash
|
82
|
+
{name: name, type: type, length: length, decimal: decimal}
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# TODO this should be a constant
|
88
|
+
def type_cast_class # nodoc
|
89
|
+
h = Hash.new(ColumnType::String)
|
90
|
+
h[:N] = ColumnType::Number
|
91
|
+
h[:I] = ColumnType::SignedLong
|
92
|
+
h[:F] = ColumnType::Float
|
93
|
+
h[:Y] = ColumnType::Currency
|
94
|
+
h[:D] = ColumnType::Date
|
95
|
+
h[:T] = ColumnType::DateTime
|
96
|
+
h[:L] = ColumnType::Boolean
|
97
|
+
h[:M] = ColumnType::Memo
|
98
|
+
h[:B] = ColumnType::Double
|
99
|
+
h[:G] = ColumnType::General
|
100
|
+
h
|
101
|
+
end
|
102
|
+
|
103
|
+
def encode(value, strip_output = false) # nodoc
|
104
|
+
return value if !value.respond_to?(:encoding)
|
105
|
+
|
106
|
+
output = @encoding ? encode_string(value) : value
|
107
|
+
strip_output ? output.strip : output
|
108
|
+
end
|
109
|
+
|
110
|
+
def encode_string(string)
|
111
|
+
string.force_encoding(@encoding).encode(*encoding_args)
|
112
|
+
end
|
113
|
+
|
114
|
+
def encoding_args # nodoc
|
115
|
+
@encoding_args ||= [
|
116
|
+
Encoding.default_external,
|
117
|
+
{undef: :replace, invalid: :replace}
|
118
|
+
]
|
119
|
+
end
|
120
|
+
|
121
|
+
def schema_data_type # nodoc
|
122
|
+
case type
|
123
|
+
when 'N', 'F'
|
124
|
+
decimal > 0 ? ':float' : ':integer'
|
125
|
+
when 'I'
|
126
|
+
':integer'
|
127
|
+
when 'Y'
|
128
|
+
':decimal, :precision => 15, :scale => 4'
|
129
|
+
when 'D'
|
130
|
+
':date'
|
131
|
+
when 'T'
|
132
|
+
':datetime'
|
133
|
+
when 'L'
|
134
|
+
':boolean'
|
135
|
+
when 'M'
|
136
|
+
':text'
|
137
|
+
when 'B'
|
138
|
+
if DBF::Table::FOXPRO_VERSIONS.keys.include?(@version)
|
139
|
+
':float'
|
140
|
+
else
|
141
|
+
':text'
|
142
|
+
end
|
143
|
+
else
|
144
|
+
":string, :limit => #{length}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def clean(value) # nodoc
|
149
|
+
truncated_value = value.strip.partition("\x00").first
|
150
|
+
truncated_value.gsub(/[^\x20-\x7E]/, '')
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_length
|
154
|
+
raise LengthError, 'field length must be 0 or greater' if length < 0
|
155
|
+
end
|
156
|
+
|
157
|
+
def validate_name
|
158
|
+
raise NameError, 'column name cannot be empty' if @name.empty?
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module DBF
|
2
|
+
module ColumnType
|
3
|
+
class Base
|
4
|
+
attr_reader :value, :decimal
|
5
|
+
|
6
|
+
def initialize(value, decimal)
|
7
|
+
@value = value
|
8
|
+
@decimal = decimal
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Number < Base
|
13
|
+
def type_cast
|
14
|
+
decimal.zero? ? value.to_i : value.to_f
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Currency < Base
|
19
|
+
def type_cast
|
20
|
+
(value.unpack('q<')[0] / 10_000.0).to_f
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class SignedLong < Base
|
25
|
+
def type_cast
|
26
|
+
value.unpack('l<')[0]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Float < Base
|
31
|
+
def type_cast
|
32
|
+
value.to_f
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Double < Base
|
37
|
+
def type_cast
|
38
|
+
value.unpack('E')[0]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Boolean < Base
|
43
|
+
def type_cast
|
44
|
+
value.strip =~ /^(y|t)$/i ? true : false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Date < Base
|
49
|
+
def type_cast
|
50
|
+
v = value.tr(' ', '0')
|
51
|
+
v !~ /\S/ ? nil : ::Date.parse(v)
|
52
|
+
rescue
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class DateTime < Base
|
58
|
+
def type_cast
|
59
|
+
days, msecs = value.unpack('l2')
|
60
|
+
secs = (msecs / 1000).to_i
|
61
|
+
::DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60)
|
62
|
+
rescue
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Memo < Base
|
68
|
+
def type_cast
|
69
|
+
value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class General < Base
|
74
|
+
def type_cast
|
75
|
+
value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class String < Base
|
80
|
+
def type_cast
|
81
|
+
value.strip
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|