dbf 0.4.6 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ = DBF
2
+
3
+ DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro database files. It is written completely in Ruby and has no external dependencies.
4
+
5
+ == Features
6
+
7
+ * No external dependencies
8
+ * DB fields are type cast
9
+ * Date/Time fields are returned as either a Time or Date object. Date
10
+ will only be used if the date is outside the range for Time.
11
+
12
+ == Installation
13
+
14
+ gem install dbf
15
+
16
+ == Usage
17
+
18
+ reader = DBF::Reader.new("old_data.dbf")
19
+
20
+ reader.records.each do |record|
21
+ puts record['name']
22
+ puts record['email']
23
+ end
24
+
25
+ puts reader.records[4]['name']
26
+ puts reader.record(4)['name']
27
+
28
+ === A note on record vs. records
29
+
30
+ DBF::Reader#records is an in-memory array of all rows in the database. All
31
+ rows are loaded the first time that the method is called. Subsequent calls
32
+ retrieve the row from memory.
33
+
34
+ DBF::Reader#record retrieves the requested row from the database each time
35
+ it is called.
36
+
37
+ Using records is probably faster most of the time. Record is more appropriate
38
+ for very large databases where you don't want the whole db loaded into memory.
39
+
40
+ == Limitations and known bugs
41
+
42
+ * DBF is read-only. Writing to the database has not yet been implemented.
43
+
44
+ == License
45
+
46
+ Copyright (c) 2006-2007 Keith Morrison <keithm@infused.org>
47
+
48
+ Permission is hereby granted, free of charge, to any person
49
+ obtaining a copy of this software and associated documentation
50
+ files (the "Software"), to deal in the Software without
51
+ restriction, including without limitation the rights to use,
52
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
53
+ copies of the Software, and to permit persons to whom the
54
+ Software is furnished to do so, subject to the following
55
+ conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
62
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
63
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
64
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
65
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
66
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
67
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,14 +1,15 @@
1
1
  require 'hoe'
2
+ require 'spec/rake/spectask'
2
3
 
3
4
  PKG_NAME = "dbf"
4
- PKG_VERSION = "0.4.6"
5
+ PKG_VERSION = "0.4.7"
5
6
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
6
7
 
7
8
  Hoe.new PKG_NAME, PKG_VERSION do |p|
8
9
  p.rubyforge_name = PKG_NAME
9
10
  p.author = "Keith Morrison"
10
11
  p.email = "keithm@infused.org"
11
- p.summary = "A library for reading dBase (or xBase, Clipper, Foxpro, etc) database files"
12
+ p.summary = "A small fast library for reading dBase, xBase, Clipper and FoxPro database files."
12
13
  p.url = "http://dbf.rubyforge.org"
13
14
  p.need_tar = true
14
15
  p.need_zip = true
@@ -23,3 +24,9 @@ Rake::TestTask.new :test do |t|
23
24
  t.pattern = 'test/*_test.rb'
24
25
  t.verbose = true
25
26
  end
27
+
28
+ desc "Run specs"
29
+ Spec::Rake::SpecTask.new :spec do |t|
30
+ t.spec_opts = ["-f specdoc"]
31
+ t.spec_files = FileList['spec/**/*spec.rb']
32
+ end
data/lib/dbf.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'date'
2
2
 
3
- require 'dbf/common'
3
+ require 'dbf/globals'
4
+ require 'dbf/record'
5
+ require 'dbf/field'
4
6
  require 'dbf/reader'
@@ -0,0 +1,12 @@
1
+ module DBF
2
+ class FieldLengthError < DBFError; end
3
+ class Field
4
+ attr_reader :name, :type, :length, :decimal
5
+
6
+ def initialize(name, type, length, decimal)
7
+ raise FieldLengthError, "field length must be greater than 0" unless length > 0
8
+ @name, @type, @length, @decimal = name.gsub(/\0/, ''), type, length, decimal
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module DBF
2
+ DBF_HEADER_SIZE = 32
3
+ FPT_HEADER_SIZE = 512
4
+ FPT_BLOCK_HEADER_SIZE = 8
5
+ DATE_REGEXP = /([\d]{4})([\d]{2})([\d]{2})/
6
+ VERSION_DESCRIPTIONS = {
7
+ "02" => "FoxBase",
8
+ "03" => "dBase III without memo file",
9
+ "04" => "dBase IV without memo file",
10
+ "05" => "dBase V without memo file",
11
+ "30" => "Visual FoxPro",
12
+ "31" => "Visual FoxPro with AutoIncrement field",
13
+ "7b" => "dBase IV with memo file",
14
+ "83" => "dBase III with memo file",
15
+ "8b" => "dBase IV with memo file",
16
+ "8e" => "dBase IV with SQL table",
17
+ "f5" => "FoxPro with memo file",
18
+ "fb" => "FoxPro without memo file"
19
+ }
20
+
21
+ class DBFError < StandardError; end
22
+ end
@@ -44,7 +44,7 @@ module DBF
44
44
  seek_to_record(0)
45
45
  @records ||= Array.new(@record_count) do |i|
46
46
  if active_record?
47
- Record.new(self, @data_file, @memo_file)
47
+ DBF::Record.new(self, @data_file, @memo_file)
48
48
  else
49
49
  seek_to_record(i + 1)
50
50
  nil
@@ -117,86 +117,4 @@ module DBF
117
117
 
118
118
  end
119
119
 
120
- class FieldError < StandardError; end
121
-
122
- class Field
123
- attr_accessor :name, :type, :length, :decimal
124
-
125
- def initialize(name, type, length, decimal)
126
- raise FieldError, "field length must be greater than 0" unless length > 0
127
- self.name, self.type, self.length, self.decimal = name.strip, type, length, decimal
128
- end
129
-
130
- def name=(name)
131
- @name = name.gsub(/\0/, '')
132
- end
133
-
134
- end
135
-
136
- class Record < Hash
137
-
138
- def initialize(reader, data_file, memo_file)
139
- @reader, @data_file, @memo_file = reader, data_file, memo_file
140
- reader.fields.each do |field|
141
- case field.type
142
- when 'N' # number
143
- self[field.name] = field.decimal == 0 ? unpack_string(field).to_i : unpack_string(field).to_f
144
- when 'D' # date
145
- raw = unpack_string(field).strip
146
- unless raw.empty?
147
- begin
148
- self[field.name] = Time.gm(*raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i})
149
- rescue
150
- self[field.name] = Date.new(*raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i})
151
- end
152
- end
153
- when 'M' # memo
154
- starting_block = unpack_string(field).to_i
155
- self[field.name] = read_memo(starting_block)
156
- when 'L' # logical
157
- self[field.name] = unpack_string(field) =~ /^(y|t)$/i ? true : false
158
- else
159
- self[field.name] = unpack_string(field)
160
- end
161
- end
162
- self
163
- end
164
-
165
- def unpack_field(field)
166
- @data_file.read(field.length).unpack("a#{field.length}")
167
- end
168
-
169
- def unpack_string(field)
170
- unpack_field(field).to_s
171
- end
172
-
173
- def read_memo(start_block)
174
- return nil if start_block == 0
175
- @memo_file.seek(start_block * @reader.memo_block_size)
176
- if @reader.memo_file_format == :fpt
177
- memo_type, memo_size, memo_string = @memo_file.read(@reader.memo_block_size).unpack("NNa56")
178
-
179
- memo_block_content_size = @reader.memo_block_size - FPT_BLOCK_HEADER_SIZE
180
- if memo_size > memo_block_content_size
181
- memo_string << @memo_file.read(memo_size - @reader.memo_block_size + FPT_BLOCK_HEADER_SIZE)
182
- elsif memo_size > 0 and memo_size < memo_block_content_size
183
- memo_string = memo_string[0, memo_size]
184
- end
185
- else
186
- case @reader.version
187
- when "83" # dbase iii
188
- memo_string = ""
189
- loop do
190
- memo_string << block = @memo_file.read(512)
191
- break if block.strip.size < 512
192
- end
193
- when "8b" # dbase iv
194
- memo_type, memo_size = @memo_file.read(8).unpack("LL")
195
- memo_string = @memo_file.read(memo_size)
196
- end
197
- end
198
- memo_string
199
- end
200
- end
201
-
202
- end
120
+ end
@@ -0,0 +1,66 @@
1
+ module DBF
2
+ class Record < Hash
3
+ def initialize(reader, data_file, memo_file)
4
+ @reader, @data_file, @memo_file = reader, data_file, memo_file
5
+ reader.fields.each do |field|
6
+ case field.type
7
+ when 'N' # number
8
+ self[field.name] = field.decimal == 0 ? unpack_string(field).to_i : unpack_string(field).to_f
9
+ when 'D' # date
10
+ raw = unpack_string(field).strip
11
+ unless raw.empty?
12
+ begin
13
+ self[field.name] = Time.gm(*raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i})
14
+ rescue
15
+ self[field.name] = Date.new(*raw.match(DATE_REGEXP).to_a.slice(1,3).map {|n| n.to_i})
16
+ end
17
+ end
18
+ when 'M' # memo
19
+ starting_block = unpack_string(field).to_i
20
+ self[field.name] = read_memo(starting_block)
21
+ when 'L' # logical
22
+ self[field.name] = unpack_string(field) =~ /^(y|t)$/i ? true : false
23
+ else
24
+ self[field.name] = unpack_string(field)
25
+ end
26
+ end
27
+ self
28
+ end
29
+
30
+ def unpack_field(field)
31
+ @data_file.read(field.length).unpack("a#{field.length}")
32
+ end
33
+
34
+ def unpack_string(field)
35
+ unpack_field(field).to_s
36
+ end
37
+
38
+ def read_memo(start_block)
39
+ return nil if start_block == 0
40
+ @memo_file.seek(start_block * @reader.memo_block_size)
41
+ if @reader.memo_file_format == :fpt
42
+ memo_type, memo_size, memo_string = @memo_file.read(@reader.memo_block_size).unpack("NNa56")
43
+
44
+ memo_block_content_size = @reader.memo_block_size - FPT_BLOCK_HEADER_SIZE
45
+ if memo_size > memo_block_content_size
46
+ memo_string << @memo_file.read(memo_size - @reader.memo_block_size + FPT_BLOCK_HEADER_SIZE)
47
+ elsif memo_size > 0 and memo_size < memo_block_content_size
48
+ memo_string = memo_string[0, memo_size]
49
+ end
50
+ else
51
+ case @reader.version
52
+ when "83" # dbase iii
53
+ memo_string = ""
54
+ loop do
55
+ memo_string << block = @memo_file.read(512)
56
+ break if block.strip.size < 512
57
+ end
58
+ when "8b" # dbase iv
59
+ memo_type, memo_size = @memo_file.read(8).unpack("LL")
60
+ memo_string = @memo_file.read(memo_size)
61
+ end
62
+ end
63
+ memo_string
64
+ end
65
+ end
66
+ end
@@ -21,7 +21,7 @@ class DBaseIIIReadTest < Test::Unit::TestCase
21
21
  :testable_logical_field_names => [],
22
22
  :testable_memo_field_names => []
23
23
  }
24
- @dbf = DBF::Reader.new(File.join(File.dirname(__FILE__),'databases', 'dbase_iii.dbf'))
24
+ @dbf = DBF::Reader.new "#{File.dirname(__FILE__)}/databases/dbase_iii.dbf"
25
25
  end
26
26
 
27
27
  end
@@ -21,7 +21,7 @@ class FoxproReadTest < Test::Unit::TestCase
21
21
  :testable_logical_field_names => [],
22
22
  :testable_memo_field_names => ["OBSE"]
23
23
  }
24
- @dbf = DBF::Reader.new(File.join(File.dirname(__FILE__),'databases', 'foxpro.dbf'))
24
+ @dbf = DBF::Reader.new "#{File.dirname(__FILE__)}/databases/foxpro.dbf"
25
25
  end
26
26
 
27
27
  # make sure we're grabbing the correct memo
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.3
2
+ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: dbf
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.6
7
- date: 2007-05-21 00:00:00 -07:00
8
- summary: A library for reading dBase (or xBase, Clipper, Foxpro, etc) database files
6
+ version: 0.4.7
7
+ date: 2007-05-25 00:00:00 -07:00
8
+ summary: A small fast library for reading dBase, xBase, Clipper and FoxPro database files.
9
9
  require_paths:
10
10
  - lib
11
11
  email: keithm@infused.org
@@ -30,9 +30,13 @@ authors:
30
30
  - Keith Morrison
31
31
  files:
32
32
  - Rakefile
33
- - README
33
+ - README.txt
34
+ - LICENSE.txt
34
35
  - lib/dbf.rb
36
+ - lib/dbf/field.rb
37
+ - lib/dbf/globals.rb
35
38
  - lib/dbf/reader.rb
39
+ - lib/dbf/record.rb
36
40
  - test/common.rb
37
41
  - test/dbase_iii_read_test.rb
38
42
  - test/foxpro_read_test.rb
@@ -47,8 +51,9 @@ test_files: []
47
51
  rdoc_options:
48
52
  - --main
49
53
  - README.txt
50
- extra_rdoc_files: []
51
-
54
+ extra_rdoc_files:
55
+ - README.txt
56
+ - LICENSE.txt
52
57
  executables: []
53
58
 
54
59
  extensions: []
data/README DELETED
@@ -1,37 +0,0 @@
1
- = DBF
2
- A dBase I/O library.
3
-
4
- == Features
5
-
6
- * No external dependencies
7
- * DB fields are type cast
8
- * Date/Time fields are returned as either a Time or Date object. Date
9
- will only be used if the date is outside the range for Time.
10
-
11
- == Limitations
12
-
13
- * Writing to the db has not been implemented yet
14
-
15
- == Usage
16
-
17
- reader = DBF::Reader.new("old_data.dbf")
18
-
19
- reader.records.each do |record|
20
- puts record['name']
21
- puts record['email']
22
- end
23
-
24
- puts reader.records[4]['name']
25
- puts reader.record(4)['name']
26
-
27
- === A note on record vs. records
28
-
29
- DBF::Reader#records is an in-memory array of all rows in the database. All
30
- rows are loaded the first time that the method is called. Subsequent calls
31
- retrieve the row from memory.
32
-
33
- DBF::Reader#record retrieves the requested row from the database each time
34
- it is called.
35
-
36
- Using records is probably faster most of the time. Record is more appropriate
37
- for very large databases where you don't want the whole db loaded into memory.