dbf 1.7.8 → 2.0.0

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/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