dbf 4.1.3 → 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 +3 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +63 -52
- data/LICENSE +1 -1
- data/README.md +109 -16
- 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 +26 -0
- 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 +25 -11
- data/lib/dbf/version.rb +1 -1
- data/spec/dbf/file_formats_spec.rb +18 -0
- data/spec/fixtures/dbase_02.dbf +0 -0
- metadata +52 -6
- data/docs/supported_encodings.csv +0 -60
- data/docs/supported_types.markdown +0 -38
@@ -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
@@ -3,6 +3,8 @@ module DBF
|
|
3
3
|
class Base
|
4
4
|
attr_reader :decimal, :encoding
|
5
5
|
|
6
|
+
# @param decimal [Integer]
|
7
|
+
# @param encoding [String, Encoding]
|
6
8
|
def initialize(decimal, encoding)
|
7
9
|
@decimal = decimal
|
8
10
|
@encoding = encoding
|
@@ -10,12 +12,16 @@ module DBF
|
|
10
12
|
end
|
11
13
|
|
12
14
|
class Nil < Base
|
15
|
+
|
16
|
+
# @param _value [String]
|
13
17
|
def type_cast(_value)
|
14
18
|
nil
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
22
|
class Number < Base
|
23
|
+
|
24
|
+
# @param value [String]
|
19
25
|
def type_cast(value)
|
20
26
|
return nil if value.strip.empty?
|
21
27
|
|
@@ -24,36 +30,48 @@ module DBF
|
|
24
30
|
end
|
25
31
|
|
26
32
|
class Currency < Base
|
33
|
+
|
34
|
+
# @param value [String]
|
27
35
|
def type_cast(value)
|
28
36
|
(value.unpack1('q<') / 10_000.0).to_f
|
29
37
|
end
|
30
38
|
end
|
31
39
|
|
32
40
|
class SignedLong < Base
|
41
|
+
|
42
|
+
# @param value [String]
|
33
43
|
def type_cast(value)
|
34
44
|
value.unpack1('l<')
|
35
45
|
end
|
36
46
|
end
|
37
47
|
|
38
48
|
class Float < Base
|
49
|
+
|
50
|
+
# @param value [String]
|
39
51
|
def type_cast(value)
|
40
52
|
value.to_f
|
41
53
|
end
|
42
54
|
end
|
43
55
|
|
44
56
|
class Double < Base
|
57
|
+
|
58
|
+
# @param value [String]
|
45
59
|
def type_cast(value)
|
46
60
|
value.unpack1('E')
|
47
61
|
end
|
48
62
|
end
|
49
63
|
|
50
64
|
class Boolean < Base
|
65
|
+
|
66
|
+
# @param value [String]
|
51
67
|
def type_cast(value)
|
52
68
|
value.strip.match?(/^(y|t)$/i)
|
53
69
|
end
|
54
70
|
end
|
55
71
|
|
56
72
|
class Date < Base
|
73
|
+
|
74
|
+
# @param value [String]
|
57
75
|
def type_cast(value)
|
58
76
|
value.match?(/\d{8}/) && ::Date.strptime(value, '%Y%m%d')
|
59
77
|
rescue StandardError
|
@@ -62,6 +80,8 @@ module DBF
|
|
62
80
|
end
|
63
81
|
|
64
82
|
class DateTime < Base
|
83
|
+
|
84
|
+
# @param value [String]
|
65
85
|
def type_cast(value)
|
66
86
|
days, msecs = value.unpack('l2')
|
67
87
|
secs = (msecs / 1000).to_i
|
@@ -72,6 +92,8 @@ module DBF
|
|
72
92
|
end
|
73
93
|
|
74
94
|
class Memo < Base
|
95
|
+
|
96
|
+
# @param value [String]
|
75
97
|
def type_cast(value)
|
76
98
|
if encoding && !value.nil?
|
77
99
|
value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace)
|
@@ -82,12 +104,16 @@ module DBF
|
|
82
104
|
end
|
83
105
|
|
84
106
|
class General < Base
|
107
|
+
|
108
|
+
# @param value [String]
|
85
109
|
def type_cast(value)
|
86
110
|
value
|
87
111
|
end
|
88
112
|
end
|
89
113
|
|
90
114
|
class String < Base
|
115
|
+
|
116
|
+
# @param value [String]
|
91
117
|
def type_cast(value)
|
92
118
|
value = value.strip
|
93
119
|
@encoding ? value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace) : value
|
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
@@ -12,7 +12,8 @@ module DBF
|
|
12
12
|
include Enumerable
|
13
13
|
include ::DBF::Schema
|
14
14
|
|
15
|
-
|
15
|
+
DBASE2_HEADER_SIZE = 8
|
16
|
+
DBASE3_HEADER_SIZE = 32
|
16
17
|
|
17
18
|
VERSIONS = {
|
18
19
|
'02' => 'FoxBase',
|
@@ -67,9 +68,9 @@ module DBF
|
|
67
68
|
# table = DBF::Table.new 'data.dbf', nil, 'cp437'
|
68
69
|
# table = DBF::Table.new 'data.dbf', 'memo.dbt', Encoding::US_ASCII
|
69
70
|
#
|
70
|
-
# @param [String, StringIO] data Path to the dbf file or a StringIO object
|
71
|
-
# @param [optional String, StringIO] memo Path to the memo file or a StringIO object
|
72
|
-
# @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
|
73
74
|
def initialize(data, memo = nil, encoding = nil)
|
74
75
|
@data = open_data(data)
|
75
76
|
@encoding = encoding || header.encoding
|
@@ -98,7 +99,7 @@ module DBF
|
|
98
99
|
#
|
99
100
|
# @return [String]
|
100
101
|
def column_names
|
101
|
-
columns.map(&:name)
|
102
|
+
@column_names ||= columns.map(&:name)
|
102
103
|
end
|
103
104
|
|
104
105
|
# All columns
|
@@ -145,8 +146,8 @@ module DBF
|
|
145
146
|
# returned. The equivalent SQL would be "WHERE key1 = 'value1'
|
146
147
|
# AND key2 = 'value2'".
|
147
148
|
#
|
148
|
-
# @param [Integer, Symbol] command
|
149
|
-
# @param [optional, Hash] options Hash of search parameters
|
149
|
+
# @param command [Integer, Symbol] command
|
150
|
+
# @param options [optional, Hash] options Hash of search parameters
|
150
151
|
# @yield [optional, DBF::Record, NilClass]
|
151
152
|
def find(command, options = {}, &block)
|
152
153
|
case command
|
@@ -211,16 +212,29 @@ module DBF
|
|
211
212
|
|
212
213
|
def build_columns # :nodoc:
|
213
214
|
safe_seek do
|
214
|
-
@data.seek(
|
215
|
+
@data.seek(header_size)
|
215
216
|
[].tap do |columns|
|
216
217
|
until end_of_record?
|
217
|
-
|
218
|
-
|
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
|
219
226
|
end
|
220
227
|
end
|
221
228
|
end
|
222
229
|
end
|
223
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
|
+
|
224
238
|
def deleted_record? # :nodoc:
|
225
239
|
flag = @data.read(1)
|
226
240
|
flag ? flag.unpack1('a') == '*' : true
|
@@ -250,7 +264,7 @@ module DBF
|
|
250
264
|
def header # :nodoc:
|
251
265
|
@header ||= safe_seek do
|
252
266
|
@data.seek(0)
|
253
|
-
Header.new(@data.read(
|
267
|
+
Header.new(@data.read(DBASE3_HEADER_SIZE))
|
254
268
|
end
|
255
269
|
end
|
256
270
|
|
data/lib/dbf/version.rb
CHANGED
@@ -48,6 +48,24 @@ RSpec.shared_examples_for 'DBF' do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
RSpec.describe DBF, 'of type 02 (FoxBase)' do
|
52
|
+
let(:table) { DBF::Table.new fixture('dbase_02.dbf') }
|
53
|
+
|
54
|
+
it_behaves_like 'DBF'
|
55
|
+
|
56
|
+
it 'reports the correct version number' do
|
57
|
+
expect(table.version).to eq '02'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'reports the correct version description' do
|
61
|
+
expect(table.version_description).to eq 'FoxBase'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'determines the number of records' do
|
65
|
+
expect(table.record_count).to eq 9
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
51
69
|
RSpec.describe DBF, 'of type 03 (dBase III without memo file)' do
|
52
70
|
let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
|
53
71
|
|