dbf 1.5.2 → 1.5.3
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.
- 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
|