dbf 3.0.0 → 3.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
  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