dbf 3.1.1 → 3.1.2

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.
@@ -1,7 +1,7 @@
1
1
  module DBF
2
2
  module Memo
3
3
  class Dbase3 < Base
4
- def build_memo(start_block) # nodoc
4
+ def build_memo(start_block) # :nodoc:
5
5
  @data.seek offset(start_block)
6
6
  memo_string = ''
7
7
  loop do
@@ -1,7 +1,7 @@
1
1
  module DBF
2
2
  module Memo
3
3
  class Dbase4 < Base
4
- def build_memo(start_block) # nodoc
4
+ def build_memo(start_block) # :nodoc:
5
5
  @data.seek offset(start_block)
6
6
  @data.read(@data.read(BLOCK_HEADER_SIZE).unpack('x4L').first)
7
7
  end
@@ -3,7 +3,7 @@ module DBF
3
3
  class Foxpro < Base
4
4
  FPT_HEADER_SIZE = 512
5
5
 
6
- def build_memo(start_block) # nodoc
6
+ def build_memo(start_block) # :nodoc:
7
7
  @data.seek offset(start_block)
8
8
 
9
9
  memo_type, memo_size, memo_string = @data.read(block_size).unpack('NNa*')
@@ -22,7 +22,7 @@ module DBF
22
22
 
23
23
  private
24
24
 
25
- def block_size # nodoc
25
+ def block_size # :nodoc:
26
26
  @block_size ||= begin
27
27
  @data.rewind
28
28
  @data.read(FPT_HEADER_SIZE).unpack('x6n').first || 0
@@ -49,15 +49,6 @@ module DBF
49
49
  options.all? { |key, value| self[key] == value }
50
50
  end
51
51
 
52
- # Overrides standard Object.respond_to? to return true if a
53
- # matching column name is found.
54
- #
55
- # @param [String, Symbol] method
56
- # @return [Boolean]
57
- def respond_to?(method, *args)
58
- underscored_column_names.include?(method.to_s) || super
59
- end
60
-
61
52
  # Maps a row to an array of values
62
53
  #
63
54
  # @return [Array]
@@ -67,15 +58,15 @@ module DBF
67
58
 
68
59
  private
69
60
 
70
- def attribute_map # nodoc
61
+ def attribute_map # :nodoc:
71
62
  @columns.map { |column| [column.name, init_attribute(column)] }
72
63
  end
73
64
 
74
- def get_data(column) # nodoc
65
+ def get_data(column) # :nodoc:
75
66
  @data.read(column.length)
76
67
  end
77
68
 
78
- def get_memo(column) # nodoc
69
+ def get_memo(column) # :nodoc:
79
70
  if @memo
80
71
  @memo.get(memo_start_block(column))
81
72
  else
@@ -85,20 +76,18 @@ module DBF
85
76
  end
86
77
  end
87
78
 
88
- def init_attribute(column) # nodoc
79
+ def init_attribute(column) # :nodoc:
89
80
  value = column.memo? ? get_memo(column) : get_data(column)
90
81
  column.type_cast(value)
91
82
  end
92
83
 
93
- def memo_start_block(column) # nodoc
84
+ def memo_start_block(column) # :nodoc:
94
85
  data = get_data(column)
95
- if %w(30 31).include?(@version)
96
- data = data.unpack('V').first
97
- end
86
+ data = data.unpack('V').first if %w[30 31].include?(@version)
98
87
  data.to_i
99
88
  end
100
89
 
101
- def method_missing(method, *args) # nodoc
90
+ def method_missing(method, *args) # :nodoc:
102
91
  if (index = underscored_column_names.index(method.to_s))
103
92
  attributes[@columns[index].name]
104
93
  else
@@ -106,7 +95,11 @@ module DBF
106
95
  end
107
96
  end
108
97
 
109
- def underscored_column_names # nodoc
98
+ def respond_to_missing?(method, *) # :nodoc:
99
+ underscored_column_names.include?(method.to_s) || super
100
+ end
101
+
102
+ def underscored_column_names # :nodoc:
110
103
  @underscored_column_names ||= @columns.map(&:underscored_name)
111
104
  end
112
105
  end
@@ -1,6 +1,17 @@
1
1
  module DBF
2
2
  # The Schema module is mixin for the Table class
3
3
  module Schema
4
+ FORMATS = [:activerecord, :json, :sequel].freeze
5
+
6
+ OTHER_DATA_TYPES = {
7
+ 'Y' => ':decimal, :precision => 15, :scale => 4',
8
+ 'D' => ':date',
9
+ 'T' => ':datetime',
10
+ 'L' => ':boolean',
11
+ 'M' => ':text',
12
+ 'B' => ':binary'
13
+ }.freeze
14
+
4
15
  # Generate an ActiveRecord::Schema
5
16
  #
6
17
  # xBase data types are converted to generic types as follows:
@@ -24,15 +35,17 @@ module DBF
24
35
  # @param [Symbol] format Valid options are :activerecord and :json
25
36
  # @return [String]
26
37
  def schema(format = :activerecord, table_only = false)
27
- supported_formats = [:activerecord, :json, :sequel]
28
- if supported_formats.include?(format)
29
- send("#{format}_schema", table_only)
30
- else
31
- raise ArgumentError
32
- end
38
+ schema_method_name = schema_name(format)
39
+ send(schema_method_name, table_only)
40
+ rescue NameError
41
+ raise ArgumentError, ":#{format} is not a valid schema. Valid schemas are: #{FORMATS.join(', ')}."
42
+ end
43
+
44
+ def schema_name(format) # :nodoc:
45
+ "#{format}_schema"
33
46
  end
34
47
 
35
- def activerecord_schema(_table_only = false) # nodoc
48
+ def activerecord_schema(_table_only = false) # :nodoc:
36
49
  s = "ActiveRecord::Schema.define do\n"
37
50
  s << " create_table \"#{name}\" do |t|\n"
38
51
  columns.each do |column|
@@ -42,7 +55,7 @@ module DBF
42
55
  s
43
56
  end
44
57
 
45
- def sequel_schema(table_only = false) # nodoc
58
+ def sequel_schema(table_only = false) # :nodoc:
46
59
  s = ''
47
60
  s << "Sequel.migration do\n" unless table_only
48
61
  s << " change do\n " unless table_only
@@ -51,12 +64,12 @@ module DBF
51
64
  s << " column #{sequel_schema_definition(column)}"
52
65
  end
53
66
  s << " end\n"
54
- s << " end\n" unless table_only
55
- s << "end\n" unless table_only
67
+ s << " end\n" unless table_only
68
+ s << "end\n" unless table_only
56
69
  s
57
70
  end
58
71
 
59
- def json_schema(_table_only = false) # nodoc
72
+ def json_schema(_table_only = false) # :nodoc:
60
73
  columns.map(&:to_hash).to_json
61
74
  end
62
75
 
@@ -76,30 +89,26 @@ module DBF
76
89
  ":#{column.underscored_name}, #{schema_data_type(column, :sequel)}\n"
77
90
  end
78
91
 
79
- def schema_data_type(column, format = :activerecord) # nodoc
92
+ def schema_data_type(column, format = :activerecord) # :nodoc:
80
93
  case column.type
81
- when 'N', 'F'
82
- column.decimal > 0 ? ':float' : ':integer'
83
- when 'I'
84
- ':integer'
85
- when 'Y'
86
- ':decimal, :precision => 15, :scale => 4'
87
- when 'D'
88
- ':date'
89
- when 'T'
90
- ':datetime'
91
- when 'L'
92
- ':boolean'
93
- when 'M'
94
- ':text'
95
- when 'B'
96
- ':binary'
94
+ when *%w[N F I]
95
+ number_data_type(column)
96
+ when *%w[Y D T L M B]
97
+ OTHER_DATA_TYPES[column.type]
98
+ else
99
+ string_data_format(format, column)
100
+ end
101
+ end
102
+
103
+ def number_data_type(column)
104
+ column.decimal > 0 ? ':float' : ':integer'
105
+ end
106
+
107
+ def string_data_format(format, column)
108
+ if format == :sequel
109
+ ":varchar, :size => #{column.length}"
97
110
  else
98
- if format == :sequel
99
- ":varchar, :size => #{column.length}"
100
- else
101
- ":string, :limit => #{column.length}"
102
- end
111
+ ":string, :limit => #{column.length}"
103
112
  end
104
113
  end
105
114
  end
@@ -28,14 +28,14 @@ module DBF
28
28
  'cb' => 'dBASE IV SQL table files, with memo',
29
29
  'f5' => 'FoxPro with memo file',
30
30
  'fb' => 'FoxPro without memo file'
31
- }
31
+ }.freeze
32
32
 
33
33
  FOXPRO_VERSIONS = {
34
34
  '30' => 'Visual FoxPro',
35
35
  '31' => 'Visual FoxPro with AutoIncrement field',
36
36
  'f5' => 'FoxPro with memo file',
37
37
  'fb' => 'FoxPro without memo file'
38
- }
38
+ }.freeze
39
39
 
40
40
  attr_accessor :encoding
41
41
  attr_writer :name
@@ -157,7 +157,7 @@ module DBF
157
157
 
158
158
  # @return [String]
159
159
  def name
160
- @name ||= filename && File.basename(filename, ".*")
160
+ @name ||= filename && File.basename(filename, '.*')
161
161
  end
162
162
 
163
163
  # Retrieve a record by index number.
@@ -172,7 +172,7 @@ module DBF
172
172
  DBF::Record.new(@data.read(header.record_length), columns, version, @memo)
173
173
  end
174
174
 
175
- alias_method :row, :record
175
+ alias row record
176
176
 
177
177
  # Total number of records
178
178
  #
@@ -208,30 +208,29 @@ module DBF
208
208
 
209
209
  private
210
210
 
211
- def build_columns # nodoc
212
- @data.seek(DBF_HEADER_SIZE)
213
- columns = []
214
- until end_of_record?
215
- column_data = @data.read(DBF_HEADER_SIZE)
216
- name, type, length, decimal = column_data.unpack('a10 x a x4 C2')
217
- columns << Column.new(self, name, type, length, decimal)
211
+ def build_columns # :nodoc:
212
+ safe_seek do
213
+ @data.seek(DBF_HEADER_SIZE)
214
+ columns = []
215
+ until end_of_record?
216
+ column_data = @data.read(DBF_HEADER_SIZE)
217
+ name, type, length, decimal = column_data.unpack('a10 x a x4 C2')
218
+ columns << Column.new(self, name, type, length, decimal)
219
+ end
220
+ columns
218
221
  end
219
- columns
220
222
  end
221
223
 
222
- def deleted_record? # nodoc
224
+ def deleted_record? # :nodoc:
223
225
  flag = @data.read(1)
224
226
  flag ? flag.unpack('a') == ['*'] : true
225
227
  end
226
228
 
227
- def end_of_record? # nodoc
228
- original_pos = @data.pos
229
- byte = @data.read(1)
230
- @data.seek(original_pos)
231
- byte.ord == 13
229
+ def end_of_record? # :nodoc:
230
+ safe_seek { @data.read(1).ord == 13 }
232
231
  end
233
232
 
234
- def find_all(options) # nodoc
233
+ def find_all(options) # :nodoc:
235
234
  map do |record|
236
235
  if record && record.match?(options)
237
236
  yield record if block_given?
@@ -240,19 +239,22 @@ module DBF
240
239
  end.compact
241
240
  end
242
241
 
243
- def find_first(options) # nodoc
242
+ def find_first(options) # :nodoc:
244
243
  detect { |record| record && record.match?(options) }
245
244
  end
246
245
 
247
- def foxpro? # nodoc
246
+ def foxpro? # :nodoc:
248
247
  FOXPRO_VERSIONS.keys.include? version
249
248
  end
250
249
 
251
- def header
252
- @header ||= Header.new(@data.read DBF_HEADER_SIZE)
250
+ def header # :nodoc:
251
+ @header ||= safe_seek do
252
+ @data.seek(0)
253
+ Header.new(@data.read DBF_HEADER_SIZE)
254
+ end
253
255
  end
254
256
 
255
- def memo_class # nodoc
257
+ def memo_class # :nodoc:
256
258
  @memo_class ||= begin
257
259
  if foxpro?
258
260
  Memo::Foxpro
@@ -262,33 +264,38 @@ module DBF
262
264
  end
263
265
  end
264
266
 
265
- def memo_search_path(io) # nodoc
267
+ def memo_search_path(io) # :nodoc:
266
268
  dirname = File.dirname(io)
267
269
  basename = File.basename(io, '.*')
268
270
  "#{dirname}/#{basename}*.{fpt,FPT,dbt,DBT}"
269
271
  end
270
272
 
271
- def open_data(data) # nodoc
273
+ def open_data(data) # :nodoc:
272
274
  data.is_a?(StringIO) ? data : File.open(data, 'rb')
273
275
  rescue Errno::ENOENT
274
276
  raise DBF::FileNotFoundError, "file not found: #{data}"
275
277
  end
276
278
 
277
- def open_memo(data, memo = nil) # nodoc
279
+ def open_memo(data, memo = nil) # :nodoc:
278
280
  if memo
279
281
  meth = memo.is_a?(StringIO) ? :new : :open
280
282
  memo_class.send(meth, memo, version)
281
283
  elsif !data.is_a?(StringIO)
282
- files = Dir.glob(memo_search_path data)
284
+ files = Dir.glob(memo_search_path(data))
283
285
  files.any? ? memo_class.open(files.first, version) : nil
284
286
  end
285
287
  end
286
288
 
287
- def seek(offset) # nodoc
289
+ def safe_seek # :nodoc:
290
+ original_pos = @data.pos
291
+ yield.tap { @data.seek(original_pos) }
292
+ end
293
+
294
+ def seek(offset) # :nodoc:
288
295
  @data.seek(header.header_length + offset)
289
296
  end
290
297
 
291
- def seek_to_record(index) # nodoc
298
+ def seek_to_record(index) # :nodoc:
292
299
  seek(index * header.record_length)
293
300
  end
294
301
  end
@@ -1,3 +1,3 @@
1
1
  module DBF
2
- VERSION = '3.1.1'
2
+ VERSION = '3.1.2'
3
3
  end
@@ -3,7 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe DBF::Column do
6
- let(:table) { DBF::Table.new fixture('dbase_30.dbf')}
6
+ let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
7
7
 
8
8
  context 'when initialized' do
9
9
  let(:column) { DBF::Column.new table, 'ColumnName', 'N', 1, 0 }
@@ -19,13 +19,13 @@ RSpec.describe DBF::Database::Foxpro do
19
19
 
20
20
  describe 'it loads the example db correctly' do
21
21
  it 'shows a correct list of tables' do
22
- expect(db.table_names.sort).to eq(%w(contacts calls setup types).sort)
22
+ expect(db.table_names.sort).to eq(%w[contacts calls setup types].sort)
23
23
  end
24
24
  end
25
25
  end
26
26
 
27
27
  describe '#table' do
28
- describe "when accessing related tables" do
28
+ describe 'when accessing related tables' do
29
29
  let(:db) { DBF::Database::Foxpro.new(dbf_path) }
30
30
 
31
31
  it 'loads an existing related table' do
@@ -1,167 +1,167 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  RSpec.shared_examples_for 'DBF' do
4
- specify "sum of column lengths should equal record length specified in header plus one" do
5
- header_record_length = table.instance_eval {@header.record_length}
6
- sum_of_column_lengths = table.columns.inject(1) {|sum, column| sum += column.length}
4
+ specify 'sum of column lengths should equal record length specified in header plus one' do
5
+ header_record_length = table.instance_eval { @header.record_length }
6
+ sum_of_column_lengths = table.columns.inject(1) { |sum, column| sum += column.length }
7
7
 
8
8
  expect(header_record_length).to eq sum_of_column_lengths
9
9
  end
10
10
 
11
- specify "records should be instances of DBF::Record" do
11
+ specify 'records should be instances of DBF::Record' do
12
12
  table.each do |record|
13
13
  expect(record).to be_kind_of(DBF::Record)
14
14
  end
15
15
  end
16
16
 
17
- specify "record count should be the same as reported in the header" do
17
+ specify 'record count should be the same as reported in the header' do
18
18
  expect(table.entries.size).to eq table.record_count
19
19
  end
20
20
 
21
- specify "column names should not be blank" do
21
+ specify 'column names should not be blank' do
22
22
  table.columns.each do |column|
23
23
  expect(column.name).not_to be_empty
24
24
  end
25
25
  end
26
26
 
27
- specify "column types should be valid" do
28
- valid_column_types = %w(C N L D M F B G P Y T I V X @ O + 0)
27
+ specify 'column types should be valid' do
28
+ valid_column_types = %w[C N L D M F B G P Y T I V X @ O + 0]
29
29
  table.columns.each do |column|
30
30
  expect(valid_column_types).to include(column.type)
31
31
  end
32
32
  end
33
33
 
34
- specify "column lengths should be instances of Integer" do
34
+ specify 'column lengths should be instances of Integer' do
35
35
  table.columns.each do |column|
36
36
  expect(column.length).to be_kind_of(Integer)
37
37
  end
38
38
  end
39
39
 
40
- specify "column lengths should be larger than 0" do
40
+ specify 'column lengths should be larger than 0' do
41
41
  table.columns.each do |column|
42
42
  expect(column.length).to be > 0
43
43
  end
44
44
  end
45
45
 
46
- specify "column decimals should be instances of Integer" do
46
+ specify 'column decimals should be instances of Integer' do
47
47
  table.columns.each do |column|
48
48
  expect(column.decimal).to be_kind_of(Integer)
49
49
  end
50
50
  end
51
51
  end
52
52
 
53
- RSpec.describe DBF, "of type 03 (dBase III without memo file)" do
53
+ RSpec.describe DBF, 'of type 03 (dBase III without memo file)' do
54
54
  let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
55
55
 
56
- it_should_behave_like "DBF"
56
+ it_should_behave_like 'DBF'
57
57
 
58
- it "should report the correct version number" do
59
- expect(table.version).to eq "03"
58
+ it 'should report the correct version number' do
59
+ expect(table.version).to eq '03'
60
60
  end
61
61
 
62
- it "should report the correct version description" do
63
- expect(table.version_description).to eq "dBase III without memo file"
62
+ it 'should report the correct version description' do
63
+ expect(table.version_description).to eq 'dBase III without memo file'
64
64
  end
65
65
 
66
- it "should determine the number of records" do
66
+ it 'should determine the number of records' do
67
67
  expect(table.record_count).to eq 14
68
68
  end
69
69
  end
70
70
 
71
- RSpec.describe DBF, "of type 30 (Visual FoxPro)" do
71
+ RSpec.describe DBF, 'of type 30 (Visual FoxPro)' do
72
72
  let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
73
73
 
74
- it_should_behave_like "DBF"
74
+ it_should_behave_like 'DBF'
75
75
 
76
- it "should report the correct version number" do
77
- expect(table.version).to eq "30"
76
+ it 'should report the correct version number' do
77
+ expect(table.version).to eq '30'
78
78
  end
79
79
 
80
- it "should report the correct version description" do
81
- expect(table.version_description).to eq "Visual FoxPro"
80
+ it 'should report the correct version description' do
81
+ expect(table.version_description).to eq 'Visual FoxPro'
82
82
  end
83
83
 
84
- it "should determine the number of records" do
84
+ it 'should determine the number of records' do
85
85
  expect(table.record_count).to eq 34
86
86
  end
87
87
 
88
- it "reads memo data" do
88
+ it 'reads memo data' do
89
89
  expect(table.record(3).classes).to match(/\AAgriculture.*Farming\r\n\Z/m)
90
90
  end
91
91
  end
92
92
 
93
- RSpec.describe DBF, "of type 31 (Visual FoxPro with AutoIncrement field)" do
93
+ RSpec.describe DBF, 'of type 31 (Visual FoxPro with AutoIncrement field)' do
94
94
  let(:table) { DBF::Table.new fixture('dbase_31.dbf') }
95
95
 
96
- it_should_behave_like "DBF"
96
+ it_should_behave_like 'DBF'
97
97
 
98
- it "should have a dBase version of 31" do
99
- expect(table.version).to eq "31"
98
+ it 'should have a dBase version of 31' do
99
+ expect(table.version).to eq '31'
100
100
  end
101
101
 
102
- it "should report the correct version description" do
103
- expect(table.version_description).to eq "Visual FoxPro with AutoIncrement field"
102
+ it 'should report the correct version description' do
103
+ expect(table.version_description).to eq 'Visual FoxPro with AutoIncrement field'
104
104
  end
105
105
 
106
- it "should determine the number of records" do
106
+ it 'should determine the number of records' do
107
107
  expect(table.record_count).to eq 77
108
108
  end
109
109
  end
110
110
 
111
- RSpec.describe DBF, "of type 83 (dBase III with memo file)" do
111
+ RSpec.describe DBF, 'of type 83 (dBase III with memo file)' do
112
112
  let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
113
113
 
114
- it_should_behave_like "DBF"
114
+ it_should_behave_like 'DBF'
115
115
 
116
- it "should report the correct version number" do
117
- expect(table.version).to eq "83"
116
+ it 'should report the correct version number' do
117
+ expect(table.version).to eq '83'
118
118
  end
119
119
 
120
- it "should report the correct version description" do
121
- expect(table.version_description).to eq "dBase III with memo file"
120
+ it 'should report the correct version description' do
121
+ expect(table.version_description).to eq 'dBase III with memo file'
122
122
  end
123
123
 
124
- it "should determine the number of records" do
124
+ it 'should determine the number of records' do
125
125
  expect(table.record_count).to eq 67
126
126
  end
127
127
  end
128
128
 
129
- RSpec.describe DBF, "of type 8b (dBase IV with memo file)" do
129
+ RSpec.describe DBF, 'of type 8b (dBase IV with memo file)' do
130
130
  let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
131
131
 
132
- it_should_behave_like "DBF"
132
+ it_should_behave_like 'DBF'
133
133
 
134
- it "should report the correct version number" do
135
- expect(table.version).to eq "8b"
134
+ it 'should report the correct version number' do
135
+ expect(table.version).to eq '8b'
136
136
  end
137
137
 
138
- it "should report the correct version description" do
139
- expect(table.version_description).to eq "dBase IV with memo file"
138
+ it 'should report the correct version description' do
139
+ expect(table.version_description).to eq 'dBase IV with memo file'
140
140
  end
141
141
 
142
- it "should determine the number of records" do
142
+ it 'should determine the number of records' do
143
143
  expect(table.record_count).to eq 10
144
144
  end
145
145
  end
146
146
 
147
- RSpec.describe DBF, "of type f5 (FoxPro with memo file)" do
147
+ RSpec.describe DBF, 'of type f5 (FoxPro with memo file)' do
148
148
  let(:table) { DBF::Table.new fixture('dbase_f5.dbf') }
149
149
 
150
- it_should_behave_like "DBF"
150
+ it_should_behave_like 'DBF'
151
151
 
152
- it "should report the correct version number" do
153
- expect(table.version).to eq "f5"
152
+ it 'should report the correct version number' do
153
+ expect(table.version).to eq 'f5'
154
154
  end
155
155
 
156
- it "should report the correct version description" do
157
- expect(table.version_description).to eq "FoxPro with memo file"
156
+ it 'should report the correct version description' do
157
+ expect(table.version_description).to eq 'FoxPro with memo file'
158
158
  end
159
159
 
160
- it "should determine the number of records" do
160
+ it 'should determine the number of records' do
161
161
  expect(table.record_count).to eq 975
162
162
  end
163
163
 
164
- it "reads memo data" do
164
+ it 'reads memo data' do
165
165
  expect(table.record(3).datn.to_s).to eq '1870-06-30'
166
166
  end
167
167
  end