dbf 4.0.1 → 4.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +68 -54
- data/Guardfile +1 -1
- data/LICENSE +1 -1
- data/README.md +115 -20
- data/bin/dbf +1 -1
- data/dbf.gemspec +1 -0
- data/docs/CNAME +1 -0
- data/docs/DBF.html +200 -0
- data/docs/DBF/Column.html +947 -0
- data/docs/DBF/Column/LengthError.html +124 -0
- data/docs/DBF/Column/NameError.html +124 -0
- data/docs/DBF/ColumnType.html +115 -0
- data/docs/DBF/ColumnType/Base.html +389 -0
- data/docs/DBF/ColumnType/Boolean.html +238 -0
- data/docs/DBF/ColumnType/Currency.html +238 -0
- data/docs/DBF/ColumnType/Date.html +242 -0
- data/docs/DBF/ColumnType/DateTime.html +246 -0
- data/docs/DBF/ColumnType/Double.html +238 -0
- data/docs/DBF/ColumnType/Float.html +238 -0
- data/docs/DBF/ColumnType/General.html +238 -0
- data/docs/DBF/ColumnType/Memo.html +246 -0
- data/docs/DBF/ColumnType/Nil.html +238 -0
- data/docs/DBF/ColumnType/Number.html +242 -0
- data/docs/DBF/ColumnType/SignedLong.html +238 -0
- data/docs/DBF/ColumnType/String.html +240 -0
- data/docs/DBF/Database.html +126 -0
- data/docs/DBF/Database/Foxpro.html +653 -0
- data/docs/DBF/Database/Table.html +346 -0
- data/docs/DBF/FileNotFoundError.html +124 -0
- data/docs/DBF/Header.html +723 -0
- data/docs/DBF/Memo.html +117 -0
- data/docs/DBF/Memo/Base.html +485 -0
- data/docs/DBF/Memo/Dbase3.html +242 -0
- data/docs/DBF/Memo/Dbase4.html +230 -0
- data/docs/DBF/Memo/Foxpro.html +268 -0
- data/docs/DBF/NoColumnsDefined.html +124 -0
- data/docs/DBF/Record.html +773 -0
- data/docs/DBF/Schema.html +980 -0
- data/docs/DBF/Table.html +1571 -0
- data/docs/_index.html +415 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +497 -0
- data/docs/file.README.html +359 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +359 -0
- data/docs/js/app.js +314 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +675 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/dbf/column.rb +5 -4
- data/lib/dbf/column_type.rb +29 -8
- data/lib/dbf/database/foxpro.rb +7 -2
- data/lib/dbf/header.rb +11 -3
- data/lib/dbf/record.rb +5 -5
- data/lib/dbf/schema.rb +4 -3
- data/lib/dbf/table.rb +32 -12
- data/lib/dbf/version.rb +1 -1
- data/spec/dbf/column_spec.rb +17 -17
- data/spec/dbf/{database_spec.rb → database/foxpro_spec.rb} +0 -0
- data/spec/dbf/file_formats_spec.rb +22 -6
- data/spec/dbf/record_spec.rb +2 -2
- data/spec/dbf/table_spec.rb +8 -0
- data/spec/fixtures/dbase_02.dbf +0 -0
- data/spec/fixtures/polygon.dbf +0 -0
- data/spec/spec_helper.rb +0 -2
- metadata +59 -12
- data/docs/supported_encodings.csv +0 -60
- data/docs/supported_types.markdown +0 -53
@@ -0,0 +1,110 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.26
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
16
|
+
|
17
|
+
<script type="text/javascript">
|
18
|
+
pathId = "";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
|
41
|
+
|
42
|
+
<span class="title">Top Level Namespace</span>
|
43
|
+
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div id="search">
|
47
|
+
|
48
|
+
<a class="full_list_link" id="class_list_link"
|
49
|
+
href="class_list.html">
|
50
|
+
|
51
|
+
<svg width="24" height="24">
|
52
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
54
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
55
|
+
</svg>
|
56
|
+
</a>
|
57
|
+
|
58
|
+
</div>
|
59
|
+
<div class="clear"></div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div id="content"><h1>Top Level Namespace
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
</h1>
|
67
|
+
<div class="box_info">
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<h2>Defined Under Namespace</h2>
|
82
|
+
<p class="children">
|
83
|
+
|
84
|
+
|
85
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="DBF.html" title="DBF (module)">DBF</a></span>
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
</p>
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<div id="footer">
|
103
|
+
Generated on Sun Aug 8 16:19:07 2021 by
|
104
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
|
+
0.9.26 (ruby-3.0.1).
|
106
|
+
</div>
|
107
|
+
|
108
|
+
</div>
|
109
|
+
</body>
|
110
|
+
</html>
|
data/lib/dbf/column.rb
CHANGED
@@ -28,10 +28,11 @@ module DBF
|
|
28
28
|
|
29
29
|
# Initialize a new DBF::Column
|
30
30
|
#
|
31
|
-
# @param [String]
|
32
|
-
# @param [String]
|
33
|
-
# @param [
|
34
|
-
# @param [Integer]
|
31
|
+
# @param table [String]
|
32
|
+
# @param name [String]
|
33
|
+
# @param type [String]
|
34
|
+
# @param length [Integer]
|
35
|
+
# @param decimal [Integer]
|
35
36
|
def initialize(table, name, type, length, decimal)
|
36
37
|
@table = table
|
37
38
|
@name = clean(name)
|
data/lib/dbf/column_type.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
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
|
|
6
|
+
# @param decimal [Integer]
|
7
|
+
# @param encoding [String, Encoding]
|
11
8
|
def initialize(decimal, encoding)
|
12
9
|
@decimal = decimal
|
13
10
|
@encoding = encoding
|
@@ -15,12 +12,16 @@ module DBF
|
|
15
12
|
end
|
16
13
|
|
17
14
|
class Nil < Base
|
15
|
+
|
16
|
+
# @param _value [String]
|
18
17
|
def type_cast(_value)
|
19
18
|
nil
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
23
22
|
class Number < Base
|
23
|
+
|
24
|
+
# @param value [String]
|
24
25
|
def type_cast(value)
|
25
26
|
return nil if value.strip.empty?
|
26
27
|
|
@@ -29,36 +30,48 @@ module DBF
|
|
29
30
|
end
|
30
31
|
|
31
32
|
class Currency < Base
|
33
|
+
|
34
|
+
# @param value [String]
|
32
35
|
def type_cast(value)
|
33
36
|
(value.unpack1('q<') / 10_000.0).to_f
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
37
40
|
class SignedLong < Base
|
41
|
+
|
42
|
+
# @param value [String]
|
38
43
|
def type_cast(value)
|
39
44
|
value.unpack1('l<')
|
40
45
|
end
|
41
46
|
end
|
42
47
|
|
43
48
|
class Float < Base
|
49
|
+
|
50
|
+
# @param value [String]
|
44
51
|
def type_cast(value)
|
45
52
|
value.to_f
|
46
53
|
end
|
47
54
|
end
|
48
55
|
|
49
56
|
class Double < Base
|
57
|
+
|
58
|
+
# @param value [String]
|
50
59
|
def type_cast(value)
|
51
60
|
value.unpack1('E')
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
55
64
|
class Boolean < Base
|
65
|
+
|
66
|
+
# @param value [String]
|
56
67
|
def type_cast(value)
|
57
|
-
value.strip.match?
|
68
|
+
value.strip.match?(/^(y|t)$/i)
|
58
69
|
end
|
59
70
|
end
|
60
71
|
|
61
72
|
class Date < Base
|
73
|
+
|
74
|
+
# @param value [String]
|
62
75
|
def type_cast(value)
|
63
76
|
value.match?(/\d{8}/) && ::Date.strptime(value, '%Y%m%d')
|
64
77
|
rescue StandardError
|
@@ -67,6 +80,8 @@ module DBF
|
|
67
80
|
end
|
68
81
|
|
69
82
|
class DateTime < Base
|
83
|
+
|
84
|
+
# @param value [String]
|
70
85
|
def type_cast(value)
|
71
86
|
days, msecs = value.unpack('l2')
|
72
87
|
secs = (msecs / 1000).to_i
|
@@ -77,9 +92,11 @@ module DBF
|
|
77
92
|
end
|
78
93
|
|
79
94
|
class Memo < Base
|
95
|
+
|
96
|
+
# @param value [String]
|
80
97
|
def type_cast(value)
|
81
98
|
if encoding && !value.nil?
|
82
|
-
value.force_encoding(@encoding).encode(
|
99
|
+
value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace)
|
83
100
|
else
|
84
101
|
value
|
85
102
|
end
|
@@ -87,15 +104,19 @@ module DBF
|
|
87
104
|
end
|
88
105
|
|
89
106
|
class General < Base
|
107
|
+
|
108
|
+
# @param value [String]
|
90
109
|
def type_cast(value)
|
91
110
|
value
|
92
111
|
end
|
93
112
|
end
|
94
113
|
|
95
114
|
class String < Base
|
115
|
+
|
116
|
+
# @param value [String]
|
96
117
|
def type_cast(value)
|
97
118
|
value = value.strip
|
98
|
-
@encoding ? value.force_encoding(@encoding).encode(
|
119
|
+
@encoding ? value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace) : value
|
99
120
|
end
|
100
121
|
end
|
101
122
|
end
|
data/lib/dbf/database/foxpro.rb
CHANGED
@@ -16,6 +16,8 @@ module DBF
|
|
16
16
|
#
|
17
17
|
# # Calling a table
|
18
18
|
# contacts = db.contacts.record(0)
|
19
|
+
#
|
20
|
+
# @param path [String]
|
19
21
|
def initialize(path)
|
20
22
|
@path = path
|
21
23
|
@dirname = File.dirname(@path)
|
@@ -30,7 +32,9 @@ module DBF
|
|
30
32
|
end
|
31
33
|
|
32
34
|
# Returns table with given name
|
33
|
-
#
|
35
|
+
#
|
36
|
+
# @param name [String]
|
37
|
+
# @return [DBF::Table]
|
34
38
|
def table(name)
|
35
39
|
Table.new table_path(name) do |table|
|
36
40
|
table.long_names = @tables[name]
|
@@ -40,7 +44,8 @@ module DBF
|
|
40
44
|
# Searches the database directory for the table's dbf file
|
41
45
|
# and returns the absolute path. Ensures case-insensitivity
|
42
46
|
# on any platform.
|
43
|
-
# @
|
47
|
+
# @param name [String]
|
48
|
+
# @return [String]
|
44
49
|
def table_path(name)
|
45
50
|
glob = File.join(@dirname, "#{name}.dbf")
|
46
51
|
path = Dir.glob(glob, File::FNM_CASEFOLD).first
|
data/lib/dbf/header.rb
CHANGED
@@ -9,12 +9,20 @@ module DBF
|
|
9
9
|
|
10
10
|
def initialize(data)
|
11
11
|
@data = data
|
12
|
-
|
13
|
-
@encoding = DBF::ENCODINGS[@encoding_key]
|
12
|
+
unpack_header
|
14
13
|
end
|
15
14
|
|
16
15
|
def unpack_header
|
17
|
-
@data.unpack('H2
|
16
|
+
@version = @data.unpack('H2').first
|
17
|
+
|
18
|
+
case @version
|
19
|
+
when '02'
|
20
|
+
@record_count, @record_length = @data.unpack('x v x3 v')
|
21
|
+
@header_length = 521
|
22
|
+
else
|
23
|
+
@record_count, @header_length, @record_length, @encoding_key = @data.unpack('x x3 V v2 x17 H2')
|
24
|
+
@encoding = DBF::ENCODINGS[@encoding_key]
|
25
|
+
end
|
18
26
|
end
|
19
27
|
end
|
20
28
|
end
|
data/lib/dbf/record.rb
CHANGED
@@ -3,10 +3,10 @@ module DBF
|
|
3
3
|
class Record
|
4
4
|
# Initialize a new DBF::Record
|
5
5
|
#
|
6
|
-
# @data [String, StringIO] data
|
7
|
-
# @columns [Column]
|
8
|
-
# @version [String]
|
9
|
-
# @memo [DBF::Memo]
|
6
|
+
# @param data [String, StringIO] data
|
7
|
+
# @param columns [Column]
|
8
|
+
# @param version [String]
|
9
|
+
# @param memo [DBF::Memo]
|
10
10
|
def initialize(data, columns, version, memo)
|
11
11
|
@data = StringIO.new(data)
|
12
12
|
@columns = columns
|
@@ -24,7 +24,7 @@ module DBF
|
|
24
24
|
|
25
25
|
# Reads attributes by column name
|
26
26
|
#
|
27
|
-
# @param [String, Symbol] key
|
27
|
+
# @param name [String, Symbol] key
|
28
28
|
def [](name)
|
29
29
|
key = name.to_s
|
30
30
|
if attributes.key?(key)
|
data/lib/dbf/schema.rb
CHANGED
@@ -32,7 +32,8 @@ module DBF
|
|
32
32
|
# t.column :notes, :text
|
33
33
|
# end
|
34
34
|
#
|
35
|
-
# @param [Symbol] format Valid options are :activerecord and :json
|
35
|
+
# @param format [Symbol] format Valid options are :activerecord and :json
|
36
|
+
# @param table_only [Boolean]
|
36
37
|
# @return [String]
|
37
38
|
def schema(format = :activerecord, table_only = false)
|
38
39
|
schema_method_name = schema_name(format)
|
@@ -75,7 +76,7 @@ module DBF
|
|
75
76
|
|
76
77
|
# ActiveRecord schema definition
|
77
78
|
#
|
78
|
-
# @param [DBF::Column]
|
79
|
+
# @param column [DBF::Column]
|
79
80
|
# @return [String]
|
80
81
|
def activerecord_schema_definition(column)
|
81
82
|
"\"#{column.underscored_name}\", #{schema_data_type(column, :activerecord)}\n"
|
@@ -83,7 +84,7 @@ module DBF
|
|
83
84
|
|
84
85
|
# Sequel schema definition
|
85
86
|
#
|
86
|
-
# @
|
87
|
+
# @param column [DBF::Column]
|
87
88
|
# @return [String]
|
88
89
|
def sequel_schema_definition(column)
|
89
90
|
":#{column.underscored_name}, #{schema_data_type(column, :sequel)}\n"
|
data/lib/dbf/table.rb
CHANGED
@@ -2,6 +2,9 @@ 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
|
@@ -9,7 +12,8 @@ module DBF
|
|
9
12
|
include Enumerable
|
10
13
|
include ::DBF::Schema
|
11
14
|
|
12
|
-
|
15
|
+
DBASE2_HEADER_SIZE = 8
|
16
|
+
DBASE3_HEADER_SIZE = 32
|
13
17
|
|
14
18
|
VERSIONS = {
|
15
19
|
'02' => 'FoxBase',
|
@@ -64,9 +68,9 @@ module DBF
|
|
64
68
|
# table = DBF::Table.new 'data.dbf', nil, 'cp437'
|
65
69
|
# table = DBF::Table.new 'data.dbf', 'memo.dbt', Encoding::US_ASCII
|
66
70
|
#
|
67
|
-
# @param [String, StringIO] data Path to the dbf file or a StringIO object
|
68
|
-
# @param [optional String, StringIO] memo Path to the memo file or a StringIO object
|
69
|
-
# @param [optional String, Encoding] encoding Name of the encoding or an Encoding object
|
71
|
+
# @param data [String, StringIO] data Path to the dbf file or a StringIO object
|
72
|
+
# @param memo [optional String, StringIO] memo Path to the memo file or a StringIO object
|
73
|
+
# @param encoding [optional String, Encoding] encoding Name of the encoding or an Encoding object
|
70
74
|
def initialize(data, memo = nil, encoding = nil)
|
71
75
|
@data = open_data(data)
|
72
76
|
@encoding = encoding || header.encoding
|
@@ -95,7 +99,7 @@ module DBF
|
|
95
99
|
#
|
96
100
|
# @return [String]
|
97
101
|
def column_names
|
98
|
-
columns.map(&:name)
|
102
|
+
@column_names ||= columns.map(&:name)
|
99
103
|
end
|
100
104
|
|
101
105
|
# All columns
|
@@ -142,8 +146,8 @@ module DBF
|
|
142
146
|
# returned. The equivalent SQL would be "WHERE key1 = 'value1'
|
143
147
|
# AND key2 = 'value2'".
|
144
148
|
#
|
145
|
-
# @param [Integer, Symbol] command
|
146
|
-
# @param [optional, Hash] options Hash of search parameters
|
149
|
+
# @param command [Integer, Symbol] command
|
150
|
+
# @param options [optional, Hash] options Hash of search parameters
|
147
151
|
# @yield [optional, DBF::Record, NilClass]
|
148
152
|
def find(command, options = {}, &block)
|
149
153
|
case command
|
@@ -175,10 +179,13 @@ module DBF
|
|
175
179
|
# @param [Integer] index
|
176
180
|
# @return [DBF::Record, NilClass]
|
177
181
|
def record(index)
|
182
|
+
raise DBF::NoColumnsDefined, 'The DBF file has no columns defined' if columns.empty?
|
183
|
+
|
178
184
|
seek_to_record(index)
|
179
185
|
return nil if deleted_record?
|
180
186
|
|
181
|
-
|
187
|
+
record_data = @data.read(record_length)
|
188
|
+
DBF::Record.new(record_data, columns, version, @memo)
|
182
189
|
end
|
183
190
|
|
184
191
|
alias row record
|
@@ -205,16 +212,29 @@ module DBF
|
|
205
212
|
|
206
213
|
def build_columns # :nodoc:
|
207
214
|
safe_seek do
|
208
|
-
@data.seek(
|
215
|
+
@data.seek(header_size)
|
209
216
|
[].tap do |columns|
|
210
217
|
until end_of_record?
|
211
|
-
|
212
|
-
|
218
|
+
case version
|
219
|
+
when '02'
|
220
|
+
column_data = @data.read(header_size * 2)
|
221
|
+
columns << Column.new(self, *column_data.unpack('A11 a C'), 0)
|
222
|
+
else
|
223
|
+
column_data = @data.read(header_size)
|
224
|
+
columns << Column.new(self, *column_data.unpack('A11 a x4 C2'))
|
225
|
+
end
|
213
226
|
end
|
214
227
|
end
|
215
228
|
end
|
216
229
|
end
|
217
230
|
|
231
|
+
def header_size
|
232
|
+
header_size = case version
|
233
|
+
when '02' then DBASE2_HEADER_SIZE
|
234
|
+
else DBASE3_HEADER_SIZE
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
218
238
|
def deleted_record? # :nodoc:
|
219
239
|
flag = @data.read(1)
|
220
240
|
flag ? flag.unpack1('a') == '*' : true
|
@@ -244,7 +264,7 @@ module DBF
|
|
244
264
|
def header # :nodoc:
|
245
265
|
@header ||= safe_seek do
|
246
266
|
@data.seek(0)
|
247
|
-
Header.new(@data.read(
|
267
|
+
Header.new(@data.read(DBASE3_HEADER_SIZE))
|
248
268
|
end
|
249
269
|
end
|
250
270
|
|