dbf 4.3.2 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c754ead4e347035e5997c61369dc602784ed7b1c6abda474d4f5678980d6280a
4
- data.tar.gz: 5b8494f61b66720690b77cb11fc4bc2bfa778dd51e269b5091c9a77cb3c74c4e
3
+ metadata.gz: 4db2b075394f8f3cb60fc36e71a71eb9f8367e7b34b8863481fe6c2ab0a2aeac
4
+ data.tar.gz: f3e7ba5bd793d1732d2ae20b23b3a56285fa81228d06db9d38b055049591b76e
5
5
  SHA512:
6
- metadata.gz: bffdced2430256755264936f67bbabc996add3b10b89f39e0c03b53203b859eabf760d5d6c0660679e741daee619f4dc69c4707c7cca6fea4c86719709b99461
7
- data.tar.gz: 447cd7a12f995dcd2fcedf4e0c9c009b9d5b990e2ce2bd0eac65515a98247bb112fd230b5fd4023bf1478ee407dedcac42e50102ed1cbd73ade5608056aa0ea9
6
+ metadata.gz: b7c5847999bf422df92d90bbdea11bf0d6baa09ace5649cd65814b22cb5192a5ec085e8b25b9a2c56cb889f0f23d0fd0e3e20561c737ae05857afb63db69aea4
7
+ data.tar.gz: e1ed01f59b6f13e5bd3a91b327cdcd2060f85f2a608dfaf3e4dae1723f9a97b0ef2221465b0ae14aed1345e99b0c6c8edf9de00259f8a741914d889212ca5dc6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.0.1
4
+
5
+ - Raise ArgumentError data is not a string or StringIO object
6
+
7
+ ## 5.0.0
8
+
9
+ - Refactor the Column class to support non-ASCII header names
10
+ - Output encoding is now set to UTF-8 if there is no embedded encoding and one is not
11
+ specified during DBF::Table initialization.
12
+
3
13
  ## 4.3.2
4
14
 
5
15
  - Fixes to maintain support for Ruby 3.0.x until it's EOL
data/bin/dbf CHANGED
@@ -41,6 +41,7 @@ else
41
41
  puts
42
42
  puts "Database: #{filename}"
43
43
  puts "Type: (#{table.version}) #{table.version_description}"
44
+ puts "Encoding: #{table.header_encoding}" if table.header_encoding
44
45
  puts "Memo File: #{table.has_memo_file? ? 'true' : 'false'}"
45
46
  puts "Records: #{table.record_count}"
46
47
 
data/lib/dbf/column.rb CHANGED
@@ -8,7 +8,7 @@ module DBF
8
8
  class NameError < StandardError
9
9
  end
10
10
 
11
- attr_reader :table, :name, :type, :length, :decimal
11
+ attr_reader :table, :name, :type, :length, :decimal, :encoding
12
12
 
13
13
  def_delegator :type_cast_class, :type_cast
14
14
 
@@ -38,13 +38,14 @@ module DBF
38
38
  # @param length [Integer]
39
39
  # @param decimal [Integer]
40
40
  def initialize(table, name, type, length, decimal)
41
+ @encoding = table.encoding
42
+
41
43
  @table = table
42
44
  @name = clean(name)
43
45
  @type = type
44
46
  @length = length
45
47
  @decimal = decimal
46
48
  @version = table.version
47
- @encoding = table.encoding
48
49
 
49
50
  validate_length
50
51
  validate_name
@@ -72,37 +73,18 @@ module DBF
72
73
  # @return [String]
73
74
  def underscored_name
74
75
  @underscored_name ||= name.gsub(/([a-z\d])([A-Z])/, '\1_\2').tr('-', '_').downcase
75
-
76
76
  end
77
77
 
78
78
  private
79
79
 
80
80
  def clean(value) # :nodoc:
81
- value.strip.partition("\x00").first.gsub(/[^\x20-\x7E]/, '')
82
- end
83
-
84
- def encode(value, strip_output: false) # :nodoc:
85
- return value unless value.respond_to?(:encoding)
86
-
87
- output = @encoding ? encode_string(value) : value
88
- strip_output ? output.strip : output
89
- end
90
-
91
- def encoding_args # :nodoc:
92
- @encoding_args ||= [
93
- Encoding.default_external,
94
- {undef: :replace, invalid: :replace}
95
- ]
96
- end
97
-
98
- def encode_string(string) # :nodoc:
99
- string.force_encoding(@encoding).encode(*encoding_args)
81
+ table.encode_string(value.strip.partition("\x00").first)
100
82
  end
101
83
 
102
84
  def type_cast_class # :nodoc:
103
85
  @type_cast_class ||= begin
104
86
  klass = @length == 0 ? ColumnType::Nil : TYPE_CAST_CLASS[type.to_sym]
105
- klass.new(@decimal, @encoding)
87
+ klass.new(self)
106
88
  end
107
89
  end
108
90
 
@@ -5,9 +5,9 @@ module DBF
5
5
 
6
6
  # @param decimal [Integer]
7
7
  # @param encoding [String, Encoding]
8
- def initialize(decimal, encoding)
9
- @decimal = decimal
10
- @encoding = encoding
8
+ def initialize(column)
9
+ @decimal = column.decimal
10
+ @encoding = column.encoding
11
11
  end
12
12
  end
13
13
 
data/lib/dbf/table.rb CHANGED
@@ -76,7 +76,7 @@ module DBF
76
76
  # @param encoding [optional String, Encoding] encoding Name of the encoding or an Encoding object
77
77
  def initialize(data, memo = nil, encoding = nil)
78
78
  @data = open_data(data)
79
- @encoding = encoding || header.encoding
79
+ @encoding = encoding || header.encoding || Encoding.default_external
80
80
  @memo = open_memo(data, memo)
81
81
  yield self if block_given?
82
82
  end
@@ -211,6 +211,21 @@ module DBF
211
211
  VERSIONS[version]
212
212
  end
213
213
 
214
+ # Encode string
215
+ #
216
+ # @param [String] string
217
+ # @return [String]
218
+ def encode_string(string) # :nodoc:
219
+ string.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace)
220
+ end
221
+
222
+ # Encoding specified in the file header
223
+ #
224
+ # @return [Encoding]
225
+ def header_encoding
226
+ header.encoding
227
+ end
228
+
214
229
  private
215
230
 
216
231
  def build_columns # :nodoc:
@@ -293,7 +308,14 @@ module DBF
293
308
  end
294
309
 
295
310
  def open_data(data) # :nodoc:
296
- data.is_a?(StringIO) ? data : File.open(data, 'rb')
311
+ case data
312
+ when StringIO
313
+ data
314
+ when String
315
+ File.open(data, 'rb')
316
+ else
317
+ raise ArgumentError, 'data must be a file path or StringIO object'
318
+ end
297
319
  rescue Errno::ENOENT
298
320
  raise DBF::FileNotFoundError, "file not found: #{data}"
299
321
  end
data/lib/dbf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module DBF
2
- VERSION = '4.3.2'.freeze
2
+ VERSION = '5.0.1'.freeze
3
3
  end
@@ -37,7 +37,7 @@ RSpec.describe DBF::Column do
37
37
 
38
38
  describe 'with empty column name' do
39
39
  it 'raises DBF::Column::NameError' do
40
- expect { DBF::Column.new table, "\xFF\xFC", 'N', 1, 0 }.to raise_error(DBF::Column::NameError)
40
+ expect { DBF::Column.new table, '', 'N', 1, 0 }.to raise_error(DBF::Column::NameError)
41
41
  end
42
42
  end
43
43
  end
@@ -283,16 +283,4 @@ RSpec.describe DBF::Column do
283
283
  end
284
284
  end
285
285
  end
286
-
287
- describe '#name' do
288
- it 'contains only ASCII characters' do
289
- column = DBF::Column.new table, "--\x1F-\x68\x65\x6C\x6C\x6F \x00world-\x80--", 'N', 1, 0
290
- expect(column.name).to eq '---hello '
291
- end
292
-
293
- it 'is truncated at the null character' do
294
- column = DBF::Column.new table, "--\x1F-\x68\x65\x6C\x6C\x6F \x00world-\x80\x80--", 'N', 1, 0
295
- expect(column.name).to eq '---hello '
296
- end
297
- end
298
286
  end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'default encoding' do
6
+ let(:dbf_path) { fixture('dbase_03_cyrillic.dbf') }
7
+ let(:table) { DBF::Table.new dbf_path }
8
+
9
+ it 'defaults to UTF-8 encoding' do
10
+ expect(table.encoding).to eq Encoding::UTF_8
11
+ end
12
+
13
+ it 'uses the table encoding for column encoding' do
14
+ column = table.columns.first
15
+ expect(column.encoding).to eq table.encoding
16
+ end
17
+
18
+ it 'encodes column names' do
19
+ expect(table.column_names).to eq ['ШАР', 'ПЛОЩА']
20
+ end
21
+
22
+ it 'encodes record values' do
23
+ expect(table.record(0).attributes['ШАР']).to eq 'Номер'
24
+ end
25
+ end
26
+
27
+ RSpec.describe 'embedded encoding' do
28
+ let(:dbf_path) { fixture('cp1251.dbf') }
29
+ let(:table) { DBF::Table.new dbf_path }
30
+
31
+ it 'defaults to UTF-8 encoding' do
32
+ expect(table.encoding).to eq 'cp1251'
33
+ end
34
+
35
+ it 'uses the table encoding for column encoding' do
36
+ column = table.columns.first
37
+ expect(column.encoding).to eq table.encoding
38
+ end
39
+
40
+ it 'encodes column names' do
41
+ expect(table.column_names).to eq ['RN', 'NAME']
42
+ end
43
+
44
+ it 'encodes record values' do
45
+ expect(table.record(0).attributes['NAME']).to eq 'амбулаторно-поликлиническое'
46
+ end
47
+ end
@@ -29,6 +29,12 @@ RSpec.describe DBF::Table do
29
29
  end
30
30
  end
31
31
 
32
+ describe 'when data is nil' do
33
+ it 'raises ArgumentError' do
34
+ expect { DBF::Table.new nil }.to raise_error(ArgumentError, 'data must be a file path or StringIO object')
35
+ end
36
+ end
37
+
32
38
  describe 'when given paths to existing dbf and memo files' do
33
39
  it 'does not raise an error' do
34
40
  expect { DBF::Table.new dbf_path, memo_path }.to_not raise_error
@@ -1,6 +1,7 @@
1
1
 
2
2
  Database: cp1251.dbf
3
3
  Type: (30) Visual FoxPro
4
+ Encoding: cp1251
4
5
  Memo File: false
5
6
  Records: 4
6
7
 
Binary file
@@ -0,0 +1,11 @@
1
+
2
+ Database: dbase_03_cyrillic.dbf
3
+ Type: (03) dBase III without memo file
4
+ Memo File: false
5
+ Records: 2
6
+
7
+ Fields:
8
+ Name Type Length Decimal
9
+ ------------------------------------------------------------------------------
10
+ ШАР C 25 0
11
+ ПЛОЩА N 15 2
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: 4.3.2
4
+ version: 5.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: 2024-01-23 00:00:00.000000000 Z
11
+ date: 2024-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: csv
@@ -56,6 +56,7 @@ files:
56
56
  - lib/dbf/version.rb
57
57
  - spec/dbf/column_spec.rb
58
58
  - spec/dbf/database/foxpro_spec.rb
59
+ - spec/dbf/encoding_spec.rb
59
60
  - spec/dbf/file_formats_spec.rb
60
61
  - spec/dbf/record_spec.rb
61
62
  - spec/dbf/table_spec.rb
@@ -64,6 +65,8 @@ files:
64
65
  - spec/fixtures/dbase_02.dbf
65
66
  - spec/fixtures/dbase_02_summary.txt
66
67
  - spec/fixtures/dbase_03.dbf
68
+ - spec/fixtures/dbase_03_cyrillic.dbf
69
+ - spec/fixtures/dbase_03_cyrillic_summary.txt
67
70
  - spec/fixtures/dbase_03_summary.txt
68
71
  - spec/fixtures/dbase_30.dbf
69
72
  - spec/fixtures/dbase_30.fpt
@@ -125,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
128
  - !ruby/object:Gem::Version
126
129
  version: 1.3.0
127
130
  requirements: []
128
- rubygems_version: 3.4.12
131
+ rubygems_version: 3.5.8
129
132
  signing_key:
130
133
  specification_version: 4
131
134
  summary: Read xBase files