dbf 2.0.9 → 2.0.10

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
  SHA1:
3
- metadata.gz: ae3ffdc41708236ad7e4f5b6e97ccb7d1f4103a9
4
- data.tar.gz: 92ac525038db50bd6ee8860a22d4314a37c76b4c
3
+ metadata.gz: 0c8808826478c973095384cbe5b0f86e94f96ed8
4
+ data.tar.gz: 0ccf185436e3753f2bc08cb7211d1dea19e4b438
5
5
  SHA512:
6
- metadata.gz: 1301756b1375c63e6508d32328b411577c39f38568396d0123c25d8e6a03de85a06765e3e9cdeddd86a3588d58961108778d4b795724f1b8323adc9f9a564968
7
- data.tar.gz: 06dd9b432d1c2dab916e7aab441f268951325e6d36b7e125e7c8f0a04b8ece6ecc79c025e59d29e191438bb10978603564aa9357ec28c27c8945df1dd4149dad
6
+ metadata.gz: 9cda0f53ddb72957e83d63504b0d0a58227b05f07623b057cacce2d5325203ebb3419ce8524a82d2ef59b5d5a2dd5a35edcc5f4f5e9cc1bee7e3186b2565a0ae
7
+ data.tar.gz: 965eaaf92b0274c7e88b88e5c31c02387f95dd9810b25a65b2e41c756e615850e1ad6778265e6aa343b4c189caa040e5610672419337a874693f9cdf3c296af5
@@ -1,3 +1,6 @@
1
+ # 2.0.10
2
+ - allow 0 length fields, but always return nil as value
3
+
1
4
  # 2.0.9
2
5
  - fix dBase IV attributes when memo file is missing
3
6
 
data/Gemfile CHANGED
@@ -1,8 +1,6 @@
1
1
  gemspec
2
2
  source 'https://rubygems.org'
3
3
 
4
- gem 'rubysl', :platform => :rbx
5
-
6
4
  group :development, :test do
7
5
  gem 'rspec'
8
6
  gem 'guard'
@@ -1,272 +1,75 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dbf (2.0.8)
5
- fastercsv (~> 1.5.4)
4
+ dbf (2.0.9)
5
+ fastercsv (~> 1.5)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- byebug (3.2.0)
11
- columnize (~> 0.8)
12
- debugger-linecache (~> 1.2)
13
- celluloid (0.15.2)
14
- timers (~> 1.1.0)
10
+ celluloid (0.16.0)
11
+ timers (~> 4.0.0)
15
12
  coderay (1.1.0)
16
- columnize (0.9.0)
17
- debugger-linecache (1.2.0)
18
13
  diff-lcs (1.2.5)
19
14
  fastercsv (1.5.5)
20
- ffi (1.9.3)
21
- ffi2-generators (0.1.1)
15
+ ffi (1.9.8)
22
16
  formatador (0.2.5)
23
- guard (2.6.1)
17
+ guard (2.12.5)
24
18
  formatador (>= 0.2.4)
25
19
  listen (~> 2.7)
26
20
  lumberjack (~> 1.0)
21
+ nenv (~> 0.1)
22
+ notiffany (~> 0.0)
27
23
  pry (>= 0.9.12)
24
+ shellany (~> 0.0)
28
25
  thor (>= 0.18.1)
29
- guard-rspec (4.2.9)
26
+ guard-compat (1.2.1)
27
+ guard-rspec (4.5.0)
30
28
  guard (~> 2.1)
31
- rspec (>= 2.14, < 4.0)
32
- listen (2.7.7)
33
- celluloid (>= 0.15.2)
29
+ guard-compat (~> 1.1)
30
+ rspec (>= 2.99.0, < 4.0)
31
+ hitimes (1.2.2)
32
+ listen (2.10.0)
33
+ celluloid (~> 0.16.0)
34
34
  rb-fsevent (>= 0.9.3)
35
35
  rb-inotify (>= 0.9)
36
- lumberjack (1.0.6)
36
+ lumberjack (1.0.9)
37
37
  method_source (0.8.2)
38
- pry (0.9.12.6)
39
- coderay (~> 1.0)
40
- method_source (~> 0.8)
38
+ nenv (0.2.0)
39
+ notiffany (0.0.6)
40
+ nenv (~> 0.1)
41
+ shellany (~> 0.0)
42
+ pry (0.10.1)
43
+ coderay (~> 1.1.0)
44
+ method_source (~> 0.8.1)
41
45
  slop (~> 3.4)
42
46
  rb-fsevent (0.9.4)
43
47
  rb-inotify (0.9.5)
44
48
  ffi (>= 0.5.0)
45
- rspec (3.0.0)
46
- rspec-core (~> 3.0.0)
47
- rspec-expectations (~> 3.0.0)
48
- rspec-mocks (~> 3.0.0)
49
- rspec-core (3.0.0)
50
- rspec-support (~> 3.0.0)
51
- rspec-expectations (3.0.0)
49
+ rspec (3.2.0)
50
+ rspec-core (~> 3.2.0)
51
+ rspec-expectations (~> 3.2.0)
52
+ rspec-mocks (~> 3.2.0)
53
+ rspec-core (3.2.3)
54
+ rspec-support (~> 3.2.0)
55
+ rspec-expectations (3.2.1)
52
56
  diff-lcs (>= 1.2.0, < 2.0)
53
- rspec-support (~> 3.0.0)
54
- rspec-mocks (3.0.0)
55
- rspec-support (~> 3.0.0)
56
- rspec-support (3.0.0)
57
- rubysl (2.0.15)
58
- rubysl-abbrev (~> 2.0)
59
- rubysl-base64 (~> 2.0)
60
- rubysl-benchmark (~> 2.0)
61
- rubysl-bigdecimal (~> 2.0)
62
- rubysl-cgi (~> 2.0)
63
- rubysl-cgi-session (~> 2.0)
64
- rubysl-cmath (~> 2.0)
65
- rubysl-complex (~> 2.0)
66
- rubysl-continuation (~> 2.0)
67
- rubysl-coverage (~> 2.0)
68
- rubysl-csv (~> 2.0)
69
- rubysl-curses (~> 2.0)
70
- rubysl-date (~> 2.0)
71
- rubysl-delegate (~> 2.0)
72
- rubysl-digest (~> 2.0)
73
- rubysl-drb (~> 2.0)
74
- rubysl-e2mmap (~> 2.0)
75
- rubysl-english (~> 2.0)
76
- rubysl-enumerator (~> 2.0)
77
- rubysl-erb (~> 2.0)
78
- rubysl-etc (~> 2.0)
79
- rubysl-expect (~> 2.0)
80
- rubysl-fcntl (~> 2.0)
81
- rubysl-fiber (~> 2.0)
82
- rubysl-fileutils (~> 2.0)
83
- rubysl-find (~> 2.0)
84
- rubysl-forwardable (~> 2.0)
85
- rubysl-getoptlong (~> 2.0)
86
- rubysl-gserver (~> 2.0)
87
- rubysl-io-console (~> 2.0)
88
- rubysl-io-nonblock (~> 2.0)
89
- rubysl-io-wait (~> 2.0)
90
- rubysl-ipaddr (~> 2.0)
91
- rubysl-irb (~> 2.0)
92
- rubysl-logger (~> 2.0)
93
- rubysl-mathn (~> 2.0)
94
- rubysl-matrix (~> 2.0)
95
- rubysl-mkmf (~> 2.0)
96
- rubysl-monitor (~> 2.0)
97
- rubysl-mutex_m (~> 2.0)
98
- rubysl-net-ftp (~> 2.0)
99
- rubysl-net-http (~> 2.0)
100
- rubysl-net-imap (~> 2.0)
101
- rubysl-net-pop (~> 2.0)
102
- rubysl-net-protocol (~> 2.0)
103
- rubysl-net-smtp (~> 2.0)
104
- rubysl-net-telnet (~> 2.0)
105
- rubysl-nkf (~> 2.0)
106
- rubysl-observer (~> 2.0)
107
- rubysl-open-uri (~> 2.0)
108
- rubysl-open3 (~> 2.0)
109
- rubysl-openssl (~> 2.0)
110
- rubysl-optparse (~> 2.0)
111
- rubysl-ostruct (~> 2.0)
112
- rubysl-pathname (~> 2.0)
113
- rubysl-prettyprint (~> 2.0)
114
- rubysl-prime (~> 2.0)
115
- rubysl-profile (~> 2.0)
116
- rubysl-profiler (~> 2.0)
117
- rubysl-pstore (~> 2.0)
118
- rubysl-pty (~> 2.0)
119
- rubysl-rational (~> 2.0)
120
- rubysl-readline (~> 2.0)
121
- rubysl-resolv (~> 2.0)
122
- rubysl-rexml (~> 2.0)
123
- rubysl-rinda (~> 2.0)
124
- rubysl-rss (~> 2.0)
125
- rubysl-scanf (~> 2.0)
126
- rubysl-securerandom (~> 2.0)
127
- rubysl-set (~> 2.0)
128
- rubysl-shellwords (~> 2.0)
129
- rubysl-singleton (~> 2.0)
130
- rubysl-socket (~> 2.0)
131
- rubysl-stringio (~> 2.0)
132
- rubysl-strscan (~> 2.0)
133
- rubysl-sync (~> 2.0)
134
- rubysl-syslog (~> 2.0)
135
- rubysl-tempfile (~> 2.0)
136
- rubysl-thread (~> 2.0)
137
- rubysl-thwait (~> 2.0)
138
- rubysl-time (~> 2.0)
139
- rubysl-timeout (~> 2.0)
140
- rubysl-tmpdir (~> 2.0)
141
- rubysl-tsort (~> 2.0)
142
- rubysl-un (~> 2.0)
143
- rubysl-uri (~> 2.0)
144
- rubysl-weakref (~> 2.0)
145
- rubysl-webrick (~> 2.0)
146
- rubysl-xmlrpc (~> 2.0)
147
- rubysl-yaml (~> 2.0)
148
- rubysl-zlib (~> 2.0)
149
- rubysl-abbrev (2.0.4)
150
- rubysl-base64 (2.0.0)
151
- rubysl-benchmark (2.0.1)
152
- rubysl-bigdecimal (2.0.2)
153
- rubysl-cgi (2.0.1)
154
- rubysl-cgi-session (2.0.1)
155
- rubysl-cmath (2.0.0)
156
- rubysl-complex (2.0.0)
157
- rubysl-continuation (2.0.0)
158
- rubysl-coverage (2.0.3)
159
- rubysl-csv (2.0.2)
160
- rubysl-english (~> 2.0)
161
- rubysl-curses (2.0.1)
162
- rubysl-date (2.0.6)
163
- rubysl-delegate (2.0.1)
164
- rubysl-digest (2.0.3)
165
- rubysl-drb (2.0.1)
166
- rubysl-e2mmap (2.0.0)
167
- rubysl-english (2.0.0)
168
- rubysl-enumerator (2.0.0)
169
- rubysl-erb (2.0.1)
170
- rubysl-etc (2.0.3)
171
- ffi2-generators (~> 0.1)
172
- rubysl-expect (2.0.0)
173
- rubysl-fcntl (2.0.4)
174
- ffi2-generators (~> 0.1)
175
- rubysl-fiber (2.0.0)
176
- rubysl-fileutils (2.0.3)
177
- rubysl-find (2.0.1)
178
- rubysl-forwardable (2.0.1)
179
- rubysl-getoptlong (2.0.0)
180
- rubysl-gserver (2.0.0)
181
- rubysl-socket (~> 2.0)
182
- rubysl-thread (~> 2.0)
183
- rubysl-io-console (2.0.0)
184
- rubysl-io-nonblock (2.0.0)
185
- rubysl-io-wait (2.0.0)
186
- rubysl-ipaddr (2.0.0)
187
- rubysl-irb (2.0.4)
188
- rubysl-e2mmap (~> 2.0)
189
- rubysl-mathn (~> 2.0)
190
- rubysl-readline (~> 2.0)
191
- rubysl-thread (~> 2.0)
192
- rubysl-logger (2.0.0)
193
- rubysl-mathn (2.0.0)
194
- rubysl-matrix (2.1.0)
195
- rubysl-e2mmap (~> 2.0)
196
- rubysl-mkmf (2.0.1)
197
- rubysl-fileutils (~> 2.0)
198
- rubysl-shellwords (~> 2.0)
199
- rubysl-monitor (2.0.0)
200
- rubysl-mutex_m (2.0.0)
201
- rubysl-net-ftp (2.0.1)
202
- rubysl-net-http (2.0.4)
203
- rubysl-cgi (~> 2.0)
204
- rubysl-erb (~> 2.0)
205
- rubysl-singleton (~> 2.0)
206
- rubysl-net-imap (2.0.1)
207
- rubysl-net-pop (2.0.1)
208
- rubysl-net-protocol (2.0.1)
209
- rubysl-net-smtp (2.0.1)
210
- rubysl-net-telnet (2.0.0)
211
- rubysl-nkf (2.0.1)
212
- rubysl-observer (2.0.0)
213
- rubysl-open-uri (2.0.0)
214
- rubysl-open3 (2.0.0)
215
- rubysl-openssl (2.1.0)
216
- rubysl-optparse (2.0.1)
217
- rubysl-shellwords (~> 2.0)
218
- rubysl-ostruct (2.0.4)
219
- rubysl-pathname (2.0.0)
220
- rubysl-prettyprint (2.0.3)
221
- rubysl-prime (2.0.1)
222
- rubysl-profile (2.0.0)
223
- rubysl-profiler (2.0.1)
224
- rubysl-pstore (2.0.0)
225
- rubysl-pty (2.0.2)
226
- rubysl-rational (2.0.1)
227
- rubysl-readline (2.0.2)
228
- rubysl-resolv (2.1.0)
229
- rubysl-rexml (2.0.2)
230
- rubysl-rinda (2.0.1)
231
- rubysl-rss (2.0.0)
232
- rubysl-scanf (2.0.0)
233
- rubysl-securerandom (2.0.0)
234
- rubysl-set (2.0.1)
235
- rubysl-shellwords (2.0.0)
236
- rubysl-singleton (2.0.0)
237
- rubysl-socket (2.0.1)
238
- rubysl-stringio (2.0.0)
239
- rubysl-strscan (2.0.0)
240
- rubysl-sync (2.0.0)
241
- rubysl-syslog (2.0.1)
242
- ffi2-generators (~> 0.1)
243
- rubysl-tempfile (2.0.1)
244
- rubysl-thread (2.0.2)
245
- rubysl-thwait (2.0.0)
246
- rubysl-time (2.0.3)
247
- rubysl-timeout (2.0.0)
248
- rubysl-tmpdir (2.0.1)
249
- rubysl-tsort (2.0.1)
250
- rubysl-un (2.0.0)
251
- rubysl-fileutils (~> 2.0)
252
- rubysl-optparse (~> 2.0)
253
- rubysl-uri (2.0.0)
254
- rubysl-weakref (2.0.0)
255
- rubysl-webrick (2.0.0)
256
- rubysl-xmlrpc (2.0.0)
257
- rubysl-yaml (2.0.4)
258
- rubysl-zlib (2.0.1)
259
- slop (3.5.0)
57
+ rspec-support (~> 3.2.0)
58
+ rspec-mocks (3.2.1)
59
+ diff-lcs (>= 1.2.0, < 2.0)
60
+ rspec-support (~> 3.2.0)
61
+ rspec-support (3.2.2)
62
+ shellany (0.0.1)
63
+ slop (3.6.0)
260
64
  thor (0.19.1)
261
- timers (1.1.0)
65
+ timers (4.0.1)
66
+ hitimes
262
67
 
263
68
  PLATFORMS
264
69
  ruby
265
70
 
266
71
  DEPENDENCIES
267
- byebug
268
72
  dbf!
269
73
  guard
270
74
  guard-rspec
271
75
  rspec
272
- rubysl
@@ -1,9 +1,9 @@
1
1
  gemspec
2
2
  source 'https://rubygems.org'
3
3
 
4
- gem 'rubysl', :platform => :rbx
5
4
 
6
5
  group :test do
6
+ gem 'rubysl', :platform => :rbx
7
7
  gem 'rspec'
8
8
  gem 'codeclimate-test-reporter', :require => false, :platform => :ruby
9
9
  end
data/Guardfile CHANGED
@@ -1,8 +1,11 @@
1
1
  # A sample Guardfile
2
2
  # More info at https://github.com/guard/guard#readme
3
3
 
4
- guard :rspec do
4
+ guard :rspec, cmd: 'bundle exec rspec' do
5
5
  watch(%r{^spec/.+_spec\.rb$})
6
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
- watch('spec/spec_helper.rb') { "spec" }
6
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch(%r{^lib/(.+)\.rb$}) { 'spec' }
8
+ watch('spec/spec_helper.rb') { 'spec' }
9
+ watch(/spec\/fixtures\/(.+)/) { 'spec' }
10
+ watch('Guardfile') { 'spec' }
8
11
  end
data/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  [![Code Quality](http://img.shields.io/codeclimate/github/infused/dbf.svg?style=flat)](https://codeclimate.com/github/infused/dbf)
5
5
  [![Test Coverage](http://img.shields.io/codeclimate/coverage/github/infused/dbf.svg?style=flat)](https://codeclimate.com/github/infused/dbf)
6
6
  [![Dependency Status](http://img.shields.io/gemnasium/infused/dbf.svg?style=flat)](https://gemnasium.com/infused/dbf)
7
+ [![Issues](https://img.shields.io/github/issues/infused/dbf.svg)](https://github.com/infused/dbf/issues)
8
+ [![Total Downloads](https://img.shields.io/gem/dt/dbf.svg)](https://rubygems.org/gems/dbf/)
7
9
 
8
10
  DBF is a small fast library for reading dBase, xBase, Clipper and FoxPro
9
11
  database files
@@ -18,7 +20,7 @@ database files
18
20
 
19
21
  DBF is tested to work with the following versions of ruby:
20
22
 
21
- * MRI Ruby 1.8.6, 1.8.7, 1.9.1, 1.9.2, 1.9.3, 2.0.x, 2.1.x
23
+ * MRI Ruby 1.8.6, 1.8.7, 1.9.1, 1.9.2, 1.9.3, 2.0.x, 2.1.x, 2.2.x
22
24
  * JRuby 1.7.x (1.8 and 1.9 modes), JRuby head
23
25
  * REE 1.8.7
24
26
  * Rubinius 2.1+
@@ -98,6 +100,23 @@ widgets.find :first, :slot_number => 's42'
98
100
  widgets.find(10)
99
101
  ```
100
102
 
103
+ ## Enumeration
104
+
105
+ DBF::Table is a Ruby Enumerable. This means you can use any Enumerable method.
106
+ This means that you get a bunch of traversal, searching and sorting methods
107
+ for free. For example, let's get only records created before January 1st, 2015:
108
+
109
+ ```ruby
110
+ widgets.select { |w| w.created_date < Date.new(2015, 1, 1) }
111
+ ```
112
+
113
+ Or custom sorting:
114
+
115
+ ```ruby
116
+ widgets.sort_by { |w| w.created_date }
117
+ ```
118
+
119
+
101
120
  ## Encodings (Code Pages)
102
121
 
103
122
  dBase supports encoding non-english characters in different formats.
@@ -158,6 +177,19 @@ Dump all records to a CSV file:
158
177
 
159
178
  dbf -c books.dbf > books.csv
160
179
 
180
+ ## Reading a Visual Foxpro database (v8, v9)
181
+
182
+ A special Database::Foxpro class is available to read Visual Foxpro container files (.dbc-files). When using this class,
183
+ long fieldnames are supported and tables can be references without using names.
184
+
185
+ ```ruby
186
+ require 'dbf'
187
+
188
+ contacts = DBF::Database::Foxpro.new('contactdatabase.dbc').contacts
189
+ mycontact = contacts.record(1).spouses_interests
190
+ ```
191
+
192
+
161
193
  ## dBase version compatibility
162
194
 
163
195
  The basic dBase data types are generally supported well. Support for the
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- require 'bundler/setup';
1
+ require 'bundler/setup'
2
2
  Bundler.setup(:default, :development)
3
3
 
4
4
  require 'rspec/core/rake_task'
@@ -1,5 +1,5 @@
1
1
  lib = File.expand_path('../lib/', __FILE__)
2
- $:.unshift lib unless $:.include?(lib)
2
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
3
3
  require 'dbf/version'
4
4
 
5
5
  Gem::Specification.new do |s|
@@ -12,6 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.description = 'A small fast library for reading dBase, xBase, Clipper and FoxPro database files.'
13
13
  s.license = 'MIT'
14
14
 
15
+ s.bindir = 'bin'
15
16
  s.executables = ['dbf']
16
17
  s.rdoc_options = ['--charset=UTF-8']
17
18
  s.extra_rdoc_files = ['README.md', 'CHANGELOG.md', 'LICENSE']
data/lib/dbf.rb CHANGED
@@ -17,3 +17,4 @@ require 'dbf/memo/base'
17
17
  require 'dbf/memo/dbase3'
18
18
  require 'dbf/memo/dbase4'
19
19
  require 'dbf/memo/foxpro'
20
+ require 'dbf/database/foxpro'
@@ -24,12 +24,12 @@ module DBF
24
24
  @version = table.version
25
25
  @encoding = table.encoding
26
26
 
27
- unless length > 0
28
- raise LengthError, "field length must be greater than 0"
27
+ unless length >= 0
28
+ raise LengthError, 'field length must be 0 or greater'
29
29
  end
30
30
 
31
31
  if @name.empty?
32
- raise NameError, "column name cannot be empty"
32
+ raise NameError, 'column name cannot be empty'
33
33
  end
34
34
  end
35
35
 
@@ -38,6 +38,8 @@ module DBF
38
38
  # @param [String] value
39
39
  # @return [Fixnum, Float, Date, DateTime, Boolean, String]
40
40
  def type_cast(value)
41
+ return nil if length == 0
42
+
41
43
  case type
42
44
  when 'N' then unpack_number(value)
43
45
  when 'I' then unpack_unsigned_long(value)
@@ -0,0 +1,109 @@
1
+ module DBF
2
+ # DBF::Database::Foxpro is the primary interface to a Visual Foxpro database container (.dbc file).
3
+ # When using this database container, long fieldnames are supported, and you can reference tables
4
+ # directly instead of instantiating Table objects yourself.
5
+ # Table references are created based on the filename, but it this class tries to correct the
6
+ # table filenames because they could be wrong for case sensitive filesystems, e.g. when
7
+ # a foxpro database is uploaded to a linux server.
8
+ module Database
9
+
10
+ class Foxpro
11
+ # Opens a DBF::Database::Foxpro
12
+ # Examples:
13
+ # # working with a database stored on the filesystem
14
+ # db = DBF::Database::Foxpro.new 'path_to_db/database.dbc'
15
+ #
16
+ # # Calling a table
17
+ # contacts = db.contacts.record(0)
18
+ def initialize(path)
19
+ begin
20
+ @path = path
21
+ @dirname = File.dirname(@path)
22
+ @db = DBF::Table.new(@path)
23
+ @tables = extract_dbc_data
24
+
25
+ rescue Errno::ENOENT
26
+ raise DBF::FileNotFoundError.new("file not found: #{data}")
27
+ end
28
+ end
29
+
30
+ def table_names
31
+ @tables.keys
32
+ end
33
+
34
+ # Returns table with given name
35
+ # @return Table
36
+ def table(name)
37
+ Table.new(table_path name) do |table|
38
+ table.long_names = @tables[name]
39
+ end
40
+ end
41
+
42
+ # Searches the database directory for the table's dbf file
43
+ # and returns the absolute path. Ensures case-insensitivity
44
+ # on any platform.
45
+ # @return String
46
+ def table_path(name)
47
+ example = File.join(@dirname, "#{name}.dbf")
48
+ glob = File.join(@dirname, '*')
49
+ path = Dir.glob(glob).find { |match| match.downcase == example.downcase }
50
+
51
+ unless path && File.exist?(path)
52
+ raise DBF::FileNotFoundError.new("related table not found: #{name}")
53
+ end
54
+
55
+ path
56
+ end
57
+
58
+ def method_missing(method, *args) # nodoc
59
+ if index = table_names.index(method.to_s)
60
+ table method.to_s
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # This method extracts the data from the database container. This is just an ordinary table
69
+ # with a treelike structure. Field definitions are in the same order as in the linked tables
70
+ # but only the long name is provided.
71
+ def extract_dbc_data # nodoc
72
+ data = {}
73
+ @db.each do |record|
74
+ next unless record
75
+
76
+ case record.objecttype
77
+ when 'Table'
78
+ # This is a related table
79
+ data[record.objectid] = {:name => record.objectname, :fields => []}
80
+ when 'Field'
81
+ # This is a related field. The parentid points to the table object.
82
+ # Create using the parentid if the parentid is still unknown.
83
+ data[record.parentid] ||= {:name => "UNKNOWN", :fields => []}
84
+ data[record.parentid][:fields] << record.objectname
85
+ end
86
+ end
87
+
88
+ Hash[data.values.map {|v| [v[:name], v[:fields]] }]
89
+ end
90
+
91
+ end
92
+
93
+ class Table < DBF::Table
94
+ attr_accessor :long_names
95
+
96
+ def build_columns # nodoc
97
+ columns = super
98
+
99
+ # modify the column definitions to use the long names as the
100
+ # columnname property is readonly, recreate the column definitions
101
+ columns.map do |column|
102
+ long_name = long_names[columns.index(column)]
103
+ column_class.new(self, long_name, column.type, column.length, column.decimal)
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+ end
@@ -68,6 +68,7 @@ module DBF
68
68
  @header = Header.new(@data.read(DBF_HEADER_SIZE), supports_encoding?)
69
69
  @encoding = encoding || header.encoding
70
70
  @memo = open_memo(data, memo)
71
+ yield self if block_given?
71
72
  rescue Errno::ENOENT => error
72
73
  raise DBF::FileNotFoundError.new("file not found: #{data}")
73
74
  end
@@ -1,3 +1,3 @@
1
1
  module DBF
2
- VERSION = '2.0.9'
2
+ VERSION = '2.0.10'
3
3
  end
@@ -24,10 +24,9 @@ describe DBF::Column::Dbase do
24
24
  expect(column.decimal).to eq 0
25
25
  end
26
26
 
27
- describe 'with length of 0' do
28
- it 'raises DBF::Column::LengthError' do
29
- expect { DBF::Column::Dbase.new table, "ColumnName", "N", 0, 0 }.to raise_error(DBF::Column::LengthError)
30
- end
27
+ it 'accepts length of 0' do
28
+ column = DBF::Column::Dbase.new table, "ColumnName", "N", 0, 0
29
+ expect(column.length).to eq 0
31
30
  end
32
31
 
33
32
  describe 'with length less than 0' do
@@ -45,6 +44,13 @@ describe DBF::Column::Dbase do
45
44
 
46
45
  context '#type_cast' do
47
46
  context 'with type N (number)' do
47
+ context 'and 0 length' do
48
+ it 'returns nil' do
49
+ column = DBF::Column::Dbase.new table, "ColumnName", "N", 0, 0
50
+ expect(column.type_cast('')).to be_nil
51
+ end
52
+ end
53
+
48
54
  context 'and 0 decimals' do
49
55
  it 'casts value to Fixnum' do
50
56
  value = '135'
@@ -65,6 +71,13 @@ describe DBF::Column::Dbase do
65
71
  end
66
72
 
67
73
  context 'with type F (float)' do
74
+ context 'and 0 length' do
75
+ it 'returns nil' do
76
+ column = DBF::Column::Dbase.new table, "ColumnName", "F", 0, 0
77
+ expect(column.type_cast('')).to be_nil
78
+ end
79
+ end
80
+
68
81
  it 'casts value to Float' do
69
82
  value = '135'
70
83
  column = DBF::Column::Dbase.new table, "ColumnName", "F", 3, 0
@@ -74,6 +87,13 @@ describe DBF::Column::Dbase do
74
87
  end
75
88
 
76
89
  context 'with type I (integer)' do
90
+ context 'and 0 length' do
91
+ it 'returns nil' do
92
+ column = DBF::Column::Dbase.new table, "ColumnName", "I", 0, 0
93
+ expect(column.type_cast('')).to be_nil
94
+ end
95
+ end
96
+
77
97
  it "casts value to Fixnum" do
78
98
  value = "\203\171\001\000"
79
99
  column = DBF::Column::Dbase.new table, "ColumnName", "I", 3, 0
@@ -96,6 +116,13 @@ describe DBF::Column::Dbase do
96
116
  it "casts value other than 't' or 'y' to false" do
97
117
  expect(column.type_cast('n')).to be false
98
118
  end
119
+
120
+ context 'and 0 length' do
121
+ it 'returns nil' do
122
+ column = DBF::Column::Dbase.new table, "ColumnName", "L", 0, 0
123
+ expect(column.type_cast('')).to be_nil
124
+ end
125
+ end
99
126
  end
100
127
 
101
128
  context 'with type T (datetime)' do
@@ -127,6 +154,13 @@ describe DBF::Column::Dbase do
127
154
  expect(column.type_cast("Nl%\000\000A\000\999")).to be_nil
128
155
  end
129
156
  end
157
+
158
+ context 'and 0 length' do
159
+ it 'returns nil' do
160
+ column = DBF::Column::Dbase.new table, "ColumnName", "T", 0, 0
161
+ expect(column.type_cast('')).to be_nil
162
+ end
163
+ end
130
164
  end
131
165
 
132
166
  context 'with type D (date)' do
@@ -145,6 +179,13 @@ describe DBF::Column::Dbase do
145
179
  expect(column.type_cast("0")).to be_nil
146
180
  end
147
181
  end
182
+
183
+ context 'and 0 length' do
184
+ it 'returns nil' do
185
+ column = DBF::Column::Dbase.new table, "ColumnName", "D", 0, 0
186
+ expect(column.type_cast('')).to be_nil
187
+ end
188
+ end
148
189
  end
149
190
 
150
191
  context 'with type M (memo)' do
@@ -159,6 +200,13 @@ describe DBF::Column::Dbase do
159
200
  expect(column.type_cast(nil)).to be_a(NilClass)
160
201
  expect(column.type_cast(nil)).to be_nil
161
202
  end
203
+
204
+ context 'and 0 length' do
205
+ it 'returns nil' do
206
+ column = DBF::Column::Dbase.new table, "ColumnName", "M", 0, 0
207
+ expect(column.type_cast('')).to be_nil
208
+ end
209
+ end
162
210
  end
163
211
  end
164
212
 
@@ -169,6 +217,13 @@ describe DBF::Column::Dbase do
169
217
  expect(column.type_cast(" \xBF\x02\x00\x00\x00\x00\x00")).to be_a(Float)
170
218
  expect(column.type_cast(" \xBF\x02\x00\x00\x00\x00\x00")).to eq 18.0
171
219
  end
220
+
221
+ context 'and 0 length' do
222
+ it 'returns nil' do
223
+ column = DBF::Column::Dbase.new table, "ColumnName", "Y", 0, 0
224
+ expect(column.type_cast('')).to be_nil
225
+ end
226
+ end
172
227
  end
173
228
 
174
229
  context "#schema_definition" do
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe DBF::Database::Foxpro do
4
+ let(:dbf_path) { fixture_path('foxprodb/FOXPRO-DB-TEST.DBC') }
5
+ let(:db) { DBF::Database::Foxpro.new(dbf_path) }
6
+
7
+ describe '#initialize' do
8
+ describe 'when given a path to an existing dbc file' do
9
+ it 'does not raise an error' do
10
+ expect { DBF::Database::Foxpro.new dbf_path }.to_not raise_error
11
+ end
12
+ end
13
+
14
+ describe 'when given a path to a non-existent dbf file' do
15
+ it 'raises a DBF::FileNotFound error' do
16
+ expect { DBF::Database::Foxpro.new 'x' }.to raise_error(DBF::FileNotFoundError, 'file not found: x')
17
+ end
18
+ end
19
+
20
+ describe 'it loads the example db correctly' do
21
+ it 'shows a correct list of tables' do
22
+ expect(db.table_names.sort).to eq(%w(contacts calls setup types).sort)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '#table' do
28
+ describe "when accessing related tables" do
29
+ let(:db) { DBF::Database::Foxpro.new(dbf_path) }
30
+
31
+ it 'loads an existing related table' do
32
+ expect(db.contacts.record_count).to eq 5
33
+ end
34
+
35
+ it 'supports a long table field name' do
36
+ expect(db.contacts.record(1).spouses_interests).to eq 'Tennis, golf'
37
+ end
38
+
39
+ it 'loads an existing related table with wrong filename casing' do
40
+ expect(db.calls.record_count).to eq 16
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#table_path' do
46
+ it 'returns an absolute path' do
47
+ expect(db.table_path('contacts')).to eq File.expand_path('spec/fixtures/foxprodb/contacts.dbf')
48
+ end
49
+ end
50
+
51
+ end
@@ -4,22 +4,25 @@ describe DBF::Record do
4
4
 
5
5
  describe '#to_a' do
6
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')) }
7
9
 
8
10
  it 'should return an ordered array of attribute values' do
9
11
  record = table.record(0)
10
- expect(record.to_a).to eq [87, 2, 0, 0, 87, "1", "Assorted Petits Fours", "graphics/00000001/t_1.jpg", "graphics/00000001/1.jpg", 0.0, 0.0, "Our Original assortment...a little taste of heaven for everyone. Let us\r\nselect a special assortment of our chocolate and pastel favorites for you.\r\nEach petit four is its own special hand decorated creation. Multi-layers of\r\nmoist cake with combinations of specialty fillings create memorable cake\r\nconfections. Varietes include; Luscious Lemon, Strawberry Hearts, White\r\nChocolate, Mocha Bean, Roasted Almond, Triple Chocolate, Chocolate Hazelnut,\r\nGrand Orange, Plum Squares, Milk chocolate squares, and Raspberry Blanc.", 5.51, true, true]
12
+ expect(record.to_a).to eq record_0
11
13
 
12
14
  record = table.record(9)
13
- expect(record.to_a).to eq [34, 1, 0, 0, 34, "AB01", "Apricot Brandy Fruitcake", "graphics/00000001/t_AB01.jpg", "graphics/00000001/AB01.jpg", 37.95, 37.95, "Once tasted you will understand why we won The\r\nBoston Herald's Fruitcake Taste-off. Judges liked its generous size,\r\nluscious appearance, moist texture and fruit to cake ratio ... commented one\r\njudge \"It's a lip Smacker!\" Our signature fruitcake is baked with carefully\r\nselected ingredients that will be savored until the last moist crumb is\r\ndevoured each golden slice is brimming with Australian glaced apricots,\r\ntoasted pecans, candied orange peel, and currants, folded gently into a\r\nbrandy butter batter and slowly baked to perfection and then generously\r\nimbibed with \"Holiday Spirits\". Presented in a gift tin. (3lbs. 4oz)", 0.0, false, true]
15
+ expect(record.to_a).to eq record_9
14
16
  end
15
17
 
16
18
  describe 'with missing memo file' do
17
19
  describe 'when opening a path' do
18
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
22
 
20
23
  it 'returns nil values for memo fields' do
21
24
  record = table.record(0)
22
- expect(record.to_a).to eq [87, 2, 0, 0, 87, "1", "Assorted Petits Fours", "graphics/00000001/t_1.jpg", "graphics/00000001/1.jpg", 0.0, 0.0, nil, 5.51, true, true]
25
+ expect(record.to_a).to eq record_0
23
26
  end
24
27
  end
25
28
  end
@@ -27,10 +30,11 @@ describe DBF::Record do
27
30
  describe 'when opening StringIO' do
28
31
  let(:data) { StringIO.new(File.read(fixture_path('dbase_83_missing_memo.dbf'))) }
29
32
  let(:table) { DBF::Table.new(data) }
33
+ let(:record_0) { YAML.load_file(fixture_path('dbase_83_missing_memo_record_0.yml')) }
30
34
 
31
35
  it 'returns nil values for memo fields' do
32
36
  record = table.record(0)
33
- expect(record.to_a).to eq [87, 2, 0, 0, 87, "1", "Assorted Petits Fours", "graphics/00000001/t_1.jpg", "graphics/00000001/1.jpg", 0.0, 0.0, nil, 5.51, true, true]
37
+ expect(record.to_a).to eq record_0
34
38
  end
35
39
  end
36
40
  end
@@ -0,0 +1,16 @@
1
+ ---
2
+ - 87
3
+ - 2
4
+ - 0
5
+ - 0
6
+ - 87
7
+ - '1'
8
+ - Assorted Petits Fours
9
+ - graphics/00000001/t_1.jpg
10
+ - graphics/00000001/1.jpg
11
+ - 0.0
12
+ - 0.0
13
+ -
14
+ - 5.51
15
+ - true
16
+ - true
@@ -0,0 +1,16 @@
1
+ ---
2
+ - 87
3
+ - 2
4
+ - 0
5
+ - 0
6
+ - 87
7
+ - '1'
8
+ - Assorted Petits Fours
9
+ - graphics/00000001/t_1.jpg
10
+ - graphics/00000001/1.jpg
11
+ - 0.0
12
+ - 0.0
13
+ - "Our Original assortment...a little taste of heaven for everyone. Let us\r\nselect a special assortment of our chocolate and pastel favorites for you.\r\nEach petit four is its own special hand decorated creation. Multi-layers of\r\nmoist cake with combinations of specialty fillings create memorable cake\r\nconfections. Varietes include; Luscious Lemon, Strawberry Hearts, White\r\nChocolate, Mocha Bean, Roasted Almond, Triple Chocolate, Chocolate Hazelnut,\r\nGrand Orange, Plum Squares, Milk chocolate squares, and Raspberry Blanc."
14
+ - 5.51
15
+ - true
16
+ - true
@@ -0,0 +1,23 @@
1
+ ---
2
+ - 34
3
+ - 1
4
+ - 0
5
+ - 0
6
+ - 34
7
+ - AB01
8
+ - Apricot Brandy Fruitcake
9
+ - graphics/00000001/t_AB01.jpg
10
+ - graphics/00000001/AB01.jpg
11
+ - 37.95
12
+ - 37.95
13
+ - "Once tasted you will understand why we won The\r\nBoston Herald's Fruitcake Taste-off.
14
+ Judges liked its generous size,\r\nluscious appearance, moist texture and fruit
15
+ to cake ratio ... commented one\r\njudge \"It's a lip Smacker!\" Our signature fruitcake
16
+ is baked with carefully\r\nselected ingredients that will be savored until the last
17
+ moist crumb is\r\ndevoured each golden slice is brimming with Australian glaced
18
+ apricots,\r\ntoasted pecans, candied orange peel, and currants, folded gently into
19
+ a\r\nbrandy butter batter and slowly baked to perfection and then generously\r\nimbibed
20
+ with \"Holiday Spirits\". Presented in a gift tin. (3lbs. 4oz)"
21
+ - 0.0
22
+ - false
23
+ - true
@@ -5,7 +5,9 @@ rescue LoadError
5
5
  end
6
6
 
7
7
  require 'dbf'
8
+ require 'yaml'
8
9
  require 'rspec'
10
+ require 'fileutils'
9
11
 
10
12
  Encoding.default_external = "UTF-8" if defined?(Encoding)
11
13
 
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: 2.0.9
4
+ version: 2.0.10
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-03-05 00:00:00.000000000 Z
11
+ date: 2015-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fastercsv
@@ -52,6 +52,7 @@ files:
52
52
  - lib/dbf/column/base.rb
53
53
  - lib/dbf/column/dbase.rb
54
54
  - lib/dbf/column/foxpro.rb
55
+ - lib/dbf/database/foxpro.rb
55
56
  - lib/dbf/encodings.rb
56
57
  - lib/dbf/header.rb
57
58
  - lib/dbf/memo/base.rb
@@ -63,6 +64,7 @@ files:
63
64
  - lib/dbf/table.rb
64
65
  - lib/dbf/version.rb
65
66
  - spec/dbf/column_spec.rb
67
+ - spec/dbf/database_spec.rb
66
68
  - spec/dbf/file_formats_spec.rb
67
69
  - spec/dbf/record_spec.rb
68
70
  - spec/dbf/table_spec.rb
@@ -74,11 +76,27 @@ files:
74
76
  - spec/fixtures/dbase_83.dbf
75
77
  - spec/fixtures/dbase_83.dbt
76
78
  - spec/fixtures/dbase_83_missing_memo.dbf
79
+ - spec/fixtures/dbase_83_missing_memo_record_0.yml
80
+ - spec/fixtures/dbase_83_record_0.yml
81
+ - spec/fixtures/dbase_83_record_9.yml
77
82
  - spec/fixtures/dbase_83_schema.txt
78
83
  - spec/fixtures/dbase_8b.dbf
79
84
  - spec/fixtures/dbase_8b.dbt
80
85
  - spec/fixtures/dbase_f5.dbf
81
86
  - spec/fixtures/dbase_f5.fpt
87
+ - spec/fixtures/foxprodb/FOXPRO-DB-TEST.DBC
88
+ - spec/fixtures/foxprodb/FOXPRO-DB-TEST.DCT
89
+ - spec/fixtures/foxprodb/FOXPRO-DB-TEST.DCX
90
+ - spec/fixtures/foxprodb/calls.CDX
91
+ - spec/fixtures/foxprodb/calls.FPT
92
+ - spec/fixtures/foxprodb/calls.dbf
93
+ - spec/fixtures/foxprodb/contacts.CDX
94
+ - spec/fixtures/foxprodb/contacts.FPT
95
+ - spec/fixtures/foxprodb/contacts.dbf
96
+ - spec/fixtures/foxprodb/setup.CDX
97
+ - spec/fixtures/foxprodb/setup.dbf
98
+ - spec/fixtures/foxprodb/types.CDX
99
+ - spec/fixtures/foxprodb/types.dbf
82
100
  - spec/spec_helper.rb
83
101
  homepage: http://github.com/infused/dbf
84
102
  licenses:
@@ -101,12 +119,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
119
  version: 1.3.0
102
120
  requirements: []
103
121
  rubyforge_project:
104
- rubygems_version: 2.4.3
122
+ rubygems_version: 2.4.6
105
123
  signing_key:
106
124
  specification_version: 4
107
125
  summary: Read xBase files
108
126
  test_files:
109
127
  - spec/dbf/column_spec.rb
128
+ - spec/dbf/database_spec.rb
110
129
  - spec/dbf/file_formats_spec.rb
111
130
  - spec/dbf/record_spec.rb
112
131
  - spec/dbf/table_spec.rb