dbf 1.5.2 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +13 -77
- data/MIT-LICENSE +1 -1
- data/README.md +1 -1
- data/Rakefile +5 -5
- data/lib/dbf.rb +0 -1
- data/lib/dbf/column.rb +28 -32
- data/lib/dbf/encodings.yml +61 -0
- data/lib/dbf/memo.rb +4 -2
- data/lib/dbf/table.rb +56 -53
- data/lib/dbf/version.rb +1 -1
- data/spec/dbf/record_spec.rb +19 -8
- data/spec/fixtures/cp1251.dbf +0 -0
- metadata +19 -32
data/Gemfile.lock
CHANGED
@@ -3,92 +3,28 @@ PATH
|
|
3
3
|
specs:
|
4
4
|
dbf (1.5.2)
|
5
5
|
activesupport (~> 3.0.0)
|
6
|
-
fastercsv (= 1.5.
|
7
|
-
i18n (~> 0.
|
6
|
+
fastercsv (= 1.5.4)
|
7
|
+
i18n (~> 0.5.0)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: http://rubygems.org/
|
11
11
|
specs:
|
12
|
-
|
13
|
-
activesupport (3.0.3)
|
14
|
-
arrayfields (4.7.4)
|
15
|
-
chronic (0.2.3)
|
16
|
-
hoe (>= 1.2.1)
|
17
|
-
churn (0.0.12)
|
18
|
-
chronic (~> 0.2.3)
|
19
|
-
hirb
|
20
|
-
json_pure
|
21
|
-
main
|
22
|
-
ruby_parser (~> 2.0.4)
|
23
|
-
sexp_processor (~> 3.0.3)
|
24
|
-
colored (1.2)
|
12
|
+
activesupport (3.0.5)
|
25
13
|
diff-lcs (1.1.2)
|
26
|
-
fastercsv (1.5.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
hirb (0.3.5)
|
35
|
-
hoe (2.7.0)
|
36
|
-
rake (>= 0.8.7)
|
37
|
-
rubyforge (>= 2.0.4)
|
38
|
-
i18n (0.4.2)
|
39
|
-
json_pure (1.4.6)
|
40
|
-
main (4.3.0)
|
41
|
-
arrayfields (>= 4.7.4)
|
42
|
-
fattr (>= 2.1.0)
|
43
|
-
metric_fu (2.0.1)
|
44
|
-
Saikuro (>= 1.1.0)
|
45
|
-
activesupport (>= 2.0.0)
|
46
|
-
chronic (~> 0.2.3)
|
47
|
-
churn (>= 0.0.7)
|
48
|
-
flay (>= 1.2.1)
|
49
|
-
flog (>= 2.2.0)
|
50
|
-
rails_best_practices (>= 0.3.16)
|
51
|
-
rcov (>= 0.8.3.3)
|
52
|
-
reek (>= 1.2.6)
|
53
|
-
roodi (>= 2.1.0)
|
54
|
-
progressbar (0.9.0)
|
55
|
-
rails_best_practices (0.5.0)
|
56
|
-
activesupport
|
57
|
-
colored (~> 1.2)
|
58
|
-
progressbar (~> 0.9.0)
|
59
|
-
ruby_parser (~> 2.0.4)
|
60
|
-
rake (0.8.7)
|
61
|
-
rcov (0.9.9)
|
62
|
-
reek (1.2.8)
|
63
|
-
ruby2ruby (~> 1.2)
|
64
|
-
ruby_parser (~> 2.0)
|
65
|
-
sexp_processor (~> 3.0)
|
66
|
-
roodi (2.1.0)
|
67
|
-
ruby_parser
|
68
|
-
rspec (2.1.0)
|
69
|
-
rspec-core (~> 2.1.0)
|
70
|
-
rspec-expectations (~> 2.1.0)
|
71
|
-
rspec-mocks (~> 2.1.0)
|
72
|
-
rspec-core (2.1.0)
|
73
|
-
rspec-expectations (2.1.0)
|
14
|
+
fastercsv (1.5.4)
|
15
|
+
i18n (0.5.0)
|
16
|
+
rspec (2.5.0)
|
17
|
+
rspec-core (~> 2.5.0)
|
18
|
+
rspec-expectations (~> 2.5.0)
|
19
|
+
rspec-mocks (~> 2.5.0)
|
20
|
+
rspec-core (2.5.1)
|
21
|
+
rspec-expectations (2.5.0)
|
74
22
|
diff-lcs (~> 1.1.2)
|
75
|
-
rspec-mocks (2.
|
76
|
-
ruby2ruby (1.2.5)
|
77
|
-
ruby_parser (~> 2.0)
|
78
|
-
sexp_processor (~> 3.0)
|
79
|
-
ruby_parser (2.0.5)
|
80
|
-
sexp_processor (~> 3.0)
|
81
|
-
rubyforge (2.0.4)
|
82
|
-
json_pure (>= 1.1.7)
|
83
|
-
sexp_processor (3.0.5)
|
23
|
+
rspec-mocks (2.5.0)
|
84
24
|
|
85
25
|
PLATFORMS
|
86
26
|
ruby
|
87
27
|
|
88
28
|
DEPENDENCIES
|
89
|
-
activesupport (~> 3.0.0)
|
90
29
|
dbf!
|
91
|
-
|
92
|
-
i18n (~> 0.4.2)
|
93
|
-
metric_fu (= 2.0.1)
|
94
|
-
rspec (= 2.1.0)
|
30
|
+
rspec (= 2.5.0)
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -115,7 +115,7 @@ for a full list of supported column types.
|
|
115
115
|
|
116
116
|
## License
|
117
117
|
|
118
|
-
Copyright (c) 2006-
|
118
|
+
Copyright (c) 2006-2011 Keith Morrison <keithm@infused.org>
|
119
119
|
|
120
120
|
Permission is hereby granted, free of charge, to any person
|
121
121
|
obtaining a copy of this software and associated documentation
|
data/Rakefile
CHANGED
@@ -24,8 +24,8 @@ task :console do
|
|
24
24
|
sh "irb -rubygems -I lib -r dbf.rb"
|
25
25
|
end
|
26
26
|
|
27
|
-
require 'metric_fu'
|
28
|
-
MetricFu::Configuration.run do |config|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
27
|
+
# require 'metric_fu'
|
28
|
+
# MetricFu::Configuration.run do |config|
|
29
|
+
# config.rcov[:test_files] = ['spec/**/*_spec.rb']
|
30
|
+
# config.rcov[:rcov_opts] << "-Ispec"
|
31
|
+
# end
|
data/lib/dbf.rb
CHANGED
@@ -5,7 +5,6 @@ require 'active_support/core_ext/date/conversions'
|
|
5
5
|
require 'active_support/core_ext/time/conversions'
|
6
6
|
require 'active_support/core_ext/date_time/conversions'
|
7
7
|
require 'active_support/core_ext/string/conversions'
|
8
|
-
require 'active_support/core_ext/module/delegation'
|
9
8
|
require 'active_support/core_ext/string/inflections'
|
10
9
|
require 'active_support/core_ext/enumerable'
|
11
10
|
|
data/lib/dbf/column.rb
CHANGED
@@ -1,91 +1,87 @@
|
|
1
1
|
module DBF
|
2
2
|
class ColumnLengthError < StandardError; end
|
3
3
|
class ColumnNameError < StandardError; end
|
4
|
-
|
4
|
+
|
5
5
|
# DBF::Column stores all the information about a column including its name,
|
6
6
|
# type, length and number of decimal places (if any)
|
7
7
|
class Column
|
8
8
|
attr_reader :name, :type, :length, :decimal
|
9
|
-
|
9
|
+
|
10
10
|
# Initialize a new DBF::Column
|
11
11
|
#
|
12
12
|
# @param [String] name
|
13
13
|
# @param [String] type
|
14
14
|
# @param [Fixnum] length
|
15
15
|
# @param [Fixnum] decimal
|
16
|
-
def initialize(name, type, length, decimal)
|
17
|
-
@name, @type, @length, @decimal = clean(name), type, length, decimal
|
18
|
-
|
16
|
+
def initialize(name, type, length, decimal, encoding=nil)
|
17
|
+
@name, @type, @length, @decimal, @encoding = clean(name), type, length, decimal, encoding
|
18
|
+
|
19
19
|
raise ColumnLengthError, "field length must be greater than 0" unless length > 0
|
20
20
|
raise ColumnNameError, "column name cannot be empty" if @name.length == 0
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
# Cast value to native type
|
24
24
|
#
|
25
25
|
# @param [String] value
|
26
|
-
# @return [Fixnum, Float, Date, DateTime, Boolean, String]
|
26
|
+
# @return [Fixnum, Float, Date, DateTime, Boolean, String]
|
27
27
|
def type_cast(value)
|
28
28
|
case type
|
29
29
|
when 'N' then unpack_number(value)
|
30
30
|
when 'I' then unpack_unsigned_long(value)
|
31
|
-
when 'F' then
|
31
|
+
when 'F' then value.to_f
|
32
32
|
when 'D' then decode_date(value)
|
33
33
|
when 'T' then decode_datetime(value)
|
34
34
|
when 'L' then boolean(value)
|
35
|
-
else value.to_s.strip
|
35
|
+
else encode_string(value.to_s).strip
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def memo?
|
40
40
|
@memo ||= type == 'M'
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
# Schema definition
|
44
44
|
#
|
45
45
|
# @return [String]
|
46
46
|
def schema_definition
|
47
|
-
"\"#{
|
47
|
+
"\"#{underscored_name}\", #{schema_data_type}\n"
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
def underscored_name
|
51
51
|
@underscored_name ||= name.underscore
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
private
|
55
|
-
|
55
|
+
|
56
56
|
def decode_date(value) #nodoc
|
57
57
|
value.gsub!(' ', '0')
|
58
58
|
value.blank? ? nil : value.to_date
|
59
59
|
rescue
|
60
60
|
nil
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
def decode_datetime(value) #nodoc
|
64
64
|
days, milliseconds = value.unpack('l2')
|
65
65
|
seconds = milliseconds / 1000
|
66
66
|
DateTime.jd(days, seconds/3600, seconds/60 % 60, seconds % 60) rescue nil
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
def unpack_number(value) #nodoc
|
70
|
-
decimal.zero? ?
|
70
|
+
decimal.zero? ? value.to_i : value.to_f
|
71
71
|
end
|
72
|
-
|
73
|
-
def unpack_float(value) #nodoc
|
74
|
-
value.to_f
|
75
|
-
end
|
76
|
-
|
77
|
-
def unpack_integer(value) #nodoc
|
78
|
-
value.to_i
|
79
|
-
end
|
80
|
-
|
72
|
+
|
81
73
|
def unpack_unsigned_long(value) #nodoc
|
82
74
|
value.unpack('V')[0]
|
83
75
|
end
|
84
|
-
|
76
|
+
|
85
77
|
def boolean(value) #nodoc
|
86
78
|
value.strip =~ /^(y|t)$/i ? true : false
|
87
79
|
end
|
88
|
-
|
80
|
+
|
81
|
+
def encode_string(value)
|
82
|
+
@encoding ? value.force_encoding(@encoding).encode(Encoding.default_external) : value
|
83
|
+
end
|
84
|
+
|
89
85
|
def schema_data_type #nodoc
|
90
86
|
case type
|
91
87
|
when "N", "F"
|
@@ -104,13 +100,13 @@ module DBF
|
|
104
100
|
":string, :limit => #{length}"
|
105
101
|
end
|
106
102
|
end
|
107
|
-
|
103
|
+
|
108
104
|
def clean(s) #nodoc
|
109
105
|
first_null = s.index("\x00")
|
110
106
|
s = s[0, first_null] if first_null
|
111
107
|
s.gsub(/[^\x20-\x7E]/, "")
|
112
108
|
end
|
113
|
-
|
109
|
+
|
114
110
|
end
|
115
|
-
|
111
|
+
|
116
112
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# inspired by http://trac.osgeo.org/gdal/ticket/2864
|
2
|
+
|
3
|
+
"01": cp437 # U.S. MS–DOS
|
4
|
+
"02": cp850 # International MS–DOS
|
5
|
+
"03": cp1252 # Windows ANSI
|
6
|
+
"08": cp865 # Danish OEM
|
7
|
+
"09": cp437 # Dutch OEM
|
8
|
+
"0a": cp850 # Dutch OEM*
|
9
|
+
"0b": cp437 # Finnish OEM
|
10
|
+
"0d": cp437 # French OEM
|
11
|
+
"0e": cp850 # French OEM*
|
12
|
+
"0f": cp437 # German OEM
|
13
|
+
"10": cp850 # German OEM*
|
14
|
+
"11": cp437 # Italian OEM
|
15
|
+
"12": cp850 # Italian OEM*
|
16
|
+
"13": cp932 # Japanese Shift-JIS
|
17
|
+
"14": cp850 # Spanish OEM*
|
18
|
+
"15": cp437 # Swedish OEM
|
19
|
+
"16": cp850 # Swedish OEM*
|
20
|
+
"17": cp865 # Norwegian OEM
|
21
|
+
"18": cp437 # Spanish OEM
|
22
|
+
"19": cp437 # English OEM (Britain)
|
23
|
+
"1a": cp850 # English OEM (Britain)*
|
24
|
+
"1b": cp437 # English OEM (U.S.)
|
25
|
+
"1c": cp863 # French OEM (Canada)
|
26
|
+
"1d": cp850 # French OEM*
|
27
|
+
"1f": cp852 # Czech OEM
|
28
|
+
"22": cp852 # Hungarian OEM
|
29
|
+
"23": cp852 # Polish OEM
|
30
|
+
"24": cp860 # Portuguese OEM
|
31
|
+
"25": cp850 # Portuguese OEM*
|
32
|
+
"26": cp866 # Russian OEM
|
33
|
+
"37": cp850 # English OEM (U.S.)*
|
34
|
+
"40": cp852 # Romanian OEM
|
35
|
+
"4d": cp936 # Chinese GBK (PRC)
|
36
|
+
"4e": cp949 # Korean (ANSI/OEM)
|
37
|
+
"4f": cp950 # Chinese Big5 (Taiwan)
|
38
|
+
"50": cp874 # Thai (ANSI/OEM)
|
39
|
+
"57": cp1252 # ANSI
|
40
|
+
"58": cp1252 # Western European ANSI
|
41
|
+
"59": cp1252 # Spanish ANSI
|
42
|
+
"64": cp852 # Eastern European MS–DOS
|
43
|
+
"65": cp866 # Russian MS–DOS
|
44
|
+
"66": cp865 # Nordic MS–DOS
|
45
|
+
"67": cp861 # Icelandic MS–DOS
|
46
|
+
"6a": cp737 # Greek MS–DOS (437G)
|
47
|
+
"6b": cp857 # Turkish MS–DOS
|
48
|
+
"6c": cp863 # French–Canadian MS–DOS
|
49
|
+
"78": cp950 # Taiwan Big 5
|
50
|
+
"79": cp949 # Hangul (Wansung)
|
51
|
+
"7a": cp936 # PRC GBK
|
52
|
+
"7b": cp932 # Japanese Shift-JIS
|
53
|
+
"7c": cp874 # Thai Windows/MS–DOS
|
54
|
+
"86": cp737 # Greek OEM
|
55
|
+
"87": cp852 # Slovenian OEM
|
56
|
+
"88": cp857 # Turkish OEM
|
57
|
+
"c8": cp1250 # Eastern European Windows
|
58
|
+
"c9": cp1251 # Russian Windows
|
59
|
+
"ca": cp1254 # Turkish Windows
|
60
|
+
"cb": cp1253 # Greek Windows
|
61
|
+
"cc": cp1257 # Baltic Windows
|
data/lib/dbf/memo.rb
CHANGED
@@ -47,10 +47,12 @@ module DBF
|
|
47
47
|
build_dbt_83_memo(start_block)
|
48
48
|
when "8b" # dbase iv
|
49
49
|
build_dbt_8b_memo(start_block)
|
50
|
+
else
|
51
|
+
nil
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
53
|
-
def build_dbt_83_memo(start_block)
|
55
|
+
def build_dbt_83_memo(start_block) #nodoc
|
54
56
|
@data.seek offset(start_block)
|
55
57
|
memo_string = ""
|
56
58
|
begin
|
@@ -60,7 +62,7 @@ module DBF
|
|
60
62
|
memo_string
|
61
63
|
end
|
62
64
|
|
63
|
-
def build_dbt_8b_memo(start_block)
|
65
|
+
def build_dbt_8b_memo(start_block) #nodoc
|
64
66
|
@data.seek offset(start_block)
|
65
67
|
@data.read(@data.read(BLOCK_HEADER_SIZE).unpack("x4L").first)
|
66
68
|
end
|
data/lib/dbf/table.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
module DBF
|
2
2
|
|
3
|
-
# DBF::Table is the primary interface to a single DBF file and provides
|
3
|
+
# DBF::Table is the primary interface to a single DBF file and provides
|
4
4
|
# methods for enumerating and searching the records.
|
5
|
-
|
5
|
+
|
6
6
|
# TODO set record_length to length of actual used column lengths
|
7
7
|
class Table
|
8
8
|
include Enumerable
|
9
|
-
|
9
|
+
|
10
10
|
DBF_HEADER_SIZE = 32
|
11
|
-
|
11
|
+
|
12
12
|
VERSION_DESCRIPTIONS = {
|
13
13
|
"02" => "FoxBase",
|
14
14
|
"03" => "dBase III without memo file",
|
@@ -23,10 +23,11 @@ module DBF
|
|
23
23
|
"f5" => "FoxPro with memo file",
|
24
24
|
"fb" => "FoxPro without memo file"
|
25
25
|
}
|
26
|
-
|
27
|
-
attr_reader
|
28
|
-
attr_reader
|
29
|
-
|
26
|
+
|
27
|
+
attr_reader :version # Internal dBase version number
|
28
|
+
attr_reader :record_count # Total number of records
|
29
|
+
attr_accessor :encoding # Source encoding (for ex. :cp1251)
|
30
|
+
|
30
31
|
# Opens a DBF::Table
|
31
32
|
# Example:
|
32
33
|
# table = DBF::Table.new 'data.dbf'
|
@@ -37,13 +38,13 @@ module DBF
|
|
37
38
|
get_header_info
|
38
39
|
@memo = open_memo(path)
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
# Closes the table and memo file
|
42
43
|
def close
|
43
44
|
@memo && @memo.close
|
44
45
|
@data.close
|
45
46
|
end
|
46
|
-
|
47
|
+
|
47
48
|
# Calls block once for each record in the table. The record may be nil
|
48
49
|
# if the record has been marked as deleted.
|
49
50
|
#
|
@@ -51,7 +52,7 @@ module DBF
|
|
51
52
|
def each
|
52
53
|
@record_count.times {|i| yield record(i)}
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
# Retrieve a record by index number.
|
56
57
|
# The record will be nil if it has been deleted, but not yet pruned from
|
57
58
|
# the database.
|
@@ -62,18 +63,18 @@ module DBF
|
|
62
63
|
seek_to_record(index)
|
63
64
|
current_record
|
64
65
|
end
|
65
|
-
|
66
|
+
|
66
67
|
alias_method :row, :record
|
67
|
-
|
68
|
+
|
68
69
|
# Human readable version description
|
69
70
|
#
|
70
71
|
# @return [String]
|
71
72
|
def version_description
|
72
73
|
VERSION_DESCRIPTIONS[version]
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
# Generate an ActiveRecord::Schema
|
76
|
-
#
|
77
|
+
#
|
77
78
|
# xBase data types are converted to generic types as follows:
|
78
79
|
# - Number columns with no decimals are converted to :integer
|
79
80
|
# - Number columns with decimals are converted to :float
|
@@ -99,40 +100,39 @@ module DBF
|
|
99
100
|
columns.each do |column|
|
100
101
|
s << " t.column #{column.schema_definition}"
|
101
102
|
end
|
102
|
-
s << " end\nend"
|
103
|
+
s << " end\nend"
|
103
104
|
s
|
104
105
|
end
|
105
|
-
|
106
|
+
|
106
107
|
# Dumps all records to a CSV file. If no filename is given then CSV is
|
107
108
|
# output to STDOUT.
|
108
109
|
#
|
109
110
|
# @param [optional String] path Defaults to basename of dbf file
|
110
111
|
def to_csv(path = nil)
|
111
|
-
path
|
112
|
-
csv_class.open(path, 'w', :force_quotes => true) do |csv|
|
112
|
+
csv_class.open(path || default_csv_path, 'w', :force_quotes => true) do |csv|
|
113
113
|
csv << columns.map {|c| c.name}
|
114
114
|
each {|record| csv << record.to_a}
|
115
115
|
end
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
# Find records using a simple ActiveRecord-like syntax.
|
119
119
|
#
|
120
120
|
# Examples:
|
121
121
|
# table = DBF::Table.new 'mydata.dbf'
|
122
|
-
#
|
122
|
+
#
|
123
123
|
# # Find record number 5
|
124
124
|
# table.find(5)
|
125
125
|
#
|
126
126
|
# # Find all records for Keith Morrison
|
127
127
|
# table.find :all, :first_name => "Keith", :last_name => "Morrison"
|
128
|
-
#
|
128
|
+
#
|
129
129
|
# # Find first record
|
130
130
|
# table.find :first, :first_name => "Keith"
|
131
131
|
#
|
132
132
|
# The <b>command</b> may be a record index, :all, or :first.
|
133
133
|
# <b>options</b> is optional and, if specified, should be a hash where the keys correspond
|
134
134
|
# to column names in the database. The values will be matched exactly with the value
|
135
|
-
# in the database. If you specify more than one key, all values must match in order
|
135
|
+
# in the database. If you specify more than one key, all values must match in order
|
136
136
|
# for the record to be returned. The equivalent SQL would be "WHERE key1 = 'value1'
|
137
137
|
# AND key2 = 'value2'".
|
138
138
|
#
|
@@ -151,23 +151,24 @@ module DBF
|
|
151
151
|
find_first(options)
|
152
152
|
end
|
153
153
|
end
|
154
|
-
|
154
|
+
|
155
155
|
# Retrieves column information from the database
|
156
156
|
def columns
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
157
|
+
@columns ||= begin
|
158
|
+
column_count = (@header_length - DBF_HEADER_SIZE + 1) / DBF_HEADER_SIZE
|
159
|
+
|
160
|
+
@data.seek(DBF_HEADER_SIZE)
|
161
|
+
columns = []
|
162
|
+
column_count.times do
|
163
|
+
name, type, length, decimal = @data.read(32).unpack('a10 x a x4 C2')
|
164
|
+
columns << Column.new(name.strip, type, length, decimal, @encoding) if length > 0
|
165
|
+
end
|
166
|
+
columns
|
165
167
|
end
|
166
|
-
@columns
|
167
168
|
end
|
168
|
-
|
169
|
+
|
169
170
|
private
|
170
|
-
|
171
|
+
|
171
172
|
def open_memo(path) #nodoc
|
172
173
|
%w(fpt FPT dbt DBT).each do |extname|
|
173
174
|
filename = path.sub(/#{File.extname(path)[1..-1]}$/, extname)
|
@@ -177,7 +178,7 @@ module DBF
|
|
177
178
|
end
|
178
179
|
nil
|
179
180
|
end
|
180
|
-
|
181
|
+
|
181
182
|
def find_all(options) #nodoc
|
182
183
|
map do |record|
|
183
184
|
if record.try(:match?, options)
|
@@ -186,43 +187,45 @@ module DBF
|
|
186
187
|
end
|
187
188
|
end.compact
|
188
189
|
end
|
189
|
-
|
190
|
+
|
190
191
|
def find_first(options) #nodoc
|
191
|
-
|
192
|
-
return record if record.try(:match?, options)
|
193
|
-
end
|
194
|
-
nil
|
192
|
+
detect {|record| record.try(:match?, options)}
|
195
193
|
end
|
196
|
-
|
194
|
+
|
197
195
|
def deleted_record? #nodoc
|
198
196
|
@data.read(1).unpack('a') == ['*']
|
199
197
|
end
|
200
|
-
|
201
|
-
def current_record
|
198
|
+
|
199
|
+
def current_record #nodoc
|
202
200
|
deleted_record? ? nil : DBF::Record.new(@data.read(@record_length), columns, version, @memo)
|
203
201
|
end
|
204
|
-
|
202
|
+
|
205
203
|
def get_header_info #nodoc
|
206
204
|
@data.rewind
|
207
|
-
@version, @record_count, @header_length, @record_length =
|
205
|
+
@version, @record_count, @header_length, @record_length, encoding_key =
|
206
|
+
@data.read(DBF_HEADER_SIZE).unpack("H2 x3 V v2 x17H2")
|
207
|
+
@encoding = self.class.encodings[encoding_key] if "".respond_to? :encoding
|
208
208
|
end
|
209
|
-
|
209
|
+
|
210
210
|
def seek(offset) #nodoc
|
211
211
|
@data.seek @header_length + offset
|
212
212
|
end
|
213
|
-
|
213
|
+
|
214
214
|
def seek_to_record(index) #nodoc
|
215
215
|
seek index * @record_length
|
216
216
|
end
|
217
|
-
|
217
|
+
|
218
218
|
def csv_class #nodoc
|
219
219
|
CSV.const_defined?(:Reader) ? FCSV : CSV
|
220
220
|
end
|
221
|
-
|
221
|
+
|
222
222
|
def default_csv_path #nodoc
|
223
223
|
File.basename(@data.path, '.dbf') + '.csv'
|
224
224
|
end
|
225
|
-
|
225
|
+
|
226
|
+
def self.encodings
|
227
|
+
@encodings ||= YAML.load_file(File.expand_path("../encodings.yml", __FILE__))
|
228
|
+
end
|
226
229
|
end
|
227
|
-
|
228
|
-
end
|
230
|
+
|
231
|
+
end
|
data/lib/dbf/version.rb
CHANGED
data/spec/dbf/record_spec.rb
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe DBF::Record do
|
4
|
-
|
4
|
+
|
5
5
|
describe '#to_a' do
|
6
6
|
it 'should return an ordered array of attribute values' do
|
7
7
|
table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
|
8
|
-
|
8
|
+
|
9
9
|
record = table.record(0)
|
10
10
|
record.to_a.should == ["One", 1.0, Date.new(1970, 1, 1), true, 1.23456789012346, "First memo\r\n\037 \037 \037 \037 "]
|
11
|
-
|
11
|
+
|
12
12
|
record = table.record(9)
|
13
13
|
record.to_a.should == ["Ten records stored in this database", 10.0, nil, false, 0.1, nil]
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
describe '#==' do
|
18
18
|
before do
|
19
19
|
table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
|
20
20
|
@record = table.record(9)
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
it 'should be false if other does not have attributes' do
|
24
24
|
(@record == mock('other')).should be_false
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
it 'should be true if other attributes match' do
|
28
28
|
attributes = {:x => 1, :y => 2}
|
29
29
|
@record.stub!(:attributes).and_return(attributes)
|
@@ -31,10 +31,10 @@ describe DBF::Record do
|
|
31
31
|
(@record == other).should be_true
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
describe 'column accessors' do
|
36
36
|
let(:table) { DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"}
|
37
|
-
|
37
|
+
|
38
38
|
it 'should define accessor methods for each column' do
|
39
39
|
record = table.find(0)
|
40
40
|
record.should respond_to(:character)
|
@@ -42,4 +42,15 @@ describe DBF::Record do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe 'column data for table' do
|
46
|
+
let(:table) { DBF::Table.new "#{DB_PATH}/cp1251.dbf"}
|
47
|
+
|
48
|
+
let(:record) { table.find(0) }
|
49
|
+
it 'should automatically encodes to default system encoding' do
|
50
|
+
if "".respond_to? :encoding
|
51
|
+
record.name.encoding.should == Encoding.default_external
|
52
|
+
record.name.encode("UTF-8").unpack("H4").should == ["d0b0"] # russian a
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
45
56
|
end
|
Binary file
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 5
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 1.5.
|
9
|
+
- 3
|
10
|
+
version: 1.5.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Keith Morrison
|
@@ -15,8 +15,8 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
19
|
-
default_executable:
|
18
|
+
date: 2011-04-06 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: activesupport
|
@@ -45,9 +45,9 @@ dependencies:
|
|
45
45
|
hash: 11
|
46
46
|
segments:
|
47
47
|
- 0
|
48
|
-
-
|
49
|
-
-
|
50
|
-
version: 0.
|
48
|
+
- 5
|
49
|
+
- 0
|
50
|
+
version: 0.5.0
|
51
51
|
type: :runtime
|
52
52
|
version_requirements: *id002
|
53
53
|
- !ruby/object:Gem::Dependency
|
@@ -58,12 +58,12 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
hash:
|
61
|
+
hash: 11
|
62
62
|
segments:
|
63
63
|
- 1
|
64
64
|
- 5
|
65
|
-
-
|
66
|
-
version: 1.5.
|
65
|
+
- 4
|
66
|
+
version: 1.5.4
|
67
67
|
type: :runtime
|
68
68
|
version_requirements: *id003
|
69
69
|
- !ruby/object:Gem::Dependency
|
@@ -74,30 +74,14 @@ dependencies:
|
|
74
74
|
requirements:
|
75
75
|
- - "="
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
hash:
|
77
|
+
hash: 27
|
78
78
|
segments:
|
79
79
|
- 2
|
80
|
-
-
|
80
|
+
- 5
|
81
81
|
- 0
|
82
|
-
version: 2.
|
82
|
+
version: 2.5.0
|
83
83
|
type: :development
|
84
84
|
version_requirements: *id004
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: metric_fu
|
87
|
-
prerelease: false
|
88
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - "="
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
hash: 13
|
94
|
-
segments:
|
95
|
-
- 2
|
96
|
-
- 0
|
97
|
-
- 1
|
98
|
-
version: 2.0.1
|
99
|
-
type: :development
|
100
|
-
version_requirements: *id005
|
101
85
|
description: A small fast library for reading dBase, xBase, Clipper and FoxPro database files.
|
102
86
|
email: keithm@infused.org
|
103
87
|
executables:
|
@@ -107,6 +91,7 @@ extensions: []
|
|
107
91
|
extra_rdoc_files:
|
108
92
|
- README.md
|
109
93
|
- CHANGELOG.md
|
94
|
+
- MIT-LICENSE
|
110
95
|
files:
|
111
96
|
- CHANGELOG.md
|
112
97
|
- Gemfile
|
@@ -118,6 +103,7 @@ files:
|
|
118
103
|
- docs/supported_types.markdown
|
119
104
|
- lib/dbf/attributes.rb
|
120
105
|
- lib/dbf/column.rb
|
106
|
+
- lib/dbf/encodings.yml
|
121
107
|
- lib/dbf/memo.rb
|
122
108
|
- lib/dbf/record.rb
|
123
109
|
- lib/dbf/table.rb
|
@@ -127,6 +113,7 @@ files:
|
|
127
113
|
- spec/dbf/file_formats_spec.rb
|
128
114
|
- spec/dbf/record_spec.rb
|
129
115
|
- spec/dbf/table_spec.rb
|
116
|
+
- spec/fixtures/cp1251.dbf
|
130
117
|
- spec/fixtures/dbase_03.dbf
|
131
118
|
- spec/fixtures/dbase_30.dbf
|
132
119
|
- spec/fixtures/dbase_30.fpt
|
@@ -171,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
158
|
requirements: []
|
172
159
|
|
173
160
|
rubyforge_project:
|
174
|
-
rubygems_version: 1.
|
161
|
+
rubygems_version: 1.6.2
|
175
162
|
signing_key:
|
176
163
|
specification_version: 3
|
177
164
|
summary: Read xBase files
|