dbf 1.7.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,6 +1,8 @@
1
- # 1.7.8
2
- - [#44] Require FasterCSV on all platforms
1
+ # 2.0.0
2
+ - #44 Require FasterCSV gem on all platforms
3
3
  - Remove rdoc development dependency
4
+ - #42 Fixes encoding of memos
5
+ - #43 Improve handling of record attributes
4
6
 
5
7
  # 1.7.5
6
8
  - fixes FoxPro currency (Y) fields
data/README.md CHANGED
@@ -39,14 +39,14 @@ Find a single record
39
39
 
40
40
  widget.find(6)
41
41
 
42
- Attributes can also be accessed through the attributes hash in original or
43
- underscored form or as an accessor method using the underscored name. (Note
44
- that find() will return nil if the requested record has been deleted and not
45
- yet pruned from the database)
42
+ Attributes can also be accessed through the attributes hash or the record
43
+ object using either the original or underscored attribute name. Note that
44
+ find() will return nil if the requested record has been deleted and not yet
45
+ pruned from the database.
46
46
 
47
47
  widget.find(4).attributes["SlotNumber"]
48
- widget.find(4).attributes["slot_number"]
49
- widget.find(4).slot_number
48
+ widget.find(4)["SlotNumber"]
49
+ widget.find(4)[:slot_number]
50
50
 
51
51
  Search for records using a simple hash format. Multiple search criteria are
52
52
  ANDed. Use the block form if the resulting recordset could be large, otherwise
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require 'rubygems'
4
2
  require 'bundler/setup';
5
3
  Bundler.setup(:default, :development)
data/dbf.gemspec CHANGED
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ['lib']
20
20
 
21
21
  s.required_rubygems_version = '>= 1.3.0'
22
-
23
22
  s.add_dependency 'fastercsv', '~> 1.5.4'
24
23
 
25
24
  s.add_development_dependency 'rspec', '~> 2.9.0'
data/lib/dbf.rb CHANGED
@@ -1,19 +1,17 @@
1
1
  require 'date'
2
2
 
3
- require 'yaml'
4
3
  require 'csv'
5
4
  if CSV.const_defined? :Reader
6
5
  require 'fastercsv'
7
6
  end
8
7
 
9
- require 'dbf/util'
10
- require 'dbf/attributes'
11
8
  require 'dbf/record'
12
9
  require 'dbf/column/base'
13
10
  require 'dbf/column/dbase'
14
11
  require 'dbf/column/foxpro'
15
12
  require 'dbf/table'
13
+ require 'dbf/table/encodings'
16
14
  require 'dbf/memo/base'
17
15
  require 'dbf/memo/dbase3'
18
16
  require 'dbf/memo/dbase4'
19
- require 'dbf/memo/foxpro'
17
+ require 'dbf/memo/foxpro'
@@ -33,6 +33,7 @@ module DBF
33
33
  when 'T' then decode_datetime(value)
34
34
  when 'L' then boolean(value)
35
35
  when 'B' then unpack_binary(value)
36
+ when 'M' then decode_memo(value)
36
37
  else encode_string(value.to_s).strip
37
38
  end
38
39
  end
@@ -48,8 +49,16 @@ module DBF
48
49
  "\"#{underscored_name}\", #{schema_data_type}\n"
49
50
  end
50
51
 
52
+ def self.underscore_name(string)
53
+ string.gsub(/::/, '/').
54
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
55
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
56
+ tr('-', '_').
57
+ downcase
58
+ end
59
+
51
60
  def underscored_name
52
- @underscored_name ||= Util.underscore(name)
61
+ @underscored_name ||= self.class.underscore_name(name)
53
62
  end
54
63
 
55
64
  private
@@ -66,6 +75,10 @@ module DBF
66
75
  seconds = (milliseconds / 1000).to_i
67
76
  DateTime.jd(days, (seconds/3600).to_i, (seconds/60).to_i % 60, seconds % 60) rescue nil
68
77
  end
78
+
79
+ def decode_memo(value) #nodoc
80
+ encode_string(value) if value
81
+ end
69
82
 
70
83
  def unpack_number(value) #nodoc
71
84
  decimal.zero? ? value.to_i : value.to_f
@@ -121,4 +134,4 @@ module DBF
121
134
 
122
135
  end
123
136
  end
124
- end
137
+ end
data/lib/dbf/memo/base.rb CHANGED
@@ -18,7 +18,11 @@ module DBF
18
18
  end
19
19
 
20
20
  def close
21
- @data.close
21
+ @data.close && @data.closed?
22
+ end
23
+
24
+ def closed?
25
+ @data.closed?
22
26
  end
23
27
 
24
28
  private
data/lib/dbf/record.rb CHANGED
@@ -10,8 +10,6 @@ module DBF
10
10
  def initialize(data, columns, version, memo)
11
11
  @data = StringIO.new(data)
12
12
  @columns, @version, @memo = columns, version, memo
13
- @column_names = @columns.map {|column| column.underscored_name}
14
- define_accessors
15
13
  end
16
14
 
17
15
  # Equality
@@ -26,7 +24,7 @@ module DBF
26
24
  #
27
25
  # @return [Array]
28
26
  def to_a
29
- @column_names.map {|name| attributes[name]}
27
+ @columns.map {|column| attributes[column.name]}
30
28
  end
31
29
 
32
30
  # Do all search parameters match?
@@ -34,35 +32,50 @@ module DBF
34
32
  # @param [Hash] options
35
33
  # @return [Boolean]
36
34
  def match?(options)
37
- options.all? {|key, value| attributes[Util.underscore(key.to_s)] == value}
35
+ options.all? {|key, value| self[key] == value}
36
+ end
37
+
38
+ # Reads attributes by column name
39
+ def [](key)
40
+ key = key.to_s
41
+ if attributes.has_key?(key)
42
+ attributes[key]
43
+ elsif index = column_names.index(key)
44
+ attributes[@columns[index].name]
45
+ end
38
46
  end
39
47
 
40
48
  # @return [Hash]
41
49
  def attributes
42
- @attributes ||= begin
43
- attributes = Attributes.new
44
- @columns.each {|column| attributes[column.name] = init_attribute(column)}
45
- attributes
46
- end
50
+ @attributes ||= Hash[@columns.map {|column| [column.name, init_attribute(column)]}]
47
51
  end
48
52
 
49
- private
50
-
51
- def define_accessors #nodoc
52
- @column_names.each do |name|
53
- next if respond_to? name
54
- self.class.send(:define_method, name) do
55
- attributes[name]
56
- end
53
+ def respond_to?(method, *args)
54
+ return true if column_names.include?(method.to_s)
55
+ super
56
+ end
57
+
58
+ def method_missing(method, *args)
59
+ if index = column_names.index(method.to_s)
60
+ attributes[@columns[index].name]
61
+ else
62
+ super
57
63
  end
58
64
  end
65
+
66
+ private
67
+
68
+ def column_names
69
+ @column_names ||= @columns.map {|column| column.underscored_name}
70
+ end
59
71
 
60
72
  def init_attribute(column) #nodoc
61
- if column.memo?
73
+ value = if column.memo?
62
74
  @memo.get get_memo_start_block(column)
63
75
  else
64
- column.type_cast unpack_data(column)
76
+ unpack_data(column)
65
77
  end
78
+ column.type_cast value
66
79
  end
67
80
 
68
81
  def get_memo_start_block(column) #nodoc
data/lib/dbf/table.rb CHANGED
@@ -66,8 +66,17 @@ module DBF
66
66
  #
67
67
  # @return [TrueClass, FalseClass]
68
68
  def close
69
+ @data.close
69
70
  @memo && @memo.close
70
- @data.close && @data.closed?
71
+ end
72
+
73
+ # @return [TrueClass, FalseClass]
74
+ def closed?
75
+ if @memo
76
+ @data.closed? && @memo.closed?
77
+ else
78
+ @data.closed?
79
+ end
71
80
  end
72
81
 
73
82
  # @return String
@@ -272,7 +281,7 @@ module DBF
272
281
  def get_header_info #nodoc
273
282
  @data.rewind
274
283
  @version, @record_count, @header_length, @record_length, @encoding_key = read_header
275
- @encoding = self.class.encodings[@encoding_key] if supports_encoding?
284
+ @encoding = ENCODINGS[@encoding_key] if supports_encoding?
276
285
  end
277
286
 
278
287
  def read_header #nodoc
@@ -286,10 +295,6 @@ module DBF
286
295
  def csv_class #nodoc
287
296
  @csv_class ||= CSV.const_defined?(:Reader) ? FCSV : CSV
288
297
  end
289
-
290
- def self.encodings #nodoc
291
- @encodings ||= YAML.load_file File.expand_path("../encodings.yml", __FILE__)
292
- end
293
298
  end
294
299
 
295
300
  end
@@ -0,0 +1,66 @@
1
+ module DBF
2
+ class Table
3
+ # inspired by http://trac.osgeo.org/gdal/ticket/2864
4
+ ENCODINGS = {
5
+ "01" => "cp437", # U.S. MS–DOS
6
+ "02" => "cp850", # International MS–DOS
7
+ "03" => "cp1252", # Windows ANSI
8
+ "08" => "cp865", # Danish OEM
9
+ "09" => "cp437", # Dutch OEM
10
+ "0a" => "cp850", # Dutch OEM*
11
+ "0b" => "cp437", # Finnish OEM
12
+ "0d" => "cp437", # French OEM
13
+ "0e" => "cp850", # French OEM*
14
+ "0f" => "cp437", # German OEM
15
+ "10" => "cp850", # German OEM*
16
+ "11" => "cp437", # Italian OEM
17
+ "12" => "cp850", # Italian OEM*
18
+ "13" => "cp932", # Japanese Shift-JIS
19
+ "14" => "cp850", # Spanish OEM*
20
+ "15" => "cp437", # Swedish OEM
21
+ "16" => "cp850", # Swedish OEM*
22
+ "17" => "cp865", # Norwegian OEM
23
+ "18" => "cp437", # Spanish OEM
24
+ "19" => "cp437", # English OEM (Britain)
25
+ "1a" => "cp850", # English OEM (Britain)*
26
+ "1b" => "cp437", # English OEM (U.S.)
27
+ "1c" => "cp863", # French OEM (Canada)
28
+ "1d" => "cp850", # French OEM*
29
+ "1f" => "cp852", # Czech OEM
30
+ "22" => "cp852", # Hungarian OEM
31
+ "23" => "cp852", # Polish OEM
32
+ "24" => "cp860", # Portuguese OEM
33
+ "25" => "cp850", # Portuguese OEM*
34
+ "26" => "cp866", # Russian OEM
35
+ "37" => "cp850", # English OEM (U.S.)*
36
+ "40" => "cp852", # Romanian OEM
37
+ "4d" => "cp936", # Chinese GBK (PRC)
38
+ "4e" => "cp949", # Korean (ANSI/OEM)
39
+ "4f" => "cp950", # Chinese Big5 (Taiwan)
40
+ "50" => "cp874", # Thai (ANSI/OEM)
41
+ "57" => "cp1252", # ANSI
42
+ "58" => "cp1252", # Western European ANSI
43
+ "59" => "cp1252", # Spanish ANSI
44
+ "64" => "cp852", # Eastern European MS–DOS
45
+ "65" => "cp866", # Russian MS–DOS
46
+ "66" => "cp865", # Nordic MS–DOS
47
+ "67" => "cp861", # Icelandic MS–DOS
48
+ "6a" => "cp737", # Greek MS–DOS (437G)
49
+ "6b" => "cp857", # Turkish MS–DOS
50
+ "6c" => "cp863", # French–Canadian MS–DOS
51
+ "78" => "cp950", # Taiwan Big 5
52
+ "79" => "cp949", # Hangul (Wansung)
53
+ "7a" => "cp936", # PRC GBK
54
+ "7b" => "cp932", # Japanese Shift-JIS
55
+ "7c" => "cp874", # Thai Windows/MS–DOS
56
+ "86" => "cp737", # Greek OEM
57
+ "87" => "cp852", # Slovenian OEM
58
+ "88" => "cp857", # Turkish OEM
59
+ "c8" => "cp1250", # Eastern European Windows
60
+ "c9" => "cp1251", # Russian Windows
61
+ "ca" => "cp1254", # Turkish Windows
62
+ "cb" => "cp1253", # Greek Windows
63
+ "cc" => "cp1257", # Baltic Windows
64
+ }
65
+ end
66
+ end
data/lib/dbf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module DBF
2
- VERSION = '1.7.8'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -22,15 +22,21 @@ describe DBF::Column::Dbase do
22
22
  end
23
23
 
24
24
  describe 'with length of 0' do
25
- specify { lambda { DBF::Column::Dbase.new "ColumnName", "N", 0, 0, "30" }.should raise_error(DBF::Column::LengthError) }
25
+ it 'raises DBF::Column::LengthError' do
26
+ expect { DBF::Column::Dbase.new "ColumnName", "N", 0, 0, "30" }.to raise_error(DBF::Column::LengthError)
27
+ end
26
28
  end
27
29
 
28
30
  describe 'with length less than 0' do
29
- specify { lambda { DBF::Column::Dbase.new "ColumnName", "N", -1, 0, "30" }.should raise_error(DBF::Column::LengthError) }
31
+ it 'raises DBF::Column::LengthError' do
32
+ expect { DBF::Column::Dbase.new "ColumnName", "N", -1, 0, "30" }.to raise_error(DBF::Column::LengthError)
33
+ end
30
34
  end
31
35
 
32
36
  describe 'with empty column name' do
33
- specify { lambda { DBF::Column::Dbase.new "\xFF\xFC", "N", 1, 0, "30" }.should raise_error(DBF::Column::NameError) }
37
+ it 'raises DBF::Column::NameError' do
38
+ expect { DBF::Column::Dbase.new "\xFF\xFC", "N", 1, 0, "30" }.to raise_error(DBF::Column::NameError)
39
+ end
34
40
  end
35
41
  end
36
42
 
@@ -97,12 +103,14 @@ describe DBF::Column::Dbase do
97
103
  end
98
104
  end
99
105
 
100
- context 'when requiring mathn' do
101
- it "casts to DateTime" do
102
- lambda do
103
- require 'mathn'
104
- column.type_cast("Nl%\000\300Z\252\003")
105
- end.call.should == DateTime.parse("2002-10-10T17:04:56+00:00")
106
+ if ruby_supports_mathn?
107
+ context 'when requiring mathn' do
108
+ it "casts to DateTime" do
109
+ expect do
110
+ require 'mathn'
111
+ column.type_cast("Nl%\000\300Z\252\003")
112
+ end.call.should == DateTime.parse("2002-10-10T17:04:56+00:00")
113
+ end
106
114
  end
107
115
  end
108
116
 
@@ -167,12 +175,6 @@ describe DBF::Column::Dbase do
167
175
  column.schema_definition.should == "\"column_name\", :integer\n"
168
176
  end
169
177
  end
170
-
171
- context "when non-Foxpro dbf" do
172
- it "outputs a text column" do
173
-
174
- end
175
- end
176
178
  end
177
179
 
178
180
  it "defines a float colmn if type is (N)umber with more than 0 decimals" do
@@ -2,179 +2,153 @@ require "spec_helper"
2
2
 
3
3
  shared_examples_for 'DBF' do
4
4
  specify "sum of column lengths should equal record length specified in header plus one" do
5
- header_record_length = @table.instance_eval {@record_length}
6
- sum_of_column_lengths = @table.columns.inject(1) {|sum, column| sum += column.length}
5
+ header_record_length = table.instance_eval {@record_length}
6
+ sum_of_column_lengths = table.columns.inject(1) {|sum, column| sum += column.length}
7
7
 
8
8
  header_record_length.should == sum_of_column_lengths
9
9
  end
10
10
 
11
11
  specify "records should be instances of DBF::Record" do
12
- @table.all? {|record| record.should be_an_instance_of(DBF::Record)}
12
+ table.all? {|record| record.is_a?(DBF::Record)}.should be_true
13
13
  end
14
14
 
15
15
  specify "record count should be the same as reported in the header" do
16
- @table.entries.size.should == @table.record_count
16
+ table.entries.size.should == table.record_count
17
17
  end
18
18
 
19
19
  specify "column names should not be blank" do
20
- @table.columns.all? {|column| column.name.should_not be_empty}
20
+ table.columns.all? {|column| !column.name.empty?}.should be_true
21
21
  end
22
22
 
23
23
  specify "column types should be valid" do
24
24
  valid_column_types = %w(C N L D M F B G P Y T I V X @ O + 0)
25
- @table.columns.all? {|column| valid_column_types.should include(column.type)}
25
+ table.columns.all? {|column| valid_column_types.include?(column.type)}.should be_true
26
26
  end
27
27
 
28
28
  specify "column lengths should be instances of Fixnum" do
29
- @table.columns.all? {|column| column.length.should be_an_instance_of(Fixnum)}
29
+ table.columns.all? {|column| column.length.is_a?(Fixnum)}.should be_true
30
30
  end
31
31
 
32
32
  specify "column lengths should be larger than 0" do
33
- @table.columns.all? {|column| column.length.should > 0}
33
+ table.columns.all? {|column| column.length > 0}.should be_true
34
34
  end
35
35
 
36
36
  specify "column decimals should be instances of Fixnum" do
37
- @table.columns.all? {|column| column.decimal.should be_an_instance_of(Fixnum)}
38
- end
39
-
40
- specify "column read accessors should return the attribute after typecast" do
41
- @table.columns do |column|
42
- record = @table.records.first
43
- record.send(column.name).should == record[column.name]
44
- end
45
- end
46
-
47
- specify "column attributes should be accessible in underscored form" do
48
- @table.columns do |column|
49
- record = @table.records.first
50
- record.send(column_name).should == record.send(Util.underscore(column_name))
51
- end
37
+ table.columns.all? {|column| column.decimal.is_a?(Fixnum)}.should be_true
52
38
  end
53
39
  end
54
40
 
55
41
  shared_examples_for 'Foxpro DBF' do
56
42
  specify "columns should be instances of DBF::FoxproColumn" do
57
- @table.columns.all? {|column| column.should be_an_instance_of(DBF::Column::Foxpro)}
43
+ table.columns.all? {|column| column.is_a?(DBF::Column::Foxpro)}.should be_true
58
44
  end
59
45
  end
60
46
 
61
47
  describe DBF, "of type 03 (dBase III without memo file)" do
62
- before do
63
- @table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
64
- end
48
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_03.dbf" }
65
49
 
66
50
  it_should_behave_like "DBF"
67
51
 
68
52
  it "should report the correct version number" do
69
- @table.version.should == "03"
53
+ table.version.should == "03"
70
54
  end
71
55
 
72
56
  it "should report the correct version description" do
73
- @table.version_description.should == "dBase III without memo file"
57
+ table.version_description.should == "dBase III without memo file"
74
58
  end
75
59
 
76
60
  it "should determine the number of records" do
77
- @table.record_count.should == 14
61
+ table.record_count.should == 14
78
62
  end
79
63
  end
80
64
 
81
65
  describe DBF, "of type 30 (Visual FoxPro)" do
82
- before do
83
- @table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
84
- end
66
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_30.dbf" }
85
67
 
86
68
  it_should_behave_like "DBF"
87
69
 
88
70
  it "should report the correct version number" do
89
- @table.version.should == "30"
71
+ table.version.should == "30"
90
72
  end
91
73
 
92
74
  it "should report the correct version description" do
93
- @table.version_description.should == "Visual FoxPro"
75
+ table.version_description.should == "Visual FoxPro"
94
76
  end
95
77
 
96
78
  it "should determine the number of records" do
97
- @table.record_count.should == 34
79
+ table.record_count.should == 34
98
80
  end
99
81
  end
100
82
 
101
83
  describe DBF, "of type 31 (Visual FoxPro with AutoIncrement field)" do
102
- before do
103
- @table = DBF::Table.new "#{DB_PATH}/dbase_31.dbf"
104
- end
84
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_31.dbf" }
105
85
 
106
86
  it_should_behave_like "DBF"
107
87
 
108
88
  it "should have a dBase version of 31" do
109
- @table.version.should == "31"
89
+ table.version.should == "31"
110
90
  end
111
91
 
112
92
  it "should report the correct version description" do
113
- @table.version_description.should == "Visual FoxPro with AutoIncrement field"
93
+ table.version_description.should == "Visual FoxPro with AutoIncrement field"
114
94
  end
115
95
 
116
96
  it "should determine the number of records" do
117
- @table.record_count.should == 77
97
+ table.record_count.should == 77
118
98
  end
119
99
  end
120
100
 
121
101
  describe DBF, "of type 83 (dBase III with memo file)" do
122
- before do
123
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
124
- end
102
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_83.dbf" }
125
103
 
126
104
  it_should_behave_like "DBF"
127
105
 
128
106
  it "should report the correct version number" do
129
- @table.version.should == "83"
107
+ table.version.should == "83"
130
108
  end
131
109
 
132
110
  it "should report the correct version description" do
133
- @table.version_description.should == "dBase III with memo file"
111
+ table.version_description.should == "dBase III with memo file"
134
112
  end
135
113
 
136
114
  it "should determine the number of records" do
137
- @table.record_count.should == 67
115
+ table.record_count.should == 67
138
116
  end
139
117
  end
140
118
 
141
119
  describe DBF, "of type 8b (dBase IV with memo file)" do
142
- before do
143
- @table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
144
- end
120
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_8b.dbf" }
145
121
 
146
122
  it_should_behave_like "DBF"
147
123
 
148
124
  it "should report the correct version number" do
149
- @table.version.should == "8b"
125
+ table.version.should == "8b"
150
126
  end
151
127
 
152
128
  it "should report the correct version description" do
153
- @table.version_description.should == "dBase IV with memo file"
129
+ table.version_description.should == "dBase IV with memo file"
154
130
  end
155
131
 
156
132
  it "should determine the number of records" do
157
- @table.record_count.should == 10
133
+ table.record_count.should == 10
158
134
  end
159
135
  end
160
136
 
161
137
  describe DBF, "of type f5 (FoxPro with memo file)" do
162
- before do
163
- @table = DBF::Table.new "#{DB_PATH}/dbase_f5.dbf"
164
- end
138
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_f5.dbf" }
165
139
 
166
140
  it_should_behave_like "DBF"
167
141
  it_should_behave_like "Foxpro DBF"
168
142
 
169
143
  it "should report the correct version number" do
170
- @table.version.should == "f5"
144
+ table.version.should == "f5"
171
145
  end
172
146
 
173
147
  it "should report the correct version description" do
174
- @table.version_description.should == "FoxPro with memo file"
148
+ table.version_description.should == "FoxPro with memo file"
175
149
  end
176
150
 
177
151
  it "should determine the number of records" do
178
- @table.record_count.should == 975
152
+ table.record_count.should == 975
179
153
  end
180
- end
154
+ end
@@ -14,31 +14,40 @@ describe DBF::Record do
14
14
  end
15
15
  end
16
16
 
17
- describe '#==' do
18
- before do
17
+ describe '#==' do
18
+ let :record do
19
19
  table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
20
- @record = table.record(9)
20
+ table.record(9)
21
21
  end
22
22
 
23
23
  it 'should be false if other does not have attributes' do
24
- (@record == mock('other')).should be_false
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
- @record.stub!(:attributes).and_return(attributes)
29
+ record.stub!(:attributes).and_return(attributes)
30
30
  other = mock('object', :attributes => attributes)
31
- (@record == other).should be_true
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
+ let(:record) { table.find(0) }
37
38
 
38
- it 'should define accessor methods for each column' do
39
- record = table.find(0)
39
+ it 'should have dynamic accessors for the columns' do
40
40
  record.should respond_to(:character)
41
41
  record.character.should == 'One'
42
+ record.float.should == 1.23456789012346
43
+ record.logical.should == true
44
+ end
45
+
46
+ it 'should not define accessor methods on the base class' do
47
+ second_table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
48
+ second_record = second_table.find(0)
49
+ record.character.should == 'One'
50
+ expect { second_record.character }.to raise_error(NoMethodError)
42
51
  end
43
52
  end
44
53
 
@@ -6,20 +6,20 @@ describe DBF::Table do
6
6
  end
7
7
 
8
8
  describe '#initialize' do
9
- it 'should accept a DBF filename' do
9
+ it 'accepts a DBF filename' do
10
10
  expect { DBF::Table.new "#{DB_PATH}/dbase_83.dbf" }.to_not raise_error
11
11
  end
12
12
 
13
- it 'should accept a DBF and Memo filename' do
13
+ it 'accepts a DBF and Memo filename' do
14
14
  expect { DBF::Table.new "#{DB_PATH}/dbase_83.dbf", "#{DB_PATH}/dbase_83.dbt" }.to_not raise_error
15
15
  end
16
16
 
17
- it 'should accept an io-like data object' do
17
+ it 'accepts an io-like data object' do
18
18
  data = StringIO.new File.read("#{DB_PATH}/dbase_83.dbf")
19
19
  expect { DBF::Table.new data }.to_not raise_error
20
20
  end
21
21
 
22
- it 'should accept an io-like data and memo object' do
22
+ it 'accepts an io-like data and memo object' do
23
23
  data = StringIO.new File.read("#{DB_PATH}/dbase_83.dbf")
24
24
  memo = StringIO.new File.read("#{DB_PATH}/dbase_83.dbt")
25
25
  expect { DBF::Table.new data, memo }.to_not raise_error
@@ -27,17 +27,16 @@ describe DBF::Table do
27
27
  end
28
28
 
29
29
  context "when closed" do
30
- before do
31
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
32
- @table.close
33
- end
34
-
35
- it "should close the data file" do
36
- @table.instance_eval { @data }.should be_closed
30
+ it "closes the data and memo files" do
31
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
32
+ table.close
33
+ table.should be_closed
37
34
  end
38
35
 
39
- it "should close the memo file" do
40
- @table.instance_eval { @memo }.instance_eval { @data }.should be_closed
36
+ it "closes the data" do
37
+ table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
38
+ table.close
39
+ table.should be_closed
41
40
  end
42
41
  end
43
42
 
@@ -77,143 +76,122 @@ describe DBF::Table do
77
76
  end
78
77
 
79
78
  describe "#record" do
80
- before do
81
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
82
- end
83
-
84
79
  it "return nil for deleted records" do
85
- @table.stub!(:deleted_record?).and_return(true)
86
- @table.record(5).should be_nil
80
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
81
+ table.stub!(:deleted_record?).and_return(true)
82
+ table.record(5).should be_nil
87
83
  end
88
84
  end
89
85
 
90
86
  describe "#current_record" do
91
- before do
92
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
93
- end
94
-
95
87
  it "should return nil for deleted records" do
96
- @table.stub!(:deleted_record?).and_return(true)
97
- @table.record(0).should be_nil
88
+ table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
89
+ table.stub!(:deleted_record?).and_return(true)
90
+ table.record(0).should be_nil
98
91
  end
99
92
  end
100
93
 
101
94
  describe "#find" do
95
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_83.dbf" }
96
+
102
97
  describe "with index" do
103
- before do
104
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
105
- end
106
-
107
98
  it "should return the correct record" do
108
- @table.find(5).should == @table.record(5)
99
+ table.find(5).should == table.record(5)
109
100
  end
110
101
  end
111
102
 
112
- describe 'with array of indexes' do
113
- before do
114
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
115
- end
116
-
103
+ describe 'with array of indexes' do
117
104
  it "should return the correct records" do
118
- @table.find([1, 5, 10]).should == [@table.record(1), @table.record(5), @table.record(10)]
105
+ table.find([1, 5, 10]).should == [table.record(1), table.record(5), table.record(10)]
119
106
  end
120
107
  end
121
108
 
122
109
  describe "with :all" do
123
- before do
124
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
125
- end
126
-
127
110
  it "should accept a block" do
128
111
  records = []
129
- @table.find(:all, :weight => 0.0) do |record|
112
+ table.find(:all, :weight => 0.0) do |record|
130
113
  records << record
131
114
  end
132
- records.should == @table.find(:all, :weight => 0.0)
115
+ records.should == table.find(:all, :weight => 0.0)
133
116
  end
134
117
 
135
118
  it "should return all records if options are empty" do
136
- @table.find(:all).should == @table.to_a
119
+ table.find(:all).should == table.to_a
137
120
  end
138
121
 
139
122
  it "should return matching records when used with options" do
140
- @table.find(:all, "WEIGHT" => 0.0).should == @table.select {|r| r.attributes["weight"] == 0.0}
123
+ table.find(:all, "WEIGHT" => 0.0).should == table.select {|r| r["weight"] == 0.0}
141
124
  end
142
125
 
143
126
  it "should AND multiple search terms" do
144
- @table.find(:all, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should == []
127
+ table.find(:all, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should == []
145
128
  end
146
129
 
147
130
  it "should match original column names" do
148
- @table.find(:all, "WEIGHT" => 0.0).should_not be_empty
131
+ table.find(:all, "WEIGHT" => 0.0).should_not be_empty
149
132
  end
150
133
 
151
134
  it "should match symbolized column names" do
152
- @table.find(:all, :WEIGHT => 0.0).should_not be_empty
135
+ table.find(:all, :WEIGHT => 0.0).should_not be_empty
153
136
  end
154
137
 
155
138
  it "should match downcased column names" do
156
- @table.find(:all, "weight" => 0.0).should_not be_empty
139
+ table.find(:all, "weight" => 0.0).should_not be_empty
157
140
  end
158
141
 
159
142
  it "should match symbolized downcased column names" do
160
- @table.find(:all, :weight => 0.0).should_not be_empty
143
+ table.find(:all, :weight => 0.0).should_not be_empty
161
144
  end
162
145
  end
163
146
 
164
147
  describe "with :first" do
165
- before do
166
- @table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
167
- end
168
-
169
148
  it "should return the first record if options are empty" do
170
- @table.find(:first).should == @table.record(0)
149
+ table.find(:first).should == table.record(0)
171
150
  end
172
151
 
173
152
  it "should return the first matching record when used with options" do
174
- @table.find(:first, "CODE" => "C").should == @table.record(5)
153
+ table.find(:first, "CODE" => "C").should == table.record(5)
175
154
  end
176
155
 
177
156
  it "should AND multiple search terms" do
178
- @table.find(:first, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should be_nil
157
+ table.find(:first, "ID" => 30, "IMAGE" => "graphics/00000001/TBC01.jpg").should be_nil
179
158
  end
180
159
  end
181
160
  end
182
161
 
183
162
  describe "filename" do
184
- before do
185
- @table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
186
- end
187
-
188
163
  it 'should be dbase_03.dbf' do
189
- @table.filename.should == "dbase_03.dbf"
164
+ table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
165
+ table.filename.should == "dbase_03.dbf"
190
166
  end
191
167
  end
192
168
 
193
169
  describe 'has_memo_file?' do
194
170
  describe 'without a memo file' do
195
- let(:table) { DBF::Table.new "#{DB_PATH}/dbase_03.dbf" }
196
- specify { table.has_memo_file?.should be_false }
171
+ it 'returns false' do
172
+ table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
173
+ table.has_memo_file?.should be_false
174
+ end
197
175
  end
198
176
 
199
177
  describe 'with a memo file' do
200
- let(:table) { DBF::Table.new "#{DB_PATH}/dbase_30.dbf" }
201
- specify { table.has_memo_file?.should be_true }
178
+ it 'returns true' do
179
+ table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
180
+ table.has_memo_file?.should be_true
181
+ end
202
182
  end
203
183
  end
204
184
 
205
185
  describe 'columns' do
206
- before do
207
- @table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
208
- end
186
+ let(:table) { DBF::Table.new "#{DB_PATH}/dbase_03.dbf" }
209
187
 
210
188
  it 'should have correct size' do
211
- @table.columns.size.should == 31
189
+ table.columns.size.should == 31
212
190
  end
213
191
 
214
192
  it 'should have correct names' do
215
- @table.columns.first.name.should == 'Point_ID'
216
- @table.columns[29].name.should == 'Easting'
193
+ table.columns.first.name.should == 'Point_ID'
194
+ table.columns[29].name.should == 'Easting'
217
195
  end
218
196
  end
219
197
  end
data/spec/spec_helper.rb CHANGED
@@ -12,5 +12,11 @@ if RUBY_VERSION == "1.8.6"
12
12
  end
13
13
 
14
14
  RSpec.configure do |config|
15
-
15
+ def ruby_supports_mathn?
16
+ begin
17
+ require 'mathn'
18
+ rescue UnsupportedLibraryError
19
+ false
20
+ end
21
+ end
16
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.8
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -78,18 +78,16 @@ files:
78
78
  - README.md
79
79
  - bin/dbf
80
80
  - docs/supported_types.markdown
81
- - lib/dbf/attributes.rb
82
81
  - lib/dbf/column/base.rb
83
82
  - lib/dbf/column/dbase.rb
84
83
  - lib/dbf/column/foxpro.rb
85
- - lib/dbf/encodings.yml
86
84
  - lib/dbf/memo/base.rb
87
85
  - lib/dbf/memo/dbase3.rb
88
86
  - lib/dbf/memo/dbase4.rb
89
87
  - lib/dbf/memo/foxpro.rb
90
88
  - lib/dbf/record.rb
89
+ - lib/dbf/table/encodings.rb
91
90
  - lib/dbf/table.rb
92
- - lib/dbf/util.rb
93
91
  - lib/dbf/version.rb
94
92
  - lib/dbf.rb
95
93
  - spec/dbf/column_spec.rb
@@ -1,6 +0,0 @@
1
- class Attributes < Hash
2
- def []=(key, value)
3
- merge!(key => value)
4
- merge!(Util.underscore(key) => value)
5
- end
6
- end
@@ -1,61 +0,0 @@
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/util.rb DELETED
@@ -1,9 +0,0 @@
1
- class Util
2
- def self.underscore(string) #nodoc
3
- string.gsub(/::/, '/').
4
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
5
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
6
- tr('-', '_').
7
- downcase
8
- end
9
- end