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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +68 -54
  5. data/Guardfile +1 -1
  6. data/LICENSE +1 -1
  7. data/README.md +115 -20
  8. data/bin/dbf +1 -1
  9. data/dbf.gemspec +1 -0
  10. data/docs/CNAME +1 -0
  11. data/docs/DBF.html +200 -0
  12. data/docs/DBF/Column.html +947 -0
  13. data/docs/DBF/Column/LengthError.html +124 -0
  14. data/docs/DBF/Column/NameError.html +124 -0
  15. data/docs/DBF/ColumnType.html +115 -0
  16. data/docs/DBF/ColumnType/Base.html +389 -0
  17. data/docs/DBF/ColumnType/Boolean.html +238 -0
  18. data/docs/DBF/ColumnType/Currency.html +238 -0
  19. data/docs/DBF/ColumnType/Date.html +242 -0
  20. data/docs/DBF/ColumnType/DateTime.html +246 -0
  21. data/docs/DBF/ColumnType/Double.html +238 -0
  22. data/docs/DBF/ColumnType/Float.html +238 -0
  23. data/docs/DBF/ColumnType/General.html +238 -0
  24. data/docs/DBF/ColumnType/Memo.html +246 -0
  25. data/docs/DBF/ColumnType/Nil.html +238 -0
  26. data/docs/DBF/ColumnType/Number.html +242 -0
  27. data/docs/DBF/ColumnType/SignedLong.html +238 -0
  28. data/docs/DBF/ColumnType/String.html +240 -0
  29. data/docs/DBF/Database.html +126 -0
  30. data/docs/DBF/Database/Foxpro.html +653 -0
  31. data/docs/DBF/Database/Table.html +346 -0
  32. data/docs/DBF/FileNotFoundError.html +124 -0
  33. data/docs/DBF/Header.html +723 -0
  34. data/docs/DBF/Memo.html +117 -0
  35. data/docs/DBF/Memo/Base.html +485 -0
  36. data/docs/DBF/Memo/Dbase3.html +242 -0
  37. data/docs/DBF/Memo/Dbase4.html +230 -0
  38. data/docs/DBF/Memo/Foxpro.html +268 -0
  39. data/docs/DBF/NoColumnsDefined.html +124 -0
  40. data/docs/DBF/Record.html +773 -0
  41. data/docs/DBF/Schema.html +980 -0
  42. data/docs/DBF/Table.html +1571 -0
  43. data/docs/_index.html +415 -0
  44. data/docs/class_list.html +51 -0
  45. data/docs/css/common.css +1 -0
  46. data/docs/css/full_list.css +58 -0
  47. data/docs/css/style.css +497 -0
  48. data/docs/file.README.html +359 -0
  49. data/docs/file_list.html +56 -0
  50. data/docs/frames.html +17 -0
  51. data/docs/index.html +359 -0
  52. data/docs/js/app.js +314 -0
  53. data/docs/js/full_list.js +216 -0
  54. data/docs/js/jquery.js +4 -0
  55. data/docs/method_list.html +675 -0
  56. data/docs/top-level-namespace.html +110 -0
  57. data/lib/dbf/column.rb +5 -4
  58. data/lib/dbf/column_type.rb +29 -8
  59. data/lib/dbf/database/foxpro.rb +7 -2
  60. data/lib/dbf/header.rb +11 -3
  61. data/lib/dbf/record.rb +5 -5
  62. data/lib/dbf/schema.rb +4 -3
  63. data/lib/dbf/table.rb +32 -12
  64. data/lib/dbf/version.rb +1 -1
  65. data/spec/dbf/column_spec.rb +17 -17
  66. data/spec/dbf/{database_spec.rb → database/foxpro_spec.rb} +0 -0
  67. data/spec/dbf/file_formats_spec.rb +22 -6
  68. data/spec/dbf/record_spec.rb +2 -2
  69. data/spec/dbf/table_spec.rb +8 -0
  70. data/spec/fixtures/dbase_02.dbf +0 -0
  71. data/spec/fixtures/polygon.dbf +0 -0
  72. data/spec/spec_helper.rb +0 -2
  73. metadata +59 -12
  74. data/docs/supported_encodings.csv +0 -60
  75. 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
+ &mdash; 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> &raquo;
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] name
32
- # @param [String] type
33
- # @param [Integer] length
34
- # @param [Integer] decimal
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)
@@ -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? /^(y|t)$/i
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(*ENCODING_ARGS)
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(*ENCODING_ARGS) : value
119
+ @encoding ? value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace) : value
99
120
  end
100
121
  end
101
122
  end
@@ -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
- # @return Table
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
- # @return String
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
- @version, @record_count, @header_length, @record_length, @encoding_key = unpack_header
13
- @encoding = DBF::ENCODINGS[@encoding_key]
12
+ unpack_header
14
13
  end
15
14
 
16
15
  def unpack_header
17
- @data.unpack('H2 x3 V v2 x17H2')
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
- # @params [DBF::Column]
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
- DBF_HEADER_SIZE = 32
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
- DBF::Record.new(@data.read(record_length), columns, version, @memo)
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(DBF_HEADER_SIZE)
215
+ @data.seek(header_size)
209
216
  [].tap do |columns|
210
217
  until end_of_record?
211
- column_data = @data.read(DBF_HEADER_SIZE)
212
- columns << Column.new(self, *column_data.unpack('a10 x a x4 C2'))
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(DBF_HEADER_SIZE))
267
+ Header.new(@data.read(DBASE3_HEADER_SIZE))
248
268
  end
249
269
  end
250
270