dbf 3.0.0 → 3.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7051deaf2cb9c0f103fe91bb811ac576453e1c4
4
- data.tar.gz: f0c06a0ca2a0cd6b2ac8e29eaa8d14f2b496a5d4
3
+ metadata.gz: 5eb7b6698bb3117ab55add9db5b4b3cb768c82e8
4
+ data.tar.gz: bfbc755dbd887584c102fdc249583c859c14011c
5
5
  SHA512:
6
- metadata.gz: 4a963a10bdb5e02c5dce9b8b7f5d8446e636b4f07ab20a07c19611617edd5d3b5f461df2537ce8be97473a1ceafc2f9cd94a9f8bfcaff6fe3e1acbf4828bfab6
7
- data.tar.gz: 1dbcdf3f5a5a84cc7fba894556f120e0fc23b4ca46572cf2ef9cb8fe4dac04a24ecf009f9e34786a75be8ec38f9144b38682496c22a3d4d61aee66d0ad97ab10
6
+ metadata.gz: 84b5da89c2953f28a559d47271614afeb452f0fbd3d770881634a4731f86279f327a67095b6191fed3e7916d886a498b880f4d69a002264b9a5fee8f62d32e09
7
+ data.tar.gz: b67fa325a58fefd66b344bba83dc58718f4ecc07bdf5676e1f0ce2a698fce1907628069fb75a75d20ca6772988c6c21f1c242949f080786f3007f18d4762549b
@@ -1,3 +1,7 @@
1
+ # 3.0.0
2
+ - Requires Ruby version 2.0 and above
3
+ - Support the (G) General Foxpro field type
4
+
1
5
  # 2.0.13
2
6
  - Support 64-bit currency signed currency values
3
7
  (see https://github.com/infused/dbf/pull/71)
@@ -1,17 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dbf (2.0.12)
5
- fastercsv (~> 1.5)
4
+ dbf (3.0.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
9
8
  specs:
10
9
  coderay (1.1.0)
11
10
  diff-lcs (1.2.5)
12
- fastercsv (1.5.5)
13
11
  ffi (1.9.10)
14
- ffi (1.9.10-java)
15
12
  formatador (0.2.5)
16
13
  guard (2.13.0)
17
14
  formatador (>= 0.2.4)
@@ -33,42 +30,34 @@ GEM
33
30
  lumberjack (1.0.9)
34
31
  method_source (0.8.2)
35
32
  nenv (0.2.0)
36
- notiffany (0.0.7)
33
+ notiffany (0.0.8)
37
34
  nenv (~> 0.1)
38
35
  shellany (~> 0.0)
39
- pry (0.10.1)
36
+ pry (0.10.2)
40
37
  coderay (~> 1.1.0)
41
38
  method_source (~> 0.8.1)
42
39
  slop (~> 3.4)
43
- pry (0.10.1-java)
44
- coderay (~> 1.1.0)
45
- method_source (~> 0.8.1)
46
- slop (~> 3.4)
47
- spoon (~> 0.0)
48
- rb-fsevent (0.9.5)
40
+ rb-fsevent (0.9.6)
49
41
  rb-inotify (0.9.5)
50
42
  ffi (>= 0.5.0)
51
- rspec (3.3.0)
52
- rspec-core (~> 3.3.0)
53
- rspec-expectations (~> 3.3.0)
54
- rspec-mocks (~> 3.3.0)
55
- rspec-core (3.3.2)
56
- rspec-support (~> 3.3.0)
57
- rspec-expectations (3.3.1)
43
+ rspec (3.4.0)
44
+ rspec-core (~> 3.4.0)
45
+ rspec-expectations (~> 3.4.0)
46
+ rspec-mocks (~> 3.4.0)
47
+ rspec-core (3.4.1)
48
+ rspec-support (~> 3.4.0)
49
+ rspec-expectations (3.4.0)
58
50
  diff-lcs (>= 1.2.0, < 2.0)
59
- rspec-support (~> 3.3.0)
60
- rspec-mocks (3.3.2)
51
+ rspec-support (~> 3.4.0)
52
+ rspec-mocks (3.4.0)
61
53
  diff-lcs (>= 1.2.0, < 2.0)
62
- rspec-support (~> 3.3.0)
63
- rspec-support (3.3.0)
54
+ rspec-support (~> 3.4.0)
55
+ rspec-support (3.4.0)
64
56
  shellany (0.0.1)
65
57
  slop (3.6.0)
66
- spoon (0.0.4)
67
- ffi
68
58
  thor (0.19.1)
69
59
 
70
60
  PLATFORMS
71
- java
72
61
  ruby
73
62
 
74
63
  DEPENDENCIES
@@ -76,3 +65,6 @@ DEPENDENCIES
76
65
  guard
77
66
  guard-rspec
78
67
  rspec
68
+
69
+ BUNDLED WITH
70
+ 1.10.6
@@ -1,9 +1,7 @@
1
1
  gemspec
2
2
  source 'https://rubygems.org'
3
3
 
4
-
5
4
  group :test do
6
- gem 'rubysl', :platform => :rbx
7
5
  gem 'rspec'
8
- gem 'codeclimate-test-reporter', :require => false, :platform => :ruby
6
+ gem 'codeclimate-test-reporter', require: false, platform: :ruby
9
7
  end
data/README.md CHANGED
@@ -28,10 +28,18 @@ DBF is tested to work with the following versions of ruby:
28
28
 
29
29
  ## Installation
30
30
 
31
+ Install the gem manually:
32
+
31
33
  ```
32
34
  gem install dbf
33
35
  ```
34
36
 
37
+ Or add to your Gemfile:
38
+
39
+ ```ruby
40
+ gem 'dbf'
41
+ ```
42
+
35
43
  ## Basic Usage
36
44
 
37
45
  Open a DBF file:
@@ -87,7 +95,7 @@ all records will be loaded into memory.
87
95
 
88
96
  ```ruby
89
97
  # find all records with slot_number equal to s42
90
- widgets.find(:all, :slot_number => 's42') do |widget|
98
+ widgets.find(:all, slot_number: 's42') do |widget|
91
99
  # the record will be nil if deleted, but not yet pruned from the database
92
100
  if widget
93
101
  puts widget.serial_number
@@ -95,7 +103,7 @@ widgets.find(:all, :slot_number => 's42') do |widget|
95
103
  end
96
104
 
97
105
  # find the first record with slot_number equal to s42
98
- widgets.find :first, :slot_number => 's42'
106
+ widgets.find :first, slot_number: 's42'
99
107
 
100
108
  # find record number 10
101
109
  widgets.find(10)
@@ -149,7 +157,7 @@ class CreateBooks < ActiveRecord::Migration
149
157
 
150
158
  Book.reset_column_information
151
159
  table.each do |record|
152
- Book.create(:title => record.title, :author => record.author)
160
+ Book.create(title: record.title, author: record.author)
153
161
  end
154
162
  end
155
163
 
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ RSpec::Core::RakeTask.new :specdoc do |t|
10
10
  t.rspec_opts = %w(-fl)
11
11
  end
12
12
 
13
- task :default => :spec
13
+ task default: :spec
14
14
 
15
15
  desc "Open an irb session preloaded with this library"
16
16
  task :console do
@@ -13,9 +13,9 @@
13
13
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
14
14
  | 07 | Visual Objects 1.x | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - | - | - |
15
15
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
16
- | 30 | Visual FoxPro | Y | Y | Y | Y | - | Y | Y | N | N | N | N | Y | N | N | N | N | - |
16
+ | 30 | Visual FoxPro | Y | Y | Y | Y | - | Y | Y | Y | N | Y | N | Y | N | N | N | N | - |
17
17
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
18
- | 31 | Visual FoxPro with AutoIncrement | Y | Y | Y | Y | Y | Y | Y | N | N | N | N | Y | N | N | N | N | N |
18
+ | 31 | Visual FoxPro with AutoIncrement | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
19
19
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
20
20
  | 7b | dBase IV with memo file | Y | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | - | - | - |
21
21
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
@@ -27,9 +27,9 @@
27
27
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
28
28
  | 8e | dBase IV with SQL table | Y | Y | Y | Y | Y | - | - | - | - | - | - | - | - | N | - | - | - |
29
29
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
30
- | f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y | N | N | N | N | Y | N | N | N | N | N |
30
+ | f5 | FoxPro with memo file | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
31
31
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
32
- | fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y | N | N | N | N | Y | N | N | N | N | N |
32
+ | fb | FoxPro without memo file | Y | Y | Y | Y | - | Y | Y | Y | N | Y | N | Y | N | N | N | N | N |
33
33
  +---------+-----------------------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
34
34
 
35
35
  Data type descriptions
@@ -50,4 +50,4 @@ Data type descriptions
50
50
  * X = SQL compat
51
51
  * @ = Timestamp
52
52
  * O = Double
53
- * + = Autoincrement
53
+ * + = Autoincrement
data/lib/dbf.rb CHANGED
@@ -5,9 +5,8 @@ require 'json'
5
5
 
6
6
  require 'dbf/schema'
7
7
  require 'dbf/record'
8
- require 'dbf/column/base'
9
- require 'dbf/column/dbase'
10
- require 'dbf/column/foxpro'
8
+ require 'dbf/column'
9
+ require 'dbf/column_type'
11
10
  require 'dbf/encodings'
12
11
  require 'dbf/header'
13
12
  require 'dbf/table'
@@ -0,0 +1,161 @@
1
+ module DBF
2
+ class 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
+ attr_reader :table, :name, :type, :length, :decimal
10
+
11
+ # Initialize a new DBF::Column
12
+ #
13
+ # @param [String] name
14
+ # @param [String] type
15
+ # @param [Fixnum] length
16
+ # @param [Fixnum] decimal
17
+ def initialize(table, name, type, length, decimal)
18
+ @table = table
19
+ @name = clean(name)
20
+ @type = type
21
+ @length = length
22
+ @decimal = decimal
23
+ @version = table.version
24
+ @encoding = table.encoding
25
+
26
+ validate_length
27
+ validate_name
28
+ end
29
+
30
+ # Cast value to native type
31
+ #
32
+ # @param [String] value
33
+ # @return [Fixnum, Float, Date, DateTime, Boolean, String]
34
+ def type_cast(value)
35
+ return nil if length == 0
36
+
37
+ klass = type_cast_class[type.to_sym]
38
+ cast_value = klass.new(value, decimal).type_cast
39
+ binary? ? cast_value : encode(cast_value)
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
+ # Returns true if the column contains binary data
50
+ #
51
+ # @return [Boolean]
52
+ def binary?
53
+ @binary ||= type == 'G'
54
+ end
55
+
56
+ # Schema definition
57
+ #
58
+ # @return [String]
59
+ def schema_definition
60
+ "\"#{underscored_name}\", #{schema_data_type}\n"
61
+ end
62
+
63
+ # Underscored name
64
+ #
65
+ # This is the column name converted to underscore format.
66
+ # For example, MyColumn will be returned as my_column.
67
+ #
68
+ # @return [String]
69
+ def underscored_name
70
+ @underscored_name ||= begin
71
+ un = name.dup
72
+ un.gsub!(/::/, '/')
73
+ un.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
74
+ un.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
75
+ un.tr!('-', '_')
76
+ un.downcase!
77
+ un
78
+ end
79
+ end
80
+
81
+ def to_hash
82
+ {name: name, type: type, length: length, decimal: decimal}
83
+ end
84
+
85
+ private
86
+
87
+ # TODO this should be a constant
88
+ def type_cast_class # nodoc
89
+ h = Hash.new(ColumnType::String)
90
+ h[:N] = ColumnType::Number
91
+ h[:I] = ColumnType::SignedLong
92
+ h[:F] = ColumnType::Float
93
+ h[:Y] = ColumnType::Currency
94
+ h[:D] = ColumnType::Date
95
+ h[:T] = ColumnType::DateTime
96
+ h[:L] = ColumnType::Boolean
97
+ h[:M] = ColumnType::Memo
98
+ h[:B] = ColumnType::Double
99
+ h[:G] = ColumnType::General
100
+ h
101
+ end
102
+
103
+ def encode(value, strip_output = false) # nodoc
104
+ return value if !value.respond_to?(:encoding)
105
+
106
+ output = @encoding ? encode_string(value) : value
107
+ strip_output ? output.strip : output
108
+ end
109
+
110
+ def encode_string(string)
111
+ string.force_encoding(@encoding).encode(*encoding_args)
112
+ end
113
+
114
+ def encoding_args # nodoc
115
+ @encoding_args ||= [
116
+ Encoding.default_external,
117
+ {undef: :replace, invalid: :replace}
118
+ ]
119
+ end
120
+
121
+ def schema_data_type # nodoc
122
+ case type
123
+ when 'N', 'F'
124
+ decimal > 0 ? ':float' : ':integer'
125
+ when 'I'
126
+ ':integer'
127
+ when 'Y'
128
+ ':decimal, :precision => 15, :scale => 4'
129
+ when 'D'
130
+ ':date'
131
+ when 'T'
132
+ ':datetime'
133
+ when 'L'
134
+ ':boolean'
135
+ when 'M'
136
+ ':text'
137
+ when 'B'
138
+ if DBF::Table::FOXPRO_VERSIONS.keys.include?(@version)
139
+ ':float'
140
+ else
141
+ ':text'
142
+ end
143
+ else
144
+ ":string, :limit => #{length}"
145
+ end
146
+ end
147
+
148
+ def clean(value) # nodoc
149
+ truncated_value = value.strip.partition("\x00").first
150
+ truncated_value.gsub(/[^\x20-\x7E]/, '')
151
+ end
152
+
153
+ def validate_length
154
+ raise LengthError, 'field length must be 0 or greater' if length < 0
155
+ end
156
+
157
+ def validate_name
158
+ raise NameError, 'column name cannot be empty' if @name.empty?
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,85 @@
1
+ module DBF
2
+ module ColumnType
3
+ class Base
4
+ attr_reader :value, :decimal
5
+
6
+ def initialize(value, decimal)
7
+ @value = value
8
+ @decimal = decimal
9
+ end
10
+ end
11
+
12
+ class Number < Base
13
+ def type_cast
14
+ decimal.zero? ? value.to_i : value.to_f
15
+ end
16
+ end
17
+
18
+ class Currency < Base
19
+ def type_cast
20
+ (value.unpack('q<')[0] / 10_000.0).to_f
21
+ end
22
+ end
23
+
24
+ class SignedLong < Base
25
+ def type_cast
26
+ value.unpack('l<')[0]
27
+ end
28
+ end
29
+
30
+ class Float < Base
31
+ def type_cast
32
+ value.to_f
33
+ end
34
+ end
35
+
36
+ class Double < Base
37
+ def type_cast
38
+ value.unpack('E')[0]
39
+ end
40
+ end
41
+
42
+ class Boolean < Base
43
+ def type_cast
44
+ value.strip =~ /^(y|t)$/i ? true : false
45
+ end
46
+ end
47
+
48
+ class Date < Base
49
+ def type_cast
50
+ v = value.tr(' ', '0')
51
+ v !~ /\S/ ? nil : ::Date.parse(v)
52
+ rescue
53
+ nil
54
+ end
55
+ end
56
+
57
+ class DateTime < Base
58
+ def type_cast
59
+ days, msecs = value.unpack('l2')
60
+ secs = (msecs / 1000).to_i
61
+ ::DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60)
62
+ rescue
63
+ nil
64
+ end
65
+ end
66
+
67
+ class Memo < Base
68
+ def type_cast
69
+ value
70
+ end
71
+ end
72
+
73
+ class General < Base
74
+ def type_cast
75
+ value
76
+ end
77
+ end
78
+
79
+ class String < Base
80
+ def type_cast
81
+ value.strip
82
+ end
83
+ end
84
+ end
85
+ end