rbase 0.1.2 → 0.1.3
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/Rakefile +1 -3
- data/lib/rbase/columns.rb +57 -6
- data/lib/rbase/record.rb +20 -4
- data/lib/rbase/table.rb +20 -5
- metadata +48 -40
data/Rakefile
CHANGED
@@ -7,11 +7,10 @@ desc 'Create a gem'
|
|
7
7
|
task :gem do
|
8
8
|
spec = Gem::Specification.new do |s|
|
9
9
|
s.name = 'rbase'
|
10
|
-
s.version = '0.1.
|
10
|
+
s.version = '0.1.3'
|
11
11
|
s.summary = 'Library to create/read/write to XBase databases (*.DBF files)'
|
12
12
|
s.files = Dir.glob('**/*').delete_if { |item| item.include?('.svn') }
|
13
13
|
s.require_path = 'lib'
|
14
|
-
s.autorequire = 'rbase'
|
15
14
|
s.authors = 'Maxim Kulkin, Leonardo Augusto Pires'
|
16
15
|
s.email = 'maxim.kulkin@gmail.com, leonardo.pires@gmail.com'
|
17
16
|
s.homepage = 'http://rbase.rubyforge.com/'
|
@@ -21,6 +20,5 @@ task :gem do
|
|
21
20
|
s.required_ruby_version = '>= 1.8.2'
|
22
21
|
end
|
23
22
|
|
24
|
-
Gem.manage_gems
|
25
23
|
Gem::Builder.new(spec).build
|
26
24
|
end
|
data/lib/rbase/columns.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module RBase
|
2
2
|
module Columns
|
3
|
+
|
4
|
+
require 'iconv'
|
3
5
|
|
4
6
|
# Base class for all column types
|
5
7
|
class Column
|
@@ -57,6 +59,10 @@ module RBase
|
|
57
59
|
def unpack(value)
|
58
60
|
throw "Not implemented"
|
59
61
|
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"#{name}(type=#{type}, size=#{size})"
|
65
|
+
end
|
60
66
|
|
61
67
|
protected
|
62
68
|
|
@@ -77,14 +83,27 @@ module RBase
|
|
77
83
|
end
|
78
84
|
|
79
85
|
super name, options.merge(:size => size)
|
86
|
+
|
87
|
+
if options[:encoding]
|
88
|
+
@unpack_converter = Iconv.new('utf-8', options[:encoding])
|
89
|
+
@pack_converter = Iconv.new(options[:encoding], 'utf-8')
|
90
|
+
end
|
80
91
|
end
|
81
92
|
|
82
93
|
def pack(value)
|
94
|
+
value = value.to_s
|
95
|
+
value = @pack_converter.iconv(value) if @pack_converter
|
83
96
|
[value].pack("A#{size}")
|
84
97
|
end
|
85
98
|
|
86
99
|
def unpack(data)
|
87
|
-
data.rstrip
|
100
|
+
value = data.rstrip
|
101
|
+
value = @unpack_converter.iconv(value) if @unpack_converter
|
102
|
+
value
|
103
|
+
end
|
104
|
+
|
105
|
+
def inspect
|
106
|
+
"#{name}(string #{size})"
|
88
107
|
end
|
89
108
|
end
|
90
109
|
|
@@ -100,17 +119,33 @@ module RBase
|
|
100
119
|
end
|
101
120
|
|
102
121
|
def pack(value)
|
103
|
-
if
|
104
|
-
|
122
|
+
if value
|
123
|
+
if float?
|
124
|
+
[format("%#{size-decimal-1}.#{decimal}f", value)].pack("A#{size}")
|
125
|
+
else
|
126
|
+
[format("%#{size}d", value)].pack("A#{size}")
|
127
|
+
end
|
105
128
|
else
|
106
|
-
|
129
|
+
" "*size
|
107
130
|
end
|
108
131
|
end
|
109
132
|
|
110
133
|
def unpack(data)
|
111
|
-
return nil if data.
|
134
|
+
return nil if data.strip == ''
|
112
135
|
data.rstrip.to_i
|
113
136
|
end
|
137
|
+
|
138
|
+
def inspect
|
139
|
+
if float?
|
140
|
+
"#{name}(decimal)"
|
141
|
+
else
|
142
|
+
"#{name}(integer)"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def float?
|
147
|
+
decimal && decimal != 0
|
148
|
+
end
|
114
149
|
end
|
115
150
|
|
116
151
|
|
@@ -139,6 +174,10 @@ module RBase
|
|
139
174
|
nil
|
140
175
|
end
|
141
176
|
end
|
177
|
+
|
178
|
+
def inspect
|
179
|
+
"#{name}(boolean)"
|
180
|
+
end
|
142
181
|
end
|
143
182
|
|
144
183
|
|
@@ -157,6 +196,10 @@ module RBase
|
|
157
196
|
return nil if data.rstrip == ''
|
158
197
|
Date.new(*data.unpack("a4a2a2").map { |s| s.to_i})
|
159
198
|
end
|
199
|
+
|
200
|
+
def inspect
|
201
|
+
"#{name}(date)"
|
202
|
+
end
|
160
203
|
end
|
161
204
|
|
162
205
|
|
@@ -175,6 +218,10 @@ module RBase
|
|
175
218
|
def unpack(data)
|
176
219
|
table.memo.read(data.to_i)
|
177
220
|
end
|
221
|
+
|
222
|
+
def inspect
|
223
|
+
"#{name}(memo)"
|
224
|
+
end
|
178
225
|
end
|
179
226
|
|
180
227
|
|
@@ -190,12 +237,16 @@ module RBase
|
|
190
237
|
end
|
191
238
|
|
192
239
|
def pack(value)
|
193
|
-
[format("%-#{size-decimal-1}.#{decimal}f", value)].pack("A#{size}")
|
240
|
+
[format("%-#{size-decimal-1}.#{decimal}f", value || 0.0)].pack("A#{size}")
|
194
241
|
end
|
195
242
|
|
196
243
|
def unpack(data)
|
197
244
|
data.rstrip.to_f
|
198
245
|
end
|
246
|
+
|
247
|
+
def inspect
|
248
|
+
"#{name}(float)"
|
249
|
+
end
|
199
250
|
end
|
200
251
|
|
201
252
|
end
|
data/lib/rbase/record.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module RBase
|
2
|
-
class Error < Exception; end
|
3
2
|
|
4
|
-
class
|
3
|
+
class StandardError < Exception; end
|
4
|
+
|
5
|
+
class UnknownColumnError < StandardError
|
5
6
|
attr_reader :name
|
6
7
|
|
7
8
|
def initialize(name)
|
@@ -10,6 +11,15 @@ module RBase
|
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
14
|
+
class InvalidValueError < StandardError
|
15
|
+
attr_reader :column, :value
|
16
|
+
|
17
|
+
def initialize(column, value)
|
18
|
+
super("Invalid value #{value.inspect} for column #{column.inspect}")
|
19
|
+
@column, @value = column, value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
13
23
|
# Class that contains data for particular table row.
|
14
24
|
# Should not be created explicitly (use Table#create to create records)
|
15
25
|
#
|
@@ -68,7 +78,7 @@ module RBase
|
|
68
78
|
@deleted ||= new_record? ? false : @data[0, 1] == '*'
|
69
79
|
end
|
70
80
|
|
71
|
-
#
|
81
|
+
# Clone record.
|
72
82
|
def clone
|
73
83
|
c = self.class.new(@table, @values_changed)
|
74
84
|
c.instance_variable_set("@values_cached", @values_cached)
|
@@ -95,7 +105,12 @@ module RBase
|
|
95
105
|
@data[0, 1] = deleted? ? '*' : ' '
|
96
106
|
@values_changed.each do |k, v|
|
97
107
|
column = @table.column(k)
|
98
|
-
|
108
|
+
raise UnknownColumnError.new(k) unless column
|
109
|
+
begin
|
110
|
+
@data[column.offset, column.size] = column.pack(v)
|
111
|
+
rescue Object => e
|
112
|
+
raise InvalidValueError.new(column, v)
|
113
|
+
end
|
99
114
|
@values_cached[k] = v
|
100
115
|
end
|
101
116
|
end
|
@@ -117,6 +132,7 @@ module RBase
|
|
117
132
|
# Sets value of specified column.
|
118
133
|
def set_value(name, value)
|
119
134
|
name = name.to_s.upcase.to_sym
|
135
|
+
raise UnknownColumnError.new(name) unless @table.column(name)
|
120
136
|
@values_changed[name] = value
|
121
137
|
end
|
122
138
|
end
|
data/lib/rbase/table.rb
CHANGED
@@ -3,6 +3,8 @@ module RBase
|
|
3
3
|
class Table
|
4
4
|
private_class_method :new
|
5
5
|
|
6
|
+
include Enumerable
|
7
|
+
|
6
8
|
# Create new XBase table file. Table file name will be equal to name with ".dbf" suffix.
|
7
9
|
#
|
8
10
|
# Allowed options
|
@@ -58,9 +60,9 @@ module RBase
|
|
58
60
|
|
59
61
|
# Open table with given name.
|
60
62
|
# Table name should be like file name without ".dbf" suffix.
|
61
|
-
def self.open(name)
|
63
|
+
def self.open(name, options = {})
|
62
64
|
table = new
|
63
|
-
table.instance_eval { open("#{name}.dbf") }
|
65
|
+
table.instance_eval { open("#{name}.dbf", options) }
|
64
66
|
if block_given?
|
65
67
|
result = yield table
|
66
68
|
table.close
|
@@ -94,6 +96,16 @@ module RBase
|
|
94
96
|
update_header
|
95
97
|
end
|
96
98
|
|
99
|
+
def clear
|
100
|
+
file_end = @record_offset
|
101
|
+
@file.pos = file_end
|
102
|
+
@file.write "\x1a"
|
103
|
+
@file.truncate file_end+1
|
104
|
+
|
105
|
+
self.count = 0
|
106
|
+
update_header
|
107
|
+
end
|
108
|
+
|
97
109
|
def close
|
98
110
|
@file.close
|
99
111
|
end
|
@@ -158,7 +170,7 @@ module RBase
|
|
158
170
|
|
159
171
|
private
|
160
172
|
|
161
|
-
def open(name)
|
173
|
+
def open(name, options = {})
|
162
174
|
@name = File.basename(name, '.dbf')
|
163
175
|
@file = File.open(name, "r+b")
|
164
176
|
header = @file.read(32)
|
@@ -177,12 +189,14 @@ module RBase
|
|
177
189
|
|
178
190
|
@columns = []
|
179
191
|
@name_to_columns = {}
|
192
|
+
column_options = {}
|
193
|
+
column_options[:encoding] = options[:encoding] if options[:encoding]
|
180
194
|
while true do
|
181
195
|
column_data = @file.read(32)
|
182
196
|
break if column_data[0, 1] == "\x0d"
|
183
197
|
name, type, offset, size, decimal = *column_data.unpack('@0a11aLCC')
|
184
198
|
name = name.strip
|
185
|
-
@columns << Columns::Column.column_for(type).new(name, :offset => offset, :size => size, :decimal => decimal)
|
199
|
+
@columns << Columns::Column.column_for(type).new(name, options.merge(:offset => offset, :size => size, :decimal => decimal))
|
186
200
|
@name_to_columns[name.upcase.to_sym] = @columns.last
|
187
201
|
end
|
188
202
|
|
@@ -196,7 +210,8 @@ module RBase
|
|
196
210
|
@file.pos = @record_offset + @record_size*count
|
197
211
|
@file.write record.serialize
|
198
212
|
@file.write [26].pack('c')
|
199
|
-
|
213
|
+
record.instance_variable_set(:@index, count)
|
214
|
+
self.count += 1
|
200
215
|
else
|
201
216
|
throw "Index out of bound" if record.index>=count
|
202
217
|
@file.pos = @record_offset + @record_size*record.index
|
metadata
CHANGED
@@ -1,56 +1,64 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.2
|
3
|
-
specification_version: 1
|
4
2
|
name: rbase
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2007-05-30 00:00:00 +04:00
|
8
|
-
summary: Library to create/read/write to XBase databases (*.DBF files)
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: maxim.kulkin@gmail.com, leonardo.pires@gmail.com
|
12
|
-
homepage: http://rbase.rubyforge.com/
|
13
|
-
rubyforge_project: rbase
|
14
|
-
description:
|
15
|
-
autorequire: rbase
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 1.8.2
|
24
|
-
version:
|
4
|
+
version: 0.1.3
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Maxim Kulkin, Leonardo Augusto Pires
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-28 00:00:00 +04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: maxim.kulkin@gmail.com, leonardo.pires@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
31
24
|
files:
|
32
|
-
- lib
|
33
|
-
- Rakefile
|
34
|
-
- lib/rbase
|
35
|
-
- lib/rbase.rb
|
36
|
-
- lib/rbase/record.rb
|
37
|
-
- lib/rbase/columns.rb
|
38
|
-
- lib/rbase/schema.rb
|
39
25
|
- lib/rbase/builder.rb
|
40
|
-
- lib/rbase/
|
26
|
+
- lib/rbase/columns.rb
|
41
27
|
- lib/rbase/memo_file.rb
|
28
|
+
- lib/rbase/record.rb
|
29
|
+
- lib/rbase/schema.rb
|
42
30
|
- lib/rbase/schema_dumper.rb
|
43
|
-
|
31
|
+
- lib/rbase/table.rb
|
32
|
+
- lib/rbase.rb
|
33
|
+
- Rakefile
|
34
|
+
- rbase-0.1.3.gem
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://rbase.rubyforge.com/
|
37
|
+
licenses: []
|
44
38
|
|
39
|
+
post_install_message:
|
45
40
|
rdoc_options: []
|
46
41
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 1.8.2
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
53
56
|
requirements: []
|
54
57
|
|
55
|
-
|
58
|
+
rubyforge_project: rbase
|
59
|
+
rubygems_version: 1.3.2
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: Library to create/read/write to XBase databases (*.DBF files)
|
63
|
+
test_files: []
|
56
64
|
|