dbf 5.1.0 → 5.2.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +4 -2
  4. data/dbf.gemspec +6 -9
  5. data/lib/dbf/column.rb +19 -15
  6. data/lib/dbf/column_builder.rb +31 -0
  7. data/lib/dbf/column_type.rb +61 -15
  8. data/lib/dbf/database/foxpro.rb +21 -32
  9. data/lib/dbf/encodings.rb +2 -0
  10. data/lib/dbf/file_handler.rb +36 -0
  11. data/lib/dbf/find.rb +54 -0
  12. data/lib/dbf/header.rb +7 -8
  13. data/lib/dbf/memo/base.rb +4 -0
  14. data/lib/dbf/memo/dbase3.rb +5 -3
  15. data/lib/dbf/memo/dbase4.rb +4 -2
  16. data/lib/dbf/memo/foxpro.rb +16 -7
  17. data/lib/dbf/record.rb +62 -34
  18. data/lib/dbf/record_context.rb +5 -0
  19. data/lib/dbf/record_iterator.rb +35 -0
  20. data/lib/dbf/schema.rb +23 -21
  21. data/lib/dbf/table.rb +44 -178
  22. data/lib/dbf/version.rb +3 -1
  23. data/lib/dbf/version_config.rb +79 -0
  24. data/lib/dbf.rb +8 -0
  25. metadata +15 -64
  26. data/spec/dbf/column_spec.rb +0 -286
  27. data/spec/dbf/database/foxpro_spec.rb +0 -51
  28. data/spec/dbf/encoding_spec.rb +0 -47
  29. data/spec/dbf/file_formats_spec.rb +0 -219
  30. data/spec/dbf/record_spec.rb +0 -114
  31. data/spec/dbf/table_spec.rb +0 -375
  32. data/spec/fixtures/cp1251.dbf +0 -0
  33. data/spec/fixtures/cp1251_summary.txt +0 -12
  34. data/spec/fixtures/dbase_02.dbf +0 -0
  35. data/spec/fixtures/dbase_02_summary.txt +0 -23
  36. data/spec/fixtures/dbase_03.dbf +0 -0
  37. data/spec/fixtures/dbase_03_cyrillic.dbf +0 -0
  38. data/spec/fixtures/dbase_03_cyrillic_summary.txt +0 -11
  39. data/spec/fixtures/dbase_03_summary.txt +0 -40
  40. data/spec/fixtures/dbase_30.dbf +0 -0
  41. data/spec/fixtures/dbase_30.fpt +0 -0
  42. data/spec/fixtures/dbase_30_summary.txt +0 -154
  43. data/spec/fixtures/dbase_31.dbf +0 -0
  44. data/spec/fixtures/dbase_31_summary.txt +0 -20
  45. data/spec/fixtures/dbase_32.dbf +0 -0
  46. data/spec/fixtures/dbase_32_summary.txt +0 -11
  47. data/spec/fixtures/dbase_83.dbf +0 -0
  48. data/spec/fixtures/dbase_83.dbt +0 -0
  49. data/spec/fixtures/dbase_83_missing_memo.dbf +0 -0
  50. data/spec/fixtures/dbase_83_missing_memo_record_0.yml +0 -16
  51. data/spec/fixtures/dbase_83_record_0.yml +0 -16
  52. data/spec/fixtures/dbase_83_record_9.yml +0 -23
  53. data/spec/fixtures/dbase_83_schema_ar.txt +0 -19
  54. data/spec/fixtures/dbase_83_schema_sq.txt +0 -21
  55. data/spec/fixtures/dbase_83_schema_sq_lim.txt +0 -21
  56. data/spec/fixtures/dbase_83_summary.txt +0 -24
  57. data/spec/fixtures/dbase_8b.dbf +0 -0
  58. data/spec/fixtures/dbase_8b.dbt +0 -0
  59. data/spec/fixtures/dbase_8b_summary.txt +0 -15
  60. data/spec/fixtures/dbase_8c.dbf +0 -0
  61. data/spec/fixtures/dbase_f5.dbf +0 -0
  62. data/spec/fixtures/dbase_f5.fpt +0 -0
  63. data/spec/fixtures/dbase_f5_summary.txt +0 -68
  64. data/spec/fixtures/foxprodb/FOXPRO-DB-TEST.DBC +0 -0
  65. data/spec/fixtures/foxprodb/FOXPRO-DB-TEST.DCT +0 -0
  66. data/spec/fixtures/foxprodb/FOXPRO-DB-TEST.DCX +0 -0
  67. data/spec/fixtures/foxprodb/calls.CDX +0 -0
  68. data/spec/fixtures/foxprodb/calls.FPT +0 -0
  69. data/spec/fixtures/foxprodb/calls.dbf +0 -0
  70. data/spec/fixtures/foxprodb/contacts.CDX +0 -0
  71. data/spec/fixtures/foxprodb/contacts.FPT +0 -0
  72. data/spec/fixtures/foxprodb/contacts.dbf +0 -0
  73. data/spec/fixtures/foxprodb/setup.CDX +0 -0
  74. data/spec/fixtures/foxprodb/setup.dbf +0 -0
  75. data/spec/fixtures/foxprodb/types.CDX +0 -0
  76. data/spec/fixtures/foxprodb/types.dbf +0 -0
  77. data/spec/fixtures/polygon.dbf +0 -0
  78. data/spec/spec_helper.rb +0 -33
@@ -1,219 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.shared_examples_for 'DBF' do
4
- let(:header_record_length) { table.instance_eval { header.record_length } }
5
- let(:sum_of_column_lengths) { table.columns.inject(1) { |sum, column| sum + column.length } }
6
-
7
- specify 'sum of column lengths should equal record length specified in header plus one' do
8
- expect(header_record_length).to eq sum_of_column_lengths
9
- end
10
-
11
- specify 'records should be instances of DBF::Record' do
12
- expect(table).to all be_a(DBF::Record)
13
- end
14
-
15
- specify 'record count should be the same as reported in the header' do
16
- expect(table.entries.size).to eq table.record_count
17
- end
18
-
19
- specify 'column names should not be blank' do
20
- table.columns.each do |column|
21
- expect(column.name).to_not be_empty
22
- end
23
- end
24
-
25
- specify 'column types should be valid' do
26
- valid_column_types = %w[C N L D M F B G P Y T I V X @ O + 0]
27
- table.columns.each do |column|
28
- expect(valid_column_types).to include(column.type)
29
- end
30
- end
31
-
32
- specify 'column lengths should be instances of Integer' do
33
- table.columns.each do |column|
34
- expect(column.length).to be_a(Integer)
35
- end
36
- end
37
-
38
- specify 'column lengths should be larger than 0' do
39
- table.columns.each do |column|
40
- expect(column.length).to be > 0
41
- end
42
- end
43
-
44
- specify 'column decimals should be instances of Integer' do
45
- table.columns.each do |column|
46
- expect(column.decimal).to be_a(Integer)
47
- end
48
- end
49
- end
50
-
51
- RSpec.describe DBF, 'of type 02 (FoxBase)' do
52
- let(:table) { DBF::Table.new fixture('dbase_02.dbf') }
53
-
54
- it_behaves_like 'DBF'
55
-
56
- it 'reports the correct version number' do
57
- expect(table.version).to eq '02'
58
- end
59
-
60
- it 'reports the correct version description' do
61
- expect(table.version_description).to eq 'FoxBase'
62
- end
63
-
64
- it 'determines the number of records' do
65
- expect(table.record_count).to eq 9
66
- end
67
- end
68
-
69
- RSpec.describe DBF, 'of type 03 (dBase III without memo file)' do
70
- let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
71
-
72
- it_behaves_like 'DBF'
73
-
74
- it 'reports the correct version number' do
75
- expect(table.version).to eq '03'
76
- end
77
-
78
- it 'reports the correct version description' do
79
- expect(table.version_description).to eq 'dBase III without memo file'
80
- end
81
-
82
- it 'determines the number of records' do
83
- expect(table.record_count).to eq 14
84
- end
85
- end
86
-
87
- RSpec.describe DBF, 'of type 30 (Visual FoxPro)' do
88
- let(:table) { DBF::Table.new fixture('dbase_30.dbf') }
89
-
90
- it_behaves_like 'DBF'
91
-
92
- it 'reports the correct version number' do
93
- expect(table.version).to eq '30'
94
- end
95
-
96
- it 'reports the correct version description' do
97
- expect(table.version_description).to eq 'Visual FoxPro'
98
- end
99
-
100
- it 'determines the number of records' do
101
- expect(table.record_count).to eq 34
102
- end
103
-
104
- it 'reads memo data' do
105
- expect(table.record(3).classes).to match(/\AAgriculture.*Farming\r\n\Z/m)
106
- end
107
- end
108
-
109
- RSpec.describe DBF, 'of type 31 (Visual FoxPro with AutoIncrement field)' do
110
- let(:table) { DBF::Table.new fixture('dbase_31.dbf') }
111
-
112
- it_behaves_like 'DBF'
113
-
114
- it 'has a dBase version of 31' do
115
- expect(table.version).to eq '31'
116
- end
117
-
118
- it 'reports the correct version description' do
119
- expect(table.version_description).to eq 'Visual FoxPro with AutoIncrement field'
120
- end
121
-
122
- it 'determines the number of records' do
123
- expect(table.record_count).to eq 77
124
- end
125
- end
126
-
127
- RSpec.describe DBF, 'of type 32 (Visual FoxPro with field type Varchar or Varbinary)' do
128
- let(:table) { DBF::Table.new fixture('dbase_32.dbf') }
129
-
130
- it_behaves_like 'DBF'
131
-
132
- it 'has a dBase version of 32' do
133
- expect(table.version).to eq '32'
134
- end
135
-
136
- it 'reports the correct version description' do
137
- expect(table.version_description).to eq 'Visual FoxPro with field type Varchar or Varbinary'
138
- end
139
-
140
- it 'determines the number of records' do
141
- expect(table.record_count).to eq 1
142
- end
143
- end
144
-
145
- RSpec.describe DBF, 'of type 83 (dBase III with memo file)' do
146
- let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
147
-
148
- it_behaves_like 'DBF'
149
-
150
- it 'reports the correct version number' do
151
- expect(table.version).to eq '83'
152
- end
153
-
154
- it 'reports the correct version description' do
155
- expect(table.version_description).to eq 'dBase III with memo file'
156
- end
157
-
158
- it 'determines the number of records' do
159
- expect(table.record_count).to eq 67
160
- end
161
- end
162
-
163
- RSpec.describe DBF, 'of type 8b (dBase IV with memo file)' do
164
- let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
165
-
166
- it_behaves_like 'DBF'
167
-
168
- it 'reports the correct version number' do
169
- expect(table.version).to eq '8b'
170
- end
171
-
172
- it 'reports the correct version description' do
173
- expect(table.version_description).to eq 'dBase IV with memo file'
174
- end
175
-
176
- it 'determines the number of records' do
177
- expect(table.record_count).to eq 10
178
- end
179
- end
180
-
181
- RSpec.describe DBF, 'of type 8c (unknown)' do
182
- let(:table) { DBF::Table.new fixture('dbase_8c.dbf') }
183
-
184
- it_behaves_like 'DBF'
185
-
186
- it 'reports the correct version number' do
187
- expect(table.version).to eq '8c'
188
- end
189
-
190
- it 'reports the correct version description' do
191
- expect(table.version_description).to eq 'dBase 7'
192
- end
193
-
194
- it 'determines the number of records' do
195
- expect(table.record_count).to eq 10
196
- end
197
- end
198
-
199
- RSpec.describe DBF, 'of type f5 (FoxPro with memo file)' do
200
- let(:table) { DBF::Table.new fixture('dbase_f5.dbf') }
201
-
202
- it_behaves_like 'DBF'
203
-
204
- it 'reports the correct version number' do
205
- expect(table.version).to eq 'f5'
206
- end
207
-
208
- it 'reports the correct version description' do
209
- expect(table.version_description).to eq 'FoxPro with memo file'
210
- end
211
-
212
- it 'determines the number of records' do
213
- expect(table.record_count).to eq 975
214
- end
215
-
216
- it 'reads memo data' do
217
- expect(table.record(3).datn.to_s).to eq '1870-06-30'
218
- end
219
- end
@@ -1,114 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe DBF::Record do
4
- describe '#to_a' do
5
- let(:table) { DBF::Table.new fixture('dbase_83.dbf') }
6
-
7
- it 'returns an ordered array of attribute values' do
8
- record = table.record(0)
9
- expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_record_0.yml'))
10
-
11
- record = table.record(9)
12
- expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_record_9.yml'))
13
- end
14
-
15
- describe 'with missing memo file' do
16
- describe 'when opening a path' do
17
- let(:table) { DBF::Table.new fixture('dbase_83_missing_memo.dbf') }
18
-
19
- it 'returns nil values for memo fields' do
20
- record = table.record(0)
21
- expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml'))
22
- end
23
- end
24
- end
25
-
26
- describe 'when opening StringIO' do
27
- let(:data) { StringIO.new(File.read(fixture('dbase_83_missing_memo.dbf'))) }
28
- let(:table) { DBF::Table.new(data) }
29
-
30
- it 'returns nil values for memo fields' do
31
- record = table.record(0)
32
- expect(record.to_a).to eq YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml'))
33
- end
34
- end
35
- end
36
-
37
- describe '#==' do
38
- let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
39
- let(:record) { table.record(9) }
40
-
41
- describe 'when other does not have attributes' do
42
- it 'returns false' do
43
- expect(record == instance_double(DBF::Record)).to be_falsey
44
- end
45
- end
46
-
47
- describe 'if other attributes match' do
48
- let(:attributes) { {x: 1, y: 2} }
49
- let(:other) { instance_double(DBF::Record, attributes: attributes) }
50
-
51
- before do
52
- allow(record).to receive(:attributes).and_return(attributes)
53
- end
54
-
55
- it 'returns true' do
56
- expect(record == other).to be_truthy
57
- end
58
- end
59
-
60
- end
61
-
62
- describe 'column accessors' do
63
- let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
64
- let(:record) { table.find(0) }
65
-
66
- %w[character numerical date logical float memo].each do |column_name|
67
- it "defines accessor method for '#{column_name}' column" do
68
- expect(record).to respond_to(column_name.to_sym)
69
- end
70
-
71
- end
72
- end
73
-
74
- describe 'column data for table' do
75
- describe 'using specified in dbf encoding' do
76
- let(:table) { DBF::Table.new fixture('cp1251.dbf') }
77
- let(:record) { table.find(0) }
78
-
79
- it 'encodes to default system encoding' do
80
- expect(record.name.encoding).to eq Encoding.default_external
81
-
82
- # russian a
83
- expect(record.name.encode('UTF-8').unpack1('H4')).to eq 'd0b0'
84
- end
85
- end
86
-
87
- describe 'overriding specified in dbf encoding' do
88
- let(:table) { DBF::Table.new fixture('cp1251.dbf'), nil, 'cp866' }
89
- let(:record) { table.find(0) }
90
-
91
- it 'transcodes from manually specified encoding to default system encoding' do
92
- expect(record.name.encoding).to eq Encoding.default_external
93
-
94
- # russian а encoded in cp1251 and read as if it was encoded in cp866
95
- expect(record.name.encode('UTF-8').unpack1('H4')).to eq 'd180'
96
- end
97
- end
98
- end
99
-
100
- describe '#attributes' do
101
- let(:table) { DBF::Table.new fixture('dbase_8b.dbf') }
102
- let(:record) { table.find(0) }
103
-
104
- it 'is a hash of attribute name/value pairs' do
105
- expect(record.attributes).to be_a(Hash)
106
- expect(record.attributes['CHARACTER']).to eq 'One'
107
- end
108
-
109
- it 'has only original field names as keys' do
110
- original_field_names = %w[CHARACTER DATE FLOAT LOGICAL MEMO NUMERICAL]
111
- expect(record.attributes.keys.sort).to eq original_field_names
112
- end
113
- end
114
- end
@@ -1,375 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe DBF::Table do
4
- let(:dbf_path) { fixture('dbase_83.dbf') }
5
- let(:memo_path) { fixture('dbase_83.dbt') }
6
- let(:table) { DBF::Table.new dbf_path }
7
-
8
- specify 'foxpro versions' do
9
- expect(DBF::Table::FOXPRO_VERSIONS.keys.sort).to eq %w[30 31 f5 fb].sort
10
- end
11
-
12
- specify 'row is an alias of record' do
13
- expect(table.record(1)).to eq table.row(1)
14
- end
15
-
16
- describe '#initialize' do
17
- let(:data) { StringIO.new File.read(dbf_path) }
18
- let(:memo) { StringIO.new File.read(memo_path) }
19
-
20
- describe 'when given a path to an existing dbf file' do
21
- it 'does not raise an error' do
22
- expect { DBF::Table.new dbf_path }.to_not raise_error
23
- end
24
- end
25
-
26
- describe 'when given a path to a non-existent dbf file' do
27
- it 'raises a DBF::FileNotFound error' do
28
- expect { DBF::Table.new 'x' }.to raise_error(DBF::FileNotFoundError, 'file not found: x')
29
- end
30
- end
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
-
38
- describe 'when given paths to existing dbf and memo files' do
39
- it 'does not raise an error' do
40
- expect { DBF::Table.new dbf_path, memo_path }.to_not raise_error
41
- end
42
- end
43
-
44
- it 'accepts an io-like data object' do
45
- expect { DBF::Table.new data }.to_not raise_error
46
- end
47
-
48
- it 'accepts an io-like data and memo object' do
49
- expect { DBF::Table.new data, memo }.to_not raise_error
50
- end
51
- end
52
-
53
- describe '#close' do
54
- before { table.close }
55
-
56
- it 'closes the io' do
57
- expect { table.record(1) }.to raise_error(IOError)
58
- end
59
- end
60
-
61
- describe '#schema' do
62
- describe 'when data is IO' do
63
- let(:control_schema) { File.read(fixture('dbase_83_schema_ar.txt')) }
64
-
65
- it 'matches the test schema fixture' do
66
- expect(table.schema).to eq control_schema
67
- end
68
-
69
- it 'raises ArgumentError if there is no matching schema' do
70
- expect { table.schema(:invalid) }.to raise_error(
71
- ArgumentError,
72
- ':invalid is not a valid schema. Valid schemas are: activerecord, json, sequel.'
73
- )
74
- end
75
- end
76
-
77
- describe 'when data is StringIO' do
78
- let(:data) { StringIO.new File.read(dbf_path) }
79
- let(:table) { DBF::Table.new data }
80
-
81
- let(:control_schema) { File.read(fixture('dbase_83_schema_ar.txt')) }
82
-
83
- it 'matches the test schema fixture' do
84
- table.name = 'dbase_83'
85
- expect(table.schema).to eq control_schema
86
- end
87
- end
88
- end
89
-
90
- describe '#sequel_schema' do
91
- it 'returns a valid Sequel migration by default' do
92
- control_schema = File.read(fixture('dbase_83_schema_sq.txt'))
93
- expect(table.sequel_schema).to eq control_schema
94
- end
95
-
96
- it 'returns a limited Sequel migration when passed true' do
97
- control_schema = File.read(fixture('dbase_83_schema_sq_lim.txt'))
98
- expect(table.sequel_schema).to eq control_schema
99
- end
100
-
101
- end
102
-
103
- describe '#json_schema' do
104
- it 'is valid JSON' do
105
- expect { JSON.parse(table.json_schema) }.to_not raise_error
106
- end
107
-
108
- it 'matches the test fixture' do
109
- data = JSON.parse(table.json_schema)
110
- expect(data).to eq [
111
- {'name' => 'ID', 'type' => 'N', 'length' => 19, 'decimal' => 0},
112
- {'name' => 'CATCOUNT', 'type' => 'N', 'length' => 19, 'decimal' => 0},
113
- {'name' => 'AGRPCOUNT', 'type' => 'N', 'length' => 19, 'decimal' => 0},
114
- {'name' => 'PGRPCOUNT', 'type' => 'N', 'length' => 19, 'decimal' => 0},
115
- {'name' => 'ORDER', 'type' => 'N', 'length' => 19, 'decimal' => 0},
116
- {'name' => 'CODE', 'type' => 'C', 'length' => 50, 'decimal' => 0},
117
- {'name' => 'NAME', 'type' => 'C', 'length' => 100, 'decimal' => 0},
118
- {'name' => 'THUMBNAIL', 'type' => 'C', 'length' => 254, 'decimal' => 0},
119
- {'name' => 'IMAGE', 'type' => 'C', 'length' => 254, 'decimal' => 0},
120
- {'name' => 'PRICE', 'type' => 'N', 'length' => 13, 'decimal' => 2},
121
- {'name' => 'COST', 'type' => 'N', 'length' => 13, 'decimal' => 2},
122
- {'name' => 'DESC', 'type' => 'M', 'length' => 10, 'decimal' => 0},
123
- {'name' => 'WEIGHT', 'type' => 'N', 'length' => 13, 'decimal' => 2},
124
- {'name' => 'TAXABLE', 'type' => 'L', 'length' => 1, 'decimal' => 0},
125
- {'name' => 'ACTIVE', 'type' => 'L', 'length' => 1, 'decimal' => 0}
126
- ]
127
- end
128
- end
129
-
130
- describe '#to_csv' do
131
- after do
132
- FileUtils.rm_f 'test.csv'
133
- end
134
-
135
- describe 'when no path param passed' do
136
- it 'writes to STDOUT' do
137
- expect { table.to_csv }.to output.to_stdout
138
- end
139
- end
140
-
141
- describe 'when path param passed' do
142
- before { table.to_csv('test.csv') }
143
-
144
- it 'creates a custom csv file' do
145
- expect(File).to exist('test.csv')
146
- end
147
- end
148
- end
149
-
150
- describe '#record' do
151
- it 'return nil for deleted records' do
152
- allow(table).to receive(:deleted_record?).and_return(true)
153
- expect(table.record(5)).to be_nil
154
- end
155
-
156
- describe 'when dbf has no column definitions' do
157
- let(:dbf_path) { fixture('polygon.dbf') }
158
-
159
- it 'raises a DBF::NoColumnsDefined error' do
160
- expect { DBF::Table.new(dbf_path).record(1) }.to raise_error(DBF::NoColumnsDefined, 'The DBF file has no columns defined')
161
- end
162
- end
163
- end
164
-
165
- describe '#current_record' do
166
- it 'returns nil for deleted records' do
167
- allow(table).to receive(:deleted_record?).and_return(true)
168
- expect(table.record(0)).to be_nil
169
- end
170
- end
171
-
172
- describe '#find' do
173
- describe 'with index' do
174
- it 'returns the correct record' do
175
- expect(table.find(5)).to eq table.record(5)
176
- end
177
- end
178
-
179
- describe 'with array of indexes' do
180
- it 'returns the correct records' do
181
- expect(table.find([1, 5, 10])).to eq [table.record(1), table.record(5), table.record(10)]
182
- end
183
- end
184
-
185
- describe 'with :all' do
186
- let(:records) do
187
- table.find(:all, weight: 0.0)
188
- end
189
-
190
- it 'retrieves only matching records' do
191
- expect(records.size).to eq 66
192
- end
193
-
194
- it 'yields to a block if given' do
195
- record_count = 0
196
- table.find(:all, weight: 0.0) do |record|
197
- record_count += 1
198
- expect(record).to be_a DBF::Record
199
- end
200
- expect(record_count).to eq 66
201
- end
202
-
203
- it 'returns all records if options are empty' do
204
- expect(table.find(:all)).to eq table.to_a
205
- end
206
-
207
- it 'returns matching records when used with options' do
208
- expect(table.find(:all, 'WEIGHT' => 0.0)).to eq(table.select { |r| r['weight'] == 0.0 })
209
- end
210
-
211
- it 'ANDS multiple search terms' do
212
- expect(table.find(:all, 'ID' => 30, :IMAGE => 'graphics/00000001/TBC01.jpg')).to be_empty
213
- end
214
-
215
- it 'matches original column names' do
216
- expect(table.find(:all, 'WEIGHT' => 0.0)).to_not be_empty
217
- end
218
-
219
- it 'matches symbolized column names' do
220
- expect(table.find(:all, WEIGHT: 0.0)).to_not be_empty
221
- end
222
-
223
- it 'matches downcased column names' do
224
- expect(table.find(:all, 'weight' => 0.0)).to_not be_empty
225
- end
226
-
227
- it 'matches symbolized downcased column names' do
228
- expect(table.find(:all, weight: 0.0)).to_not be_empty
229
- end
230
- end
231
-
232
- describe 'with :first' do
233
- it 'returns the first record if options are empty' do
234
- expect(table.find(:first)).to eq table.record(0)
235
- end
236
-
237
- it 'returns the first matching record when used with options' do
238
- expect(table.find(:first, 'CODE' => 'C')).to eq table.record(5)
239
- end
240
-
241
- it 'ANDs multiple search terms' do
242
- expect(table.find(:first, 'ID' => 30, 'IMAGE' => 'graphics/00000001/TBC01.jpg')).to be_nil
243
- end
244
- end
245
- end
246
-
247
- describe '#filename' do
248
- it 'returns the filename as a string' do
249
- expect(table.filename).to eq 'dbase_83.dbf'
250
- end
251
- end
252
-
253
- describe '#name' do
254
- describe 'when data is an IO' do
255
- it 'defaults to the filename less extension' do
256
- expect(table.name).to eq 'dbase_83'
257
- end
258
-
259
- it 'is mutable' do
260
- table.name = 'database_83'
261
- expect(table.name).to eq 'database_83'
262
- end
263
- end
264
-
265
- describe 'when data is a StringIO' do
266
- let(:data) { StringIO.new File.read(dbf_path) }
267
- let(:memo) { StringIO.new File.read(memo_path) }
268
- let(:table) { DBF::Table.new data }
269
-
270
- it 'is nil' do
271
- expect(table.name).to be_nil
272
- end
273
-
274
- it 'is mutable' do
275
- table.name = 'database_83'
276
- expect(table.name).to eq 'database_83'
277
- end
278
- end
279
- end
280
-
281
- describe '#has_memo_file?' do
282
- describe 'without a memo file' do
283
- let(:table) { DBF::Table.new fixture('dbase_03.dbf') }
284
-
285
- it 'is false' do
286
- expect(table).to_not have_memo_file
287
- end
288
- end
289
-
290
- describe 'with a memo file' do
291
- it 'is true' do
292
- expect(table).to have_memo_file
293
- end
294
- end
295
- end
296
-
297
- describe '#columns' do
298
- let(:columns) { table.columns }
299
-
300
- it 'is an array of Columns' do
301
- expect(columns).to be_an(Array)
302
- expect(columns).to_not be_empty
303
- expect(columns).to(be_all { |c| c.is_a? DBF::Column })
304
- end
305
- end
306
-
307
- describe '#column_names' do
308
- let(:column_names) do
309
- %w[ID CATCOUNT AGRPCOUNT PGRPCOUNT ORDER CODE NAME THUMBNAIL IMAGE PRICE COST DESC WEIGHT TAXABLE ACTIVE]
310
- end
311
-
312
- describe 'when data is an IO' do
313
- it 'is an array of all column names' do
314
- expect(table.column_names).to eq column_names
315
- end
316
- end
317
-
318
- describe 'when data is a StringIO' do
319
- let(:data) { StringIO.new File.read(dbf_path) }
320
- let(:table) { DBF::Table.new data, nil, Encoding::US_ASCII }
321
-
322
- it 'is an array of all column names' do
323
- expect(table.column_names).to eq column_names
324
- end
325
- end
326
- end
327
-
328
- describe '#activerecord_schema_definition' do
329
- context 'with type N (number)' do
330
- it 'outputs an integer column' do
331
- column = DBF::Column.new table, 'ColumnName', 'N', 1, 0
332
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :integer\n"
333
- end
334
- end
335
-
336
- describe 'with type B (binary)' do
337
- context 'with Foxpro dbf' do
338
- it 'outputs a float column' do
339
- column = DBF::Column.new table, 'ColumnName', 'B', 1, 2
340
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :binary\n"
341
- end
342
- end
343
- end
344
-
345
- it 'defines a float colmn if type is (N)umber with more than 0 decimals' do
346
- column = DBF::Column.new table, 'ColumnName', 'N', 1, 2
347
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :float\n"
348
- end
349
-
350
- it 'defines a date column if type is (D)ate' do
351
- column = DBF::Column.new table, 'ColumnName', 'D', 8, 0
352
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :date\n"
353
- end
354
-
355
- it 'defines a datetime column if type is (D)ate' do
356
- column = DBF::Column.new table, 'ColumnName', 'T', 16, 0
357
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :datetime\n"
358
- end
359
-
360
- it 'defines a boolean column if type is (L)ogical' do
361
- column = DBF::Column.new table, 'ColumnName', 'L', 1, 0
362
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :boolean\n"
363
- end
364
-
365
- it 'defines a text column if type is (M)emo' do
366
- column = DBF::Column.new table, 'ColumnName', 'M', 1, 0
367
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :text\n"
368
- end
369
-
370
- it 'defines a string column with length for any other data types' do
371
- column = DBF::Column.new table, 'ColumnName', 'X', 20, 0
372
- expect(table.activerecord_schema_definition(column)).to eq "\"column_name\", :string, :limit => 20\n"
373
- end
374
- end
375
- end
Binary file