infused-dbf 1.0.7
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.
- data/History.txt +55 -0
- data/Manifest.txt +31 -0
- data/README.txt +115 -0
- data/Rakefile +37 -0
- data/bin/dbf +40 -0
- data/dbf.gemspec +37 -0
- data/lib/dbf/column.rb +85 -0
- data/lib/dbf/globals.rb +27 -0
- data/lib/dbf/record.rb +99 -0
- data/lib/dbf/table.rb +243 -0
- data/lib/dbf.rb +7 -0
- data/spec/fixtures/dbase_03.dbf +0 -0
- data/spec/fixtures/dbase_30.dbf +0 -0
- data/spec/fixtures/dbase_30.fpt +0 -0
- data/spec/fixtures/dbase_83.dbf +0 -0
- data/spec/fixtures/dbase_83.dbt +0 -0
- data/spec/fixtures/dbase_83_schema.txt +19 -0
- data/spec/fixtures/dbase_8b.dbf +0 -0
- data/spec/fixtures/dbase_8b.dbt +0 -0
- data/spec/fixtures/dbase_f5.dbf +0 -0
- data/spec/fixtures/dbase_f5.fpt +0 -0
- data/spec/functional/dbf_shared.rb +45 -0
- data/spec/functional/format_03_spec.rb +23 -0
- data/spec/functional/format_30_spec.rb +23 -0
- data/spec/functional/format_83_spec.rb +23 -0
- data/spec/functional/format_8b_spec.rb +23 -0
- data/spec/functional/format_f5_spec.rb +23 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/column_spec.rb +124 -0
- data/spec/unit/record_spec.rb +99 -0
- data/spec/unit/table_spec.rb +162 -0
- metadata +103 -0
data/lib/dbf/table.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
module DBF
|
2
|
+
|
3
|
+
class Table
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :column_count # The total number of columns (columns)
|
7
|
+
attr_reader :columns # An array of DBF::Column
|
8
|
+
attr_reader :version # Internal dBase version number
|
9
|
+
attr_reader :last_updated # Last updated datetime
|
10
|
+
attr_reader :memo_file_format # :fpt or :dpt
|
11
|
+
attr_reader :memo_block_size # The block size for memo records
|
12
|
+
attr_reader :options # The options hash that was used to initialize the table
|
13
|
+
attr_reader :data # DBF file handle
|
14
|
+
attr_reader :memo # Memo file handle
|
15
|
+
|
16
|
+
# Initializes a new DBF::Table
|
17
|
+
# Example:
|
18
|
+
# table = DBF::Table.new 'data.dbf'
|
19
|
+
def initialize(filename, options = {})
|
20
|
+
@data = File.open(filename, 'rb')
|
21
|
+
@memo = open_memo(filename)
|
22
|
+
@options = options
|
23
|
+
reload!
|
24
|
+
end
|
25
|
+
|
26
|
+
# Reloads the database and memo files
|
27
|
+
def reload!
|
28
|
+
@records = nil
|
29
|
+
get_header_info
|
30
|
+
get_memo_header_info if @memo
|
31
|
+
get_column_descriptors
|
32
|
+
build_db_index
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns true if there is a corresponding memo file
|
36
|
+
def has_memo_file?
|
37
|
+
@memo ? true : false
|
38
|
+
end
|
39
|
+
|
40
|
+
# The total number of active records.
|
41
|
+
def record_count
|
42
|
+
@db_index.size
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns an instance of DBF::Column for <b>column_name</b>. The <b>column_name</b>
|
46
|
+
# can be a specified as either a symbol or string.
|
47
|
+
def column(column_name)
|
48
|
+
@columns.detect {|f| f.name == column_name.to_s}
|
49
|
+
end
|
50
|
+
|
51
|
+
# An array of all the records contained in the database file. Each record is an instance
|
52
|
+
# of DBF::Record (or nil if the record is marked for deletion).
|
53
|
+
def records
|
54
|
+
self.to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :rows, :records
|
58
|
+
|
59
|
+
def each
|
60
|
+
0.upto(@record_count - 1) do |n|
|
61
|
+
seek_to_record(n)
|
62
|
+
unless deleted_record?
|
63
|
+
yield DBF::Record.new(self)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# def get_record_from_file(index)
|
69
|
+
# seek_to_record(@db_index[index])
|
70
|
+
# Record.new(self)
|
71
|
+
# end
|
72
|
+
|
73
|
+
# Returns a DBF::Record (or nil if the record has been marked for deletion) for the record at <tt>index</tt>.
|
74
|
+
def record(index)
|
75
|
+
records[index]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Find records using a simple ActiveRecord-like syntax.
|
79
|
+
#
|
80
|
+
# Examples:
|
81
|
+
# table = DBF::Table.new 'mydata.dbf'
|
82
|
+
#
|
83
|
+
# # Find record number 5
|
84
|
+
# table.find(5)
|
85
|
+
#
|
86
|
+
# # Find all records for Keith Morrison
|
87
|
+
# table.find :all, :first_name => "Keith", :last_name => "Morrison"
|
88
|
+
#
|
89
|
+
# # Find first record
|
90
|
+
# table.find :first, :first_name => "Keith"
|
91
|
+
#
|
92
|
+
# The <b>command</b> can be an id, :all, or :first.
|
93
|
+
# <b>options</b> is optional and, if specified, should be a hash where the keys correspond
|
94
|
+
# to column names in the database. The values will be matched exactly with the value
|
95
|
+
# in the database. If you specify more than one key, all values must match in order
|
96
|
+
# for the record to be returned. The equivalent SQL would be "WHERE key1 = 'value1'
|
97
|
+
# AND key2 = 'value2'".
|
98
|
+
def find(command, options = {})
|
99
|
+
results = options.empty? ? records : records.select {|record| all_values_match?(record, options)}
|
100
|
+
|
101
|
+
case command
|
102
|
+
when Fixnum
|
103
|
+
record(command)
|
104
|
+
when :all
|
105
|
+
results
|
106
|
+
when :first
|
107
|
+
results.first
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
alias_method :row, :record
|
112
|
+
|
113
|
+
# Returns a description of the current database file.
|
114
|
+
def version_description
|
115
|
+
VERSION_DESCRIPTIONS[version]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a database schema in the portable ActiveRecord::Schema format.
|
119
|
+
#
|
120
|
+
# xBase data types are converted to generic types as follows:
|
121
|
+
# - Number columns are converted to :integer if there are no decimals, otherwise
|
122
|
+
# they are converted to :float
|
123
|
+
# - Date columns are converted to :datetime
|
124
|
+
# - Logical columns are converted to :boolean
|
125
|
+
# - Memo columns are converted to :text
|
126
|
+
# - Character columns are converted to :string and the :limit option is set
|
127
|
+
# to the length of the character column
|
128
|
+
#
|
129
|
+
# Example:
|
130
|
+
# create_table "mydata" do |t|
|
131
|
+
# t.column :name, :string, :limit => 30
|
132
|
+
# t.column :last_update, :datetime
|
133
|
+
# t.column :is_active, :boolean
|
134
|
+
# t.column :age, :integer
|
135
|
+
# t.column :notes, :text
|
136
|
+
# end
|
137
|
+
def schema(path = nil)
|
138
|
+
s = "ActiveRecord::Schema.define do\n"
|
139
|
+
s << " create_table \"#{File.basename(@data.path, ".*")}\" do |t|\n"
|
140
|
+
columns.each do |column|
|
141
|
+
s << " t.column #{column.schema_definition}"
|
142
|
+
end
|
143
|
+
s << " end\nend"
|
144
|
+
|
145
|
+
if path
|
146
|
+
File.open(path, 'w') {|f| f.puts(s)}
|
147
|
+
else
|
148
|
+
s
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the record at <tt>index</tt> by seeking to the record in the
|
153
|
+
# physical database file. See the documentation for the records method for
|
154
|
+
# information on how these two methods differ.
|
155
|
+
def get_record_from_file(index)
|
156
|
+
seek_to_record(@db_index[index])
|
157
|
+
Record.new(self)
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def open_memo(file)
|
163
|
+
%w(fpt FPT dbt DBT).each do |extname|
|
164
|
+
filename = replace_extname(file, extname)
|
165
|
+
if File.exists?(filename)
|
166
|
+
@memo_file_format = extname.downcase.to_sym
|
167
|
+
return File.open(filename, 'rb')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
|
173
|
+
def replace_extname(filename, extension)
|
174
|
+
filename.sub(/#{File.extname(filename)[1..-1]}$/, extension)
|
175
|
+
end
|
176
|
+
|
177
|
+
def deleted_record?
|
178
|
+
if @data.read(1).unpack('a') == ['*']
|
179
|
+
@data.rewind
|
180
|
+
true
|
181
|
+
else
|
182
|
+
false
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_header_info
|
187
|
+
@data.rewind
|
188
|
+
@version, @record_count, @header_length, @record_length = @data.read(DBF_HEADER_SIZE).unpack('H2 x3 V v2')
|
189
|
+
@column_count = (@header_length - DBF_HEADER_SIZE + 1) / DBF_HEADER_SIZE
|
190
|
+
end
|
191
|
+
|
192
|
+
def get_column_descriptors
|
193
|
+
@columns = []
|
194
|
+
@column_count.times do
|
195
|
+
name, type, length, decimal = @data.read(32).unpack('a10 x a x4 C2')
|
196
|
+
if length > 0
|
197
|
+
@columns << Column.new(name.strip, type, length, decimal)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
# Reset the column count in case any were skipped
|
201
|
+
@column_count = @columns.size
|
202
|
+
|
203
|
+
@columns
|
204
|
+
end
|
205
|
+
|
206
|
+
def get_memo_header_info
|
207
|
+
@memo.rewind
|
208
|
+
if @memo_file_format == :fpt
|
209
|
+
@memo_next_available_block, @memo_block_size = @memo.read(FPT_HEADER_SIZE).unpack('N x2 n')
|
210
|
+
@memo_block_size = 0 if @memo_block_size.nil?
|
211
|
+
else
|
212
|
+
@memo_block_size = 512
|
213
|
+
@memo_next_available_block = File.size(@memo.path) / @memo_block_size
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def seek(offset)
|
218
|
+
@data.seek(@header_length + offset)
|
219
|
+
end
|
220
|
+
|
221
|
+
def seek_to_record(index)
|
222
|
+
seek(index * @record_length)
|
223
|
+
end
|
224
|
+
|
225
|
+
def build_db_index
|
226
|
+
@db_index = []
|
227
|
+
@deleted_records = []
|
228
|
+
0.upto(@record_count - 1) do |n|
|
229
|
+
seek_to_record(n)
|
230
|
+
if deleted_record?
|
231
|
+
@deleted_records << n
|
232
|
+
else
|
233
|
+
@db_index << n
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def all_values_match?(record, options)
|
239
|
+
options.map {|key, value| record.attributes[key.to_s.underscore] == value}.all?
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
data/lib/dbf.rb
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
create_table "dbase_83" do |t|
|
3
|
+
t.column "id", :integer
|
4
|
+
t.column "catcount", :integer
|
5
|
+
t.column "agrpcount", :integer
|
6
|
+
t.column "pgrpcount", :integer
|
7
|
+
t.column "order", :integer
|
8
|
+
t.column "code", :string, :limit => 50
|
9
|
+
t.column "name", :string, :limit => 100
|
10
|
+
t.column "thumbnail", :string, :limit => 254
|
11
|
+
t.column "image", :string, :limit => 254
|
12
|
+
t.column "price", :float
|
13
|
+
t.column "cost", :float
|
14
|
+
t.column "desc", :text
|
15
|
+
t.column "weight", :float
|
16
|
+
t.column "taxable", :boolean
|
17
|
+
t.column "active", :boolean
|
18
|
+
end
|
19
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,45 @@
|
|
1
|
+
describe DBF, :shared => true do
|
2
|
+
specify "sum of column lengths should equal record length specified in header" do
|
3
|
+
header_record_length = @table.instance_eval {@record_length}
|
4
|
+
sum_of_column_lengths = @table.columns.inject(1) {|sum, column| sum + column.length}
|
5
|
+
|
6
|
+
header_record_length.should == sum_of_column_lengths
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "records should be instances of DBF::Record" do
|
10
|
+
@table.records.all? {|record| record.should be_an_instance_of(DBF::Record)}
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "columns should be instances of DBF::Column" do
|
14
|
+
@table.columns.all? {|column| column.should be_an_instance_of(DBF::Column)}
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "column names should not be blank" do
|
18
|
+
@table.columns.all? {|column| column.name.should_not be_empty}
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "column types should be valid" do
|
22
|
+
valid_column_types = %w(C N L D M F B G P Y T I V X @ O +)
|
23
|
+
@table.columns.all? {|column| valid_column_types.should include(column.type)}
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "column lengths should be instances of Fixnum" do
|
27
|
+
@table.columns.all? {|column| column.length.should be_an_instance_of(Fixnum)}
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "column lengths should be larger than 0" do
|
31
|
+
@table.columns.all? {|column| column.length.should > 0}
|
32
|
+
end
|
33
|
+
|
34
|
+
specify "column decimals should be instances of Fixnum" do
|
35
|
+
@table.columns.all? {|column| column.decimal.should be_an_instance_of(Fixnum)}
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "column read accessors should return the attribute after typecast" do
|
39
|
+
@table.columns do |column|
|
40
|
+
record = table.records.first
|
41
|
+
record.send(column.name).should == record[column.name]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require File.dirname(__FILE__) + "/dbf_shared"
|
3
|
+
|
4
|
+
describe DBF, "of type 03 (dBase III without memo file)" do
|
5
|
+
before(:each) do
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_03.dbf"
|
7
|
+
end
|
8
|
+
|
9
|
+
it_should_behave_like "DBF"
|
10
|
+
|
11
|
+
it "should report the correct version number" do
|
12
|
+
@table.version.should == "03"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a memo file" do
|
16
|
+
@table.should_not have_memo_file
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should report the correct memo type" do
|
20
|
+
@table.memo_file_format.should be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require File.dirname(__FILE__) + "/dbf_shared"
|
3
|
+
|
4
|
+
describe DBF, "of type 30 (Visual FoxPro)" do
|
5
|
+
before(:each) do
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_30.dbf"
|
7
|
+
end
|
8
|
+
|
9
|
+
it_should_behave_like "DBF"
|
10
|
+
|
11
|
+
it "should report the correct version number" do
|
12
|
+
@table.version.should == "30"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a memo file" do
|
16
|
+
@table.should have_memo_file
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should report the correct memo type" do
|
20
|
+
@table.memo_file_format.should == :fpt
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require File.dirname(__FILE__) + "/dbf_shared"
|
3
|
+
|
4
|
+
describe DBF, "of type 83 (dBase III with memo file)" do
|
5
|
+
before(:each) do
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_83.dbf"
|
7
|
+
end
|
8
|
+
|
9
|
+
it_should_behave_like "DBF"
|
10
|
+
|
11
|
+
it "should report the correct version number" do
|
12
|
+
@table.version.should == "83"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a memo file" do
|
16
|
+
@table.should have_memo_file
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should report the correct memo type" do
|
20
|
+
@table.memo_file_format.should == :dbt
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require File.dirname(__FILE__) + "/dbf_shared"
|
3
|
+
|
4
|
+
describe DBF, "of type 8b (dBase IV with memo file)" do
|
5
|
+
before(:each) do
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_8b.dbf"
|
7
|
+
end
|
8
|
+
|
9
|
+
it_should_behave_like "DBF"
|
10
|
+
|
11
|
+
it "should report the correct version number" do
|
12
|
+
@table.version.should == "8b"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a memo file" do
|
16
|
+
@table.should have_memo_file
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should report the correct memo type" do
|
20
|
+
@table.memo_file_format.should == :dbt
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require File.dirname(__FILE__) + "/dbf_shared"
|
3
|
+
|
4
|
+
describe DBF, "of type f5 (FoxPro with memo file)" do
|
5
|
+
before(:each) do
|
6
|
+
@table = DBF::Table.new "#{DB_PATH}/dbase_f5.dbf"
|
7
|
+
end
|
8
|
+
|
9
|
+
it_should_behave_like "DBF"
|
10
|
+
|
11
|
+
it "should report the correct version number" do
|
12
|
+
@table.version.should == "f5"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a memo file" do
|
16
|
+
@table.should have_memo_file
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should report the correct memo type" do
|
20
|
+
@table.memo_file_format.should == :fpt
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DBF::Column do
|
4
|
+
|
5
|
+
context "when initialized" do
|
6
|
+
before do
|
7
|
+
@column = DBF::Column.new "ColumnName", "N", 1, 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should set the #name accessor" do
|
11
|
+
@column.name.should == "ColumnName"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should set the #type accessor" do
|
15
|
+
@column.type.should == "N"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should set the #length accessor" do
|
19
|
+
@column.length.should == 1
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should set the #decimal accessor" do
|
23
|
+
@column.decimal.should == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise an error if length is greater than 0" do
|
27
|
+
lambda { column = DBF::Column.new "ColumnName", "N", -1, 0 }.should raise_error(DBF::ColumnLengthError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "#type_cast" do
|
32
|
+
it "should cast numbers with decimals to Float" do
|
33
|
+
value = "13.5"
|
34
|
+
column = DBF::Column.new "ColumnName", "N", 2, 1
|
35
|
+
column.type_cast(value).should == 13.5
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should cast numbers with no decimals to Integer" do
|
39
|
+
value = "135"
|
40
|
+
column = DBF::Column.new "ColumnName", "N", 3, 0
|
41
|
+
column.type_cast(value).should == 13105
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should cast :integer to Integer" do
|
45
|
+
value = "135"
|
46
|
+
column = DBF::Column.new "ColumnName", "I", 3, 0
|
47
|
+
column.type_cast(value).should == 13105
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should cast boolean to True" do
|
51
|
+
value = "y"
|
52
|
+
column = DBF::Column.new "ColumnName", "L", 1, 0
|
53
|
+
column.type_cast(value).should == true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should cast boolean to False" do
|
57
|
+
value = "n"
|
58
|
+
column = DBF::Column.new "ColumnName", "L", 1, 0
|
59
|
+
column.type_cast(value).should == false
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should cast :datetime columns to DateTime" do
|
63
|
+
value = "Nl%\000\300Z\252\003"
|
64
|
+
column = DBF::Column.new "ColumnName", "T", 16, 0
|
65
|
+
column.type_cast(value).should == "2002-10-10T17:04:56+00:00"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should cast :date columns to Date" do
|
69
|
+
value = "20050712"
|
70
|
+
column = DBF::Column.new "ColumnName", "D", 8, 0
|
71
|
+
column.type_cast(value).should == Date.new(2005,7,12)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "#schema_definition" do
|
76
|
+
it "should define an integer column if type is (N)umber with 9 decimals" do
|
77
|
+
column = DBF::Column.new "ColumnName", "N", 1, 0
|
78
|
+
column.schema_definition.should == "\"column_name\", :integer\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should define a float colmn if type is (N)umber with more than 0 decimals" do
|
82
|
+
column = DBF::Column.new "ColumnName", "N", 1, 2
|
83
|
+
column.schema_definition.should == "\"column_name\", :float\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should define a date column if type is (D)ate" do
|
87
|
+
column = DBF::Column.new "ColumnName", "D", 8, 0
|
88
|
+
column.schema_definition.should == "\"column_name\", :date\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should define a datetime column if type is (D)ate" do
|
92
|
+
column = DBF::Column.new "ColumnName", "T", 16, 0
|
93
|
+
column.schema_definition.should == "\"column_name\", :datetime\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should define a boolean column if type is (L)ogical" do
|
97
|
+
column = DBF::Column.new "ColumnName", "L", 1, 0
|
98
|
+
column.schema_definition.should == "\"column_name\", :boolean\n"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should define a text column if type is (M)emo" do
|
102
|
+
column = DBF::Column.new "ColumnName", "M", 1, 0
|
103
|
+
column.schema_definition.should == "\"column_name\", :text\n"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should define a string column with length for any other data types" do
|
107
|
+
column = DBF::Column.new "ColumnName", "X", 20, 0
|
108
|
+
column.schema_definition.should == "\"column_name\", :string, :limit => 20\n"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "#strip_non_ascii_chars" do
|
113
|
+
it "should strip characters below decimal 32 and above decimal 127" do
|
114
|
+
column = DBF::Column.new "ColumnName", "N", 1, 0
|
115
|
+
column.send(:strip_non_ascii_chars, "--\x1F-\x68\x65\x6C\x6C\x6F world-\x80--").should == "---hello world---"
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should truncate characters with decimal 0" do
|
119
|
+
column = DBF::Column.new "ColumnName", "N", 1, 0
|
120
|
+
column.send(:strip_non_ascii_chars, "--\x1F-\x68\x65\x6C\x6C\x6F \x00 world-\x80--").should == "---hello "
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|