dbf 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe DBF::Database::Foxpro do
4
- let(:dbf_path) { fixture_path('foxprodb/FOXPRO-DB-TEST.DBC') }
3
+ RSpec.describe DBF::Database::Foxpro do
4
+ let(:dbf_path) { fixture('foxprodb/FOXPRO-DB-TEST.DBC') }
5
5
  let(:db) { DBF::Database::Foxpro.new(dbf_path) }
6
6
 
7
7
  describe '#initialize' do
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- shared_examples_for 'DBF' do
3
+ RSpec.shared_examples_for 'DBF' do
4
4
  specify "sum of column lengths should equal record length specified in header plus one" do
5
5
  header_record_length = table.instance_eval {@header.record_length}
6
6
  sum_of_column_lengths = table.columns.inject(1) {|sum, column| sum += column.length}
@@ -50,16 +50,8 @@ shared_examples_for 'DBF' do
50
50
  end
51
51
  end
52
52
 
53
- shared_examples_for 'Foxpro DBF' do
54
- specify "columns should be instances of DBF::FoxproColumn" do
55
- table.columns.each do |column|
56
- expect(column).to be_kind_of(DBF::Column::Foxpro)
57
- end
58
- end
59
- end
60
-
61
- describe DBF, "of type 03 (dBase III without memo file)" do
62
- let(:table) { DBF::Table.new fixture_path('dbase_03.dbf') }
53
+ RSpec.describe DBF, "of type 03 (dBase III without memo file)" do
54
+ let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
63
55
 
64
56
  it_should_behave_like "DBF"
65
57
 
@@ -76,8 +68,8 @@ describe DBF, "of type 03 (dBase III without memo file)" do
76
68
  end
77
69
  end
78
70
 
79
- describe DBF, "of type 30 (Visual FoxPro)" do
80
- let(:table) { DBF::Table.new fixture_path('dbase_30.dbf') }
71
+ RSpec.describe DBF, "of type 30 (Visual FoxPro)" do
72
+ let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
81
73
 
82
74
  it_should_behave_like "DBF"
83
75
 
@@ -98,8 +90,8 @@ describe DBF, "of type 30 (Visual FoxPro)" do
98
90
  end
99
91
  end
100
92
 
101
- describe DBF, "of type 31 (Visual FoxPro with AutoIncrement field)" do
102
- let(:table) { DBF::Table.new fixture_path('dbase_31.dbf') }
93
+ RSpec.describe DBF, "of type 31 (Visual FoxPro with AutoIncrement field)" do
94
+ let(:table) { DBF::Table.new fixture('dbase_31.dbf') }
103
95
 
104
96
  it_should_behave_like "DBF"
105
97
 
@@ -116,8 +108,8 @@ describe DBF, "of type 31 (Visual FoxPro with AutoIncrement field)" do
116
108
  end
117
109
  end
118
110
 
119
- describe DBF, "of type 83 (dBase III with memo file)" do
120
- let(:table) { DBF::Table.new fixture_path('dbase_83.dbf') }
111
+ RSpec.describe DBF, "of type 83 (dBase III with memo file)" do
112
+ let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
121
113
 
122
114
  it_should_behave_like "DBF"
123
115
 
@@ -134,8 +126,8 @@ describe DBF, "of type 83 (dBase III with memo file)" do
134
126
  end
135
127
  end
136
128
 
137
- describe DBF, "of type 8b (dBase IV with memo file)" do
138
- let(:table) { DBF::Table.new fixture_path('dbase_8b.dbf') }
129
+ RSpec.describe DBF, "of type 8b (dBase IV with memo file)" do
130
+ let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
139
131
 
140
132
  it_should_behave_like "DBF"
141
133
 
@@ -152,11 +144,10 @@ describe DBF, "of type 8b (dBase IV with memo file)" do
152
144
  end
153
145
  end
154
146
 
155
- describe DBF, "of type f5 (FoxPro with memo file)" do
156
- let(:table) { DBF::Table.new fixture_path('dbase_f5.dbf') }
147
+ RSpec.describe DBF, "of type f5 (FoxPro with memo file)" do
148
+ let(:table) { DBF::Table.new fixture('dbase_f5.dbf') }
157
149
 
158
150
  it_should_behave_like "DBF"
159
- it_should_behave_like "Foxpro DBF"
160
151
 
161
152
  it "should report the correct version number" do
162
153
  expect(table.version).to eq "f5"
@@ -1,11 +1,10 @@
1
1
  require "spec_helper"
2
2
 
3
- describe DBF::Record do
4
-
3
+ RSpec.describe DBF::Record do
5
4
  describe '#to_a' do
6
- let(:table) { DBF::Table.new fixture_path('dbase_83.dbf') }
7
- let(:record_0) { YAML.load_file(fixture_path('dbase_83_record_0.yml')) }
8
- let(:record_9) { YAML.load_file(fixture_path('dbase_83_record_9.yml')) }
5
+ let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
6
+ let(:record_0) { YAML.load_file(fixture('dbase_83_record_0.yml')) }
7
+ let(:record_9) { YAML.load_file(fixture('dbase_83_record_9.yml')) }
9
8
 
10
9
  it 'should return an ordered array of attribute values' do
11
10
  record = table.record(0)
@@ -17,8 +16,8 @@ describe DBF::Record do
17
16
 
18
17
  describe 'with missing memo file' do
19
18
  describe 'when opening a path' do
20
- let(:table) { DBF::Table.new fixture_path('dbase_83_missing_memo.dbf') }
21
- let(:record_0) { YAML.load_file(fixture_path('dbase_83_missing_memo_record_0.yml')) }
19
+ let(:table) { DBF::Table.new fixture('dbase_83_missing_memo.dbf') }
20
+ let(:record_0) { YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml')) }
22
21
 
23
22
  it 'returns nil values for memo fields' do
24
23
  record = table.record(0)
@@ -28,9 +27,9 @@ describe DBF::Record do
28
27
  end
29
28
 
30
29
  describe 'when opening StringIO' do
31
- let(:data) { StringIO.new(File.read(fixture_path('dbase_83_missing_memo.dbf'))) }
30
+ let(:data) { StringIO.new(File.read(fixture('dbase_83_missing_memo.dbf'))) }
32
31
  let(:table) { DBF::Table.new(data) }
33
- let(:record_0) { YAML.load_file(fixture_path('dbase_83_missing_memo_record_0.yml')) }
32
+ let(:record_0) { YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml')) }
34
33
 
35
34
  it 'returns nil values for memo fields' do
36
35
  record = table.record(0)
@@ -40,7 +39,7 @@ describe DBF::Record do
40
39
  end
41
40
 
42
41
  describe '#==' do
43
- let(:table) { DBF::Table.new fixture_path('dbase_8b.dbf') }
42
+ let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
44
43
  let(:record) { table.record(9) }
45
44
 
46
45
  describe 'when other does not have attributes' do
@@ -50,8 +49,8 @@ describe DBF::Record do
50
49
  end
51
50
 
52
51
  describe 'if other attributes match' do
53
- let(:attributes) { {:x => 1, :y => 2} }
54
- let(:other) { double('object', :attributes => attributes) }
52
+ let(:attributes) { {x: 1, y: 2} }
53
+ let(:other) { double('object', attributes: attributes) }
55
54
 
56
55
  before do
57
56
  allow(record).to receive(:attributes).and_return(attributes)
@@ -65,7 +64,7 @@ describe DBF::Record do
65
64
  end
66
65
 
67
66
  describe 'column accessors' do
68
- let(:table) { DBF::Table.new fixture_path('dbase_8b.dbf') }
67
+ let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
69
68
  let(:record) { table.find(0) }
70
69
 
71
70
  %w(character numerical date logical float memo).each do |column_name|
@@ -79,36 +78,32 @@ describe DBF::Record do
79
78
 
80
79
  describe 'column data for table' do
81
80
  describe 'using specified in dbf encoding' do
82
- let(:table) { DBF::Table.new fixture_path('cp1251.dbf') }
81
+ let(:table) { DBF::Table.new fixture('cp1251.dbf') }
83
82
  let(:record) { table.find(0) }
84
83
 
85
84
  it 'should automatically encodes to default system encoding' do
86
- if table.supports_string_encoding?
87
- expect(record.name.encoding).to eq Encoding.default_external
85
+ expect(record.name.encoding).to eq Encoding.default_external
88
86
 
89
- # russian a
90
- expect(record.name.encode("UTF-8").unpack("H4")).to eq ["d0b0"]
91
- end
87
+ # russian a
88
+ expect(record.name.encode("UTF-8").unpack("H4")).to eq ["d0b0"]
92
89
  end
93
90
  end
94
91
 
95
92
  describe 'overriding specified in dbf encoding' do
96
- let(:table) { DBF::Table.new fixture_path('cp1251.dbf'), nil, 'cp866'}
93
+ let(:table) { DBF::Table.new fixture('cp1251.dbf'), nil, 'cp866'}
97
94
  let(:record) { table.find(0) }
98
95
 
99
96
  it 'should transcode from manually specified encoding to default system encoding' do
100
- if table.supports_string_encoding?
101
- expect(record.name.encoding).to eq Encoding.default_external
97
+ expect(record.name.encoding).to eq Encoding.default_external
102
98
 
103
- # russian а encoded in cp1251 and read as if it was encoded in cp866
104
- expect(record.name.encode("UTF-8").unpack("H4")).to eq ["d180"]
105
- end
99
+ # russian а encoded in cp1251 and read as if it was encoded in cp866
100
+ expect(record.name.encode("UTF-8").unpack("H4")).to eq ["d180"]
106
101
  end
107
102
  end
108
103
  end
109
104
 
110
105
  describe '#attributes' do
111
- let(:table) { DBF::Table.new fixture_path('dbase_8b.dbf') }
106
+ let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
112
107
  let(:record) { table.find(0) }
113
108
 
114
109
  it 'is a hash of attribute name/value pairs' do
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe DBF::Table do
4
- let(:dbf_path) { fixture_path('dbase_83.dbf') }
5
- let(:memo_path) { fixture_path('dbase_83.dbt') }
3
+ RSpec.describe DBF::Table do
4
+ let(:dbf_path) { fixture('dbase_83.dbf') }
5
+ let(:memo_path) { fixture('dbase_83.dbt') }
6
6
  let(:table) { DBF::Table.new dbf_path }
7
7
 
8
8
  specify 'foxpro versions' do
@@ -10,6 +10,9 @@ describe DBF::Table do
10
10
  end
11
11
 
12
12
  describe '#initialize' do
13
+ let(:data) { StringIO.new File.read(dbf_path) }
14
+ let(:memo) { StringIO.new File.read(memo_path) }
15
+
13
16
  describe 'when given a path to an existing dbf file' do
14
17
  it 'does not raise an error' do
15
18
  expect { DBF::Table.new dbf_path }.to_not raise_error
@@ -29,27 +32,26 @@ describe DBF::Table do
29
32
  end
30
33
 
31
34
  it 'accepts an io-like data object' do
32
- data = StringIO.new File.read(dbf_path)
33
35
  expect { DBF::Table.new data }.to_not raise_error
34
36
  end
35
37
 
36
38
  it 'accepts an io-like data and memo object' do
37
- data = StringIO.new File.read(dbf_path)
38
- memo = StringIO.new File.read(memo_path)
39
39
  expect { DBF::Table.new data, memo }.to_not raise_error
40
40
  end
41
41
  end
42
42
 
43
43
  context '#close' do
44
+ before { table.close }
45
+
44
46
  it 'closes the io' do
45
- table.close
46
47
  expect { table.record(1) }.to raise_error(IOError)
47
48
  end
48
49
  end
49
50
 
50
51
  describe '#schema' do
52
+ let(:control_schema) { File.read(fixture('dbase_83_schema.txt')) }
53
+
51
54
  it 'matches the test schema fixture' do
52
- control_schema = File.read(fixture_path('dbase_83_schema.txt'))
53
55
  expect(table.schema).to eq control_schema
54
56
  end
55
57
  end
@@ -99,8 +101,9 @@ describe DBF::Table do
99
101
  end
100
102
 
101
103
  describe 'when path param passed' do
104
+ before { table.to_csv('test.csv') }
105
+
102
106
  it 'creates a custom csv file' do
103
- table.to_csv('test.csv')
104
107
  expect(File.exist?('test.csv')).to be_truthy
105
108
  end
106
109
  end
@@ -134,12 +137,14 @@ describe DBF::Table do
134
137
  end
135
138
 
136
139
  describe 'with :all' do
137
- it 'accepts a block' do
138
- records = []
139
- table.find(:all, :weight => 0.0) do |record|
140
+ let(:records) do
141
+ table.find(:all, weight: 0.0).inject([]) do |records, record|
140
142
  records << record
141
143
  end
142
- expect(records).to eq table.find(:all, :weight => 0.0)
144
+ end
145
+
146
+ it 'accepts a block' do
147
+ expect(records).to eq table.find(:all, weight: 0.0)
143
148
  end
144
149
 
145
150
  it 'returns all records if options are empty' do
@@ -194,8 +199,9 @@ describe DBF::Table do
194
199
 
195
200
  describe '#has_memo_file?' do
196
201
  describe 'without a memo file' do
202
+ let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
203
+
197
204
  it 'is false' do
198
- table = DBF::Table.new fixture_path('dbase_03.dbf')
199
205
  expect(table.has_memo_file?).to be_falsey
200
206
  end
201
207
  end
@@ -208,18 +214,22 @@ describe DBF::Table do
208
214
  end
209
215
 
210
216
  describe '#columns' do
217
+ let(:columns) { table.columns }
218
+
211
219
  it 'is an array of Columns' do
212
- expect(table.columns).to be_an(Array)
213
- expect(table.columns).to_not be_empty
214
- expect(table.columns.all? {|c| c.class == DBF::Column::Dbase}).to be_truthy
220
+ expect(columns).to be_an(Array)
221
+ expect(columns).to_not be_empty
222
+ expect(columns.all? {|c| c.class == DBF::Column}).to be_truthy
215
223
  end
216
224
  end
217
225
 
218
226
  describe '#column_names' do
227
+ let(:column_names) do
228
+ %w(ID CATCOUNT AGRPCOUNT PGRPCOUNT ORDER CODE NAME THUMBNAIL IMAGE PRICE COST DESC WEIGHT TAXABLE ACTIVE)
229
+ end
230
+
219
231
  it 'is an array of all column names' do
220
- correct_column_names = %w(ID CATCOUNT AGRPCOUNT PGRPCOUNT ORDER CODE NAME THUMBNAIL IMAGE PRICE COST DESC WEIGHT TAXABLE ACTIVE)
221
- expect(table.column_names).to eq correct_column_names
232
+ expect(table.column_names).to eq column_names
222
233
  end
223
234
  end
224
-
225
235
  end
@@ -9,20 +9,24 @@ require 'yaml'
9
9
  require 'rspec'
10
10
  require 'fileutils'
11
11
 
12
- Encoding.default_external = "UTF-8" if defined?(Encoding)
12
+ RSpec.configure do |config|
13
+ config.disable_monkey_patching!
14
+ config.warnings = true
15
+ config.order = :random
13
16
 
14
- DB_PATH = File.dirname(__FILE__) + '/fixtures' unless defined?(DB_PATH)
17
+ config.expect_with :rspec do |expectations|
18
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
19
+ end
15
20
 
16
- RSpec.configure do |config|
17
- def ruby_supports_mathn?
18
- begin
19
- require 'mathn'
20
- rescue UnsupportedLibraryError
21
- false
22
- end
21
+ config.mock_with :rspec do |mocks|
22
+ mocks.verify_partial_doubles = true
23
23
  end
24
24
  end
25
25
 
26
- def fixture_path(filename)
27
- "#{DB_PATH}/#{filename}"
26
+ def fixture_path
27
+ @fixture_path ||= File.join(File.dirname(__FILE__), '/fixtures')
28
+ end
29
+
30
+ def fixture(filename)
31
+ File.join(fixture_path, filename)
28
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbf
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keith Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-23 00:00:00.000000000 Z
11
+ date: 2015-11-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A small fast library for reading dBase, xBase, Clipper and FoxPro database
14
14
  files.
@@ -34,9 +34,8 @@ files:
34
34
  - docs/supported_encodings.csv
35
35
  - docs/supported_types.markdown
36
36
  - lib/dbf.rb
37
- - lib/dbf/column/base.rb
38
- - lib/dbf/column/dbase.rb
39
- - lib/dbf/column/foxpro.rb
37
+ - lib/dbf/column.rb
38
+ - lib/dbf/column_type.rb
40
39
  - lib/dbf/database/foxpro.rb
41
40
  - lib/dbf/encodings.rb
42
41
  - lib/dbf/header.rb
@@ -111,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
110
  version: 1.3.0
112
111
  requirements: []
113
112
  rubyforge_project:
114
- rubygems_version: 2.4.6
113
+ rubygems_version: 2.4.5.1
115
114
  signing_key:
116
115
  specification_version: 4
117
116
  summary: Read xBase files
@@ -1,199 +0,0 @@
1
- module DBF
2
- module Column
3
- # Raised if length is less than 1
4
- class LengthError < StandardError; end
5
-
6
- # Raised if name is empty
7
- class NameError < StandardError; end
8
-
9
- class Base
10
- attr_reader :table, :name, :type, :length, :decimal
11
-
12
- # Initialize a new DBF::Column
13
- #
14
- # @param [String] name
15
- # @param [String] type
16
- # @param [Fixnum] length
17
- # @param [Fixnum] decimal
18
- def initialize(table, name, type, length, decimal)
19
- @table = table
20
- @name = clean(name)
21
- @type = type
22
- @length = length
23
- @decimal = decimal
24
- @version = table.version
25
- @encoding = table.encoding
26
-
27
- validate_length
28
- validate_name
29
- end
30
-
31
- # Cast value to native type
32
- #
33
- # @param [String] value
34
- # @return [Fixnum, Float, Date, DateTime, Boolean, String]
35
- def type_cast(value)
36
- return nil if length == 0
37
-
38
- meth = type_cast_methods[type]
39
- meth ? send(meth, value) : encode_string(value, true)
40
- end
41
-
42
- # Returns true if the column is a memo
43
- #
44
- # @return [Boolean]
45
- def memo?
46
- @memo ||= type == 'M'
47
- end
48
-
49
- # Schema definition
50
- #
51
- # @return [String]
52
- def schema_definition
53
- "\"#{underscored_name}\", #{schema_data_type}\n"
54
- end
55
-
56
- # Underscored name
57
- #
58
- # This is the column name converted to underscore format.
59
- # For example, MyColumn will be returned as my_column.
60
- #
61
- # @return [String]
62
- def underscored_name
63
- @underscored_name ||= begin
64
- un = name.dup
65
- un.gsub!(/::/, '/')
66
- un.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
67
- un.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
68
- un.tr!('-', '_')
69
- un.downcase!
70
- un
71
- end
72
- end
73
-
74
- def to_hash
75
- {name: name, type: type, length: length, decimal: decimal}
76
- end
77
-
78
- private
79
-
80
- def type_cast_methods # nodoc
81
- {
82
- 'N' => :unpack_number,
83
- 'I' => :unpack_signed_long,
84
- 'F' => :unpack_float,
85
- 'Y' => :unpack_currency,
86
- 'D' => :decode_date,
87
- 'T' => :decode_datetime,
88
- 'L' => :boolean,
89
- 'M' => :decode_memo,
90
- 'B' => :unpack_double
91
- }
92
- end
93
-
94
- def decode_date(value) # nodoc
95
- v = value.tr(' ', '0')
96
- v !~ /\S/ ? nil : Date.parse(v)
97
- rescue
98
- nil
99
- end
100
-
101
- def decode_datetime(value) # nodoc
102
- days, msecs = value.unpack('l2')
103
- secs = (msecs / 1000).to_i
104
- DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60)
105
- rescue
106
- nil
107
- end
108
-
109
- def decode_memo(value) # nodoc
110
- value && encode_string(value)
111
- end
112
-
113
- def unpack_number(value) # nodoc
114
- decimal.zero? ? value.to_i : value.to_f
115
- end
116
-
117
- def unpack_currency(value) # nodoc
118
- (value.unpack('q<')[0] / 10_000.0).to_f
119
- end
120
-
121
- def unpack_signed_long(value) # nodoc
122
- value.unpack('l<')[0]
123
- end
124
-
125
- def unpack_float(value) # nodoc
126
- value.to_f
127
- end
128
-
129
- def unpack_double(value) # nodoc
130
- value.unpack('E')[0]
131
- end
132
-
133
- def boolean(value) # nodoc
134
- value.strip =~ /^(y|t)$/i ? true : false
135
- end
136
-
137
- def encode_string(value, strip_output = false) # nodoc
138
- output =
139
- if supports_encoding? && table.supports_string_encoding?
140
- value.to_s.force_encoding(@encoding).encode(*encoding_args)
141
- elsif supports_encoding? && table.supports_iconv?
142
- Iconv.conv('UTF-8', @encoding, value.to_s)
143
- else
144
- value
145
- end
146
-
147
- strip_output ? output.strip : output
148
- end
149
-
150
- def encoding_args # nodoc
151
- [Encoding.default_external, {:undef => :replace, :invalid => :replace}]
152
- end
153
-
154
- def schema_data_type # nodoc
155
- case type
156
- when 'N', 'F'
157
- decimal > 0 ? ':float' : ':integer'
158
- when 'I'
159
- ':integer'
160
- when 'Y'
161
- ':decimal, :precision => 15, :scale => 4'
162
- when 'D'
163
- ':date'
164
- when 'T'
165
- ':datetime'
166
- when 'L'
167
- ':boolean'
168
- when 'M'
169
- ':text'
170
- when 'B'
171
- if DBF::Table::FOXPRO_VERSIONS.keys.include?(@version)
172
- ':float'
173
- else
174
- ':text'
175
- end
176
- else
177
- ":string, :limit => #{length}"
178
- end
179
- end
180
-
181
- def clean(value) # nodoc
182
- truncated_value = value.strip.partition("\x00").first
183
- truncated_value.gsub(/[^\x20-\x7E]/, '')
184
- end
185
-
186
- def validate_length
187
- raise LengthError, 'field length must be 0 or greater' if length < 0
188
- end
189
-
190
- def validate_name
191
- raise NameError, 'column name cannot be empty' if @name.empty?
192
- end
193
-
194
- def supports_encoding?
195
- @encoding && table.supports_encoding?
196
- end
197
- end
198
- end
199
- end