beezwax 0.5.2 → 0.6.0
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/VERSION +1 -1
- data/beezwax.gemspec +21 -32
- data/lib/beezwax.rb +3 -1
- data/lib/btrieve/btrieve_const.rb +11 -2
- data/lib/btrieve/btrieve_core.rb +15 -26
- data/lib/btrieve/btrieve_record.rb +38 -127
- data/lib/btrieve/btrieve_schema.rb +41 -39
- data/lib/btrieve/btrieve_session.rb +42 -33
- data/lib/btrieve/btrieve_table.rb +92 -82
- data/test/btrieve/test_beezwax.rb +133 -0
- data/test/helper.rb +8 -0
- metadata +8 -22
- data/lib/btrieve/btrieve_model.rb +0 -49
- data/pkg/beezwax-0.0.0.gem +0 -0
- data/pkg/beezwax-0.1.0.gem +0 -0
- data/pkg/beezwax-0.1.1.gem +0 -0
- data/pkg/beezwax-0.1.2.gem +0 -0
- data/pkg/beezwax-0.1.3.gem +0 -0
- data/pkg/beezwax-0.1.4.gem +0 -0
- data/pkg/beezwax-0.2.0.gem +0 -0
- data/pkg/beezwax-0.3.0.gem +0 -0
- data/pkg/beezwax-0.4.0.gem +0 -0
- data/test/test_beezwax.rb +0 -7
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/beezwax.gemspec
CHANGED
@@ -1,57 +1,46 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{beezwax}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Patrick Lardin"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-12-19}
|
13
13
|
s.description = %q{Access Btrieve database files through a ruby API.}
|
14
14
|
s.email = %q{plardin@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
|
18
|
-
|
17
|
+
"README",
|
18
|
+
"README.rdoc"
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
"LICENSE",
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
"pkg/beezwax-0.1.0.gem",
|
37
|
-
"pkg/beezwax-0.1.1.gem",
|
38
|
-
"pkg/beezwax-0.1.2.gem",
|
39
|
-
"pkg/beezwax-0.1.3.gem",
|
40
|
-
"pkg/beezwax-0.1.4.gem",
|
41
|
-
"pkg/beezwax-0.2.0.gem",
|
42
|
-
"pkg/beezwax-0.3.0.gem",
|
43
|
-
"pkg/beezwax-0.4.0.gem",
|
44
|
-
"test/helper.rb",
|
45
|
-
"test/test_beezwax.rb"
|
22
|
+
"README",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"beezwax.gemspec",
|
27
|
+
"lib/beezwax.rb",
|
28
|
+
"lib/btrieve/btrieve_const.rb",
|
29
|
+
"lib/btrieve/btrieve_core.rb",
|
30
|
+
"lib/btrieve/btrieve_record.rb",
|
31
|
+
"lib/btrieve/btrieve_schema.rb",
|
32
|
+
"lib/btrieve/btrieve_session.rb",
|
33
|
+
"lib/btrieve/btrieve_table.rb",
|
34
|
+
"test/btrieve/test_beezwax.rb",
|
35
|
+
"test/helper.rb"
|
46
36
|
]
|
47
37
|
s.homepage = %q{http://github.com/plardin/beezwax}
|
48
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
49
38
|
s.require_paths = ["lib"]
|
50
39
|
s.rubygems_version = %q{1.3.7}
|
51
40
|
s.summary = %q{Btrieve API wrapper.}
|
52
41
|
s.test_files = [
|
53
|
-
"test/test_beezwax.rb",
|
54
|
-
|
42
|
+
"test/btrieve/test_beezwax.rb",
|
43
|
+
"test/helper.rb"
|
55
44
|
]
|
56
45
|
|
57
46
|
if s.respond_to? :specification_version then
|
data/lib/beezwax.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
# Btrieve codes used to talk with the BTR engine.
|
1
2
|
module BtrCodes
|
2
3
|
OK = 0
|
4
|
+
KEY_NOT_FOUND = 4
|
5
|
+
EOF = 9
|
6
|
+
|
3
7
|
OPEN = 0
|
4
8
|
CLOSE = 1
|
5
9
|
INSERT = 2
|
@@ -7,14 +11,18 @@ module BtrCodes
|
|
7
11
|
DELETE = 4
|
8
12
|
GET_EQUAL = 5
|
9
13
|
GET_NEXT = 6
|
14
|
+
GET_PREVIOUS = 7
|
10
15
|
GET_GREATER_THAN_OR_EQUAL = 9
|
11
16
|
GET_LESS_THAN_OR_EQUAL = 11
|
12
|
-
|
17
|
+
GET_FIRST=12
|
13
18
|
BEGIN_TRANSACTION = 19
|
14
19
|
END_TRANSACTION = 20
|
15
20
|
ABORT_TRANSACTION = 21
|
16
21
|
STEP_NEXT = 24
|
17
|
-
|
22
|
+
STOP = 25
|
23
|
+
RESET = 28
|
24
|
+
GET_EQUAL_KEY = 55
|
25
|
+
GET_NEXT_KEY = 56
|
18
26
|
POS_BLOCK_SIZE = 128
|
19
27
|
NULL_KEY = 0x00
|
20
28
|
NULL_BUFFER = ''
|
@@ -34,6 +42,7 @@ module BtrCodes
|
|
34
42
|
NO_CURRENCY_CHANGE = 0xFF
|
35
43
|
MAX_DATATYPE = 200
|
36
44
|
|
45
|
+
# Exception messages potentially returned by the BTR engine
|
37
46
|
EXCEPTION_MESSAGES = {
|
38
47
|
1=>"The operation parameter is invalid.",
|
39
48
|
2=>"The application encountered an I/O error.",
|
data/lib/btrieve/btrieve_core.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'ffi'
|
2
2
|
require_relative 'btrieve_const'
|
3
3
|
|
4
|
+
# Main interface with the BTR engine.
|
4
5
|
module Btrieve
|
5
6
|
extend FFI::Library
|
6
7
|
include BtrCodes
|
7
8
|
|
9
|
+
# Detects the ruby platform to point ot the proper shared lib.
|
8
10
|
if(RUBY_PLATFORM.match('mswin|mingw|cygwin'))
|
9
11
|
ffi_lib 'w3btrv7.dll'
|
10
12
|
elsif(RUBY_PLATFORM.match('linux'))
|
@@ -13,42 +15,29 @@ module Btrieve
|
|
13
15
|
raise Exception.new("Unknown RUBY_PLATFORM #{RUBY_PLATFORM} to perform FFI calls into PSQL.")
|
14
16
|
end
|
15
17
|
|
18
|
+
# FFI declaration for the BTRCALLID method of the BTR engine.
|
16
19
|
attach_function :BTRCALLID, [:int, :pointer, :pointer, :pointer, :pointer, :uchar, :uchar, :pointer], :int
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
# Single entry point into the BTR engine.
|
22
|
+
def btr_op(ops, pos_buffer, data_buffer, key_buffer, key_number, valid_return_codes=[BtrCodes::OK])
|
23
|
+
session=BtrieveSession.get_session
|
24
|
+
data_length = [data_buffer.size].pack('L')
|
25
|
+
key_length = key_buffer.size
|
26
|
+
result_code = BTRCALLID(ops, pos_buffer, data_buffer, data_length, key_buffer, key_length, key_number, session.client_id)
|
27
|
+
unless valid_return_codes.include?(result_code)
|
28
|
+
raise "PSQL Exception Code #{result_code} - #{EXCEPTION_MESSAGES[result_code]}"
|
29
|
+
end
|
26
30
|
result_code
|
27
31
|
end
|
28
32
|
|
33
|
+
# Utility method to create a buffer of a given size.
|
29
34
|
def self.create_string_buffer(size)
|
30
35
|
0.chr*(size)
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
module Timer
|
36
|
-
def timer(&block)
|
37
|
-
start_time = Time.now
|
38
|
-
yield
|
39
|
-
end_time = Time.now
|
40
|
-
end_time - start_time
|
41
|
-
end
|
42
|
-
|
43
|
-
def timer2(out=nil, &block)
|
44
|
-
start_time = Time.now
|
45
|
-
out = yield out
|
46
|
-
[Time.now - start_time, out]
|
47
|
-
end
|
36
|
+
end
|
48
37
|
end
|
49
38
|
|
39
|
+
# Loads the rest of the BTR implementation.
|
50
40
|
require_relative 'btrieve_session'
|
51
41
|
require_relative 'btrieve_table'
|
52
42
|
require_relative 'btrieve_schema'
|
53
43
|
require_relative 'btrieve_record'
|
54
|
-
require_relative 'btrieve_model'
|
@@ -1,174 +1,85 @@
|
|
1
1
|
require 'digest/md5'
|
2
2
|
|
3
|
+
# Represents a single btrieve record for a particular BTR table.
|
3
4
|
class BtrieveRecord
|
4
5
|
include Btrieve
|
5
6
|
attr_reader :data_buffer, :btrieve_table
|
6
7
|
|
7
|
-
|
8
|
+
# Initializes a btrieve record.
|
9
|
+
def initialize(btrieve_table, data_buffer=nil)
|
8
10
|
@btrieve_table = btrieve_table
|
9
11
|
@data_buffer = data_buffer.nil? ? Btrieve.create_string_buffer(@btrieve_table.schema[:record_size]) : data_buffer
|
10
|
-
@session = @btrieve_table.session
|
11
12
|
end
|
12
13
|
|
14
|
+
# Inserts a new btrieve record through the transactional BTR engine.
|
13
15
|
def insert
|
14
|
-
btr_op(@session, INSERT, @btrieve_table.pos_buffer, @data_buffer, NULL_BUFFER, NO_CURRENCY_CHANGE)
|
16
|
+
btr_op(@btrieve_table.session, INSERT, @btrieve_table.pos_buffer, @data_buffer, NULL_BUFFER, NO_CURRENCY_CHANGE)
|
15
17
|
end
|
16
18
|
|
19
|
+
# Updates an existing btrieve record through the transactional BTR engine.
|
17
20
|
def update
|
18
|
-
btr_op(@session, UPDATE, @btrieve_table.pos_buffer, @data_buffer, NULL_BUFFER, NO_CURRENCY_CHANGE)
|
19
|
-
end
|
20
|
-
|
21
|
-
def delete
|
22
|
-
btr_op(@session, DELETE, @btrieve_table.pos_buffer, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
23
|
-
end
|
24
|
-
|
25
|
-
def get_relation_bindings(key)
|
26
|
-
table_map=@session.relations[@btrieve_table.tablename]
|
27
|
-
raise Exception.new("No relations defined for table #{@btrieve_table.tablename}") if table_map.nil?
|
28
|
-
relation=table_map[key]
|
29
|
-
raise Exception.new("No relation found named #{key.to_s} in table #{@btrieve_table.tablename}") if relation.nil?
|
30
|
-
bindings=relation[:join].inject({}) do |map,join|
|
31
|
-
value=join[1][:match]
|
32
|
-
map[join[0]]=join[1][:source]==:table ? self[value] : value
|
33
|
-
map
|
34
|
-
end
|
35
|
-
{:table=>relation[:target],:index=>relation[:index],:bindings=>bindings}
|
36
|
-
end
|
37
|
-
|
38
|
-
def get_relations_bindings(keys)
|
39
|
-
keys.inject({}){|map,key|map[key]=self.get_relation_bindings(key);map}
|
40
|
-
end
|
41
|
-
|
42
|
-
def get_all_relations_bindings()
|
43
|
-
relations=@session.relations[@btrieve_table.tablename]||={}
|
44
|
-
get_relations_bindings(relations.keys)
|
45
|
-
end
|
46
|
-
|
47
|
-
def get_relation(key)
|
48
|
-
match=get_relation_bindings(key)
|
49
|
-
target_table=BtrieveTable.new(relation[:target], @session)
|
50
|
-
index_number=relation[:index]
|
51
|
-
target_table.find(match,index_number)
|
52
|
-
end
|
53
|
-
|
54
|
-
def get_relations(keys)
|
55
|
-
keys.inject({}){|map,key|map[key]=self.get_relation(key);map}
|
56
|
-
end
|
57
|
-
|
58
|
-
def get_all_relations()
|
59
|
-
relations=@session.relations[@btrieve_table.tablename]||={}
|
60
|
-
get_relations(relations.keys)
|
21
|
+
btr_op(@btrieve_table.session, UPDATE, @btrieve_table.pos_buffer, @data_buffer, NULL_BUFFER, NO_CURRENCY_CHANGE)
|
61
22
|
end
|
62
23
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
mapping=table_map[key]
|
67
|
-
raise Exception.new("No mapping found named #{key.to_s} in table #{@btrieve_table.tablename}") if mapping.nil?
|
68
|
-
@session.mapping_functions[mapping[:transform]].call(self[mapping[:column]])
|
69
|
-
end
|
70
|
-
|
71
|
-
def get_mappings(keys)
|
72
|
-
keys.inject({}){|map,key|map[key]=self.get_mapping(key);map}
|
73
|
-
end
|
74
|
-
|
75
|
-
def get_all_mappings()
|
76
|
-
mappings=@session.mappings[@btrieve_table.tablename]||={}
|
77
|
-
get_mappings(mappings.keys)
|
78
|
-
end
|
79
|
-
|
80
|
-
def get_attributes(keys)
|
81
|
-
keys.inject({}){|vals, key| vals[key]=self[key]; vals}
|
24
|
+
# Deletes an existing btrieve record through the transactional BTR engine.
|
25
|
+
def delete
|
26
|
+
btr_op(@btrieve_table.session, DELETE, @btrieve_table.pos_buffer, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
82
27
|
end
|
83
28
|
|
84
|
-
|
85
|
-
|
86
|
-
|
29
|
+
# Returns hash of column-value pairs for this btrieve record, given an array of named columns.
|
30
|
+
# If the array is nil, the hash will contain ALL the column-value pairs defined by the schema.
|
31
|
+
def values(column_names=nil)
|
32
|
+
if(column_names.nil?)
|
33
|
+
column_names=@btrieve_table.schema[:columns].keys.sort.inject([]){|array,column|array<< column;array}
|
34
|
+
end
|
35
|
+
column_names.inject({}){|vals, key| vals[key]=self[key]; vals}
|
87
36
|
end
|
88
37
|
|
38
|
+
# Returns this record's primary key value. Supports composite keys.
|
89
39
|
def primary_key()
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
def foreign_key(prefix)
|
94
|
-
keys=@btrieve_table.primary_key.map{|key|:"#{prefix} #{key.to_s}"}
|
95
|
-
get_composite_key(keys)
|
96
|
-
end
|
97
|
-
|
98
|
-
def get_unique_reference(association_name)
|
99
|
-
dereference(:one_to_one, association_name) do |map, target, index|
|
100
|
-
fk_columns = map.keys.inject([]){|vals, column| vals << column; vals}
|
101
|
-
fk_values = get_attributes(fk_columns)
|
102
|
-
pk_values = map.keys.inject({}){|vals, column| vals[map[column]] = fk_values[column]; vals}
|
103
|
-
target.find_in_unique_index(pk_values, index)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def get_multiple_references(association_name)
|
108
|
-
dereference(:one_to_many, association_name) do |map, target, index|
|
109
|
-
pk_columns = map.keys.inject([]){|vals, column| vals << map[column] unless map[column] == nil; vals}
|
110
|
-
pk_values = get_attributes(pk_columns)
|
111
|
-
fk_values = map.keys.inject({}){|vals, column| vals[column] = pk_values[map[column]]; vals}
|
112
|
-
target.find_in_index(fk_values, index)
|
113
|
-
end
|
40
|
+
values(@btrieve_table.primary_key)
|
114
41
|
end
|
115
|
-
|
42
|
+
|
43
|
+
# Returns this record's value of the column passed in.
|
116
44
|
def [](key_name)
|
117
|
-
|
118
|
-
return nil if(
|
119
|
-
@data_buffer.unpack(
|
45
|
+
column_info = get_column_info(key_name)
|
46
|
+
return nil if(column_info.nil?)
|
47
|
+
@data_buffer.unpack(column_info[:unpacker])[0]
|
120
48
|
end
|
121
49
|
|
50
|
+
# Sets this record's value for a particular column.
|
122
51
|
def []=(key_name, value)
|
123
52
|
schema_key = get_schema_key(key_name)
|
124
53
|
packer_string = schema_key[:unpacker]
|
125
54
|
match = packer_string.match(/@(\d+)([a-zA-Z]+)(\d*)/)
|
126
55
|
offset = match[1].to_i
|
127
56
|
datatype = match[2]
|
128
|
-
|
129
|
-
|
130
|
-
packer = "#{datatype}#{
|
57
|
+
thesizeof = BtrieveSchema.sizeof(datatype)
|
58
|
+
thesizeof *= match[3].empty? ? 1 : match[3].to_i
|
59
|
+
packer = "#{datatype}#{thesizeof}"
|
131
60
|
array = [value]
|
132
61
|
packed_value = array.pack(packer)
|
133
62
|
range = (offset..offset+packed_value.size-1)
|
134
63
|
@data_buffer[range] = packed_value
|
135
64
|
end
|
136
|
-
|
65
|
+
|
66
|
+
def nil?()
|
67
|
+
data_buffer.gsub("\x00", "").size==0
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns a string representation of this record.
|
137
71
|
def to_s()
|
138
|
-
@btrieve_table.schema[:columns].keys.inject('')
|
72
|
+
@btrieve_table.schema[:columns].keys.inject(''){|pretty_print, key| pretty_print = "#{pretty_print} #{key}=>#{self[key]}"; pretty_print}
|
139
73
|
end
|
140
74
|
|
75
|
+
# Returns this record's version as a MD5 Digest value.
|
141
76
|
def version()
|
142
77
|
Digest::MD5.hexdigest(@data_buffer)
|
143
78
|
end
|
144
79
|
|
145
|
-
def method_missing(symbol)
|
146
|
-
return get_unique_reference(symbol) if(@btrieve_table.schema[:one_to_one][symbol])
|
147
|
-
return get_multiple_references(symbol) if(@btrieve_table.schema[:one_to_many][symbol])
|
148
|
-
super(symbol)
|
149
|
-
end
|
150
|
-
|
151
80
|
private
|
152
81
|
|
153
|
-
def
|
154
|
-
|
155
|
-
raise Exception.new("Unknown association '#{association_name}' for table '#{@btrieve_table.tablename}'.") unless association != nil
|
156
|
-
map = association[:map]
|
157
|
-
target = eval(association[:class].to_s)
|
158
|
-
index = association[:index]
|
159
|
-
yield(map, target, index)
|
160
|
-
end
|
161
|
-
|
162
|
-
def get_schema_key(key_name)
|
163
|
-
schema_key = @btrieve_table.schema[:columns][key_name]
|
164
|
-
# raising this exception is hurting the dbr process since I use keys that dont exist
|
165
|
-
# raise Exception.new("Unknown column '#{key_name}' for table '#{@btrieve_table.tablename}'.") unless schema_key != nil
|
166
|
-
schema_key
|
167
|
-
end
|
168
|
-
|
169
|
-
def get_composite_key(keys)
|
170
|
-
pk=keys.inject([]){|arr,col|arr<<self[col];arr}
|
171
|
-
pk[2]=pk[2].strip
|
172
|
-
pk.join('.')
|
82
|
+
def get_column_info(column_name)
|
83
|
+
@btrieve_table.schema[:columns][column_name]
|
173
84
|
end
|
174
85
|
end
|
@@ -1,28 +1,52 @@
|
|
1
|
+
# Represents the btrieve schema (DDF files) of a particular BTR database.
|
1
2
|
class BtrieveSchema
|
2
3
|
include Btrieve
|
3
|
-
attr_reader :
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
attr_reader :xfile_records, :xfield_records
|
5
|
+
|
6
|
+
# Datatypes used to transact with the BTR engine.
|
7
|
+
CHAR = { :name=>'CHAR', :unpacker=>'a', :size=>1 }
|
8
|
+
TINYINT = { :name=>'TINYINT', :unpacker=>'c', :size=>1 }
|
9
|
+
SMALLINT = { :name=>'SMALLINT', :unpacker=>'s', :size=>1 }
|
10
|
+
INTEGER = { :name=>'INTEGER', :unpacker=>'i', :size=>1 }
|
11
|
+
REAL = { :name=>'REAL', :unpacker=>'f', :size=>1 }
|
12
|
+
DOUBLE = { :name=>'DOUBLE', :unpacker=>'d', :size=>1 }
|
13
|
+
DATE = { :name=>'DATE', :unpacker=>'ccs', :size=>4 } #day month year
|
14
|
+
TIME = { :name=>'TIME', :unpacker=>'cccc', :size=>4 } #100th sec min hour
|
15
|
+
BIT = { :name=>'BIT', :unpacker=>'c', :size=>1 }
|
16
|
+
VARCHAR = { :name=>'VARCHAR', :unpacker=>'A', :size=>1 }
|
17
|
+
LONGVARCHAR = { :name=>'LONGVARCHAR', :unpacker=>'A', :size=>1 }
|
18
|
+
UTINYINT = { :name=>'UTINYINT', :unpacker=>'C', :size=>1, :size=>1 }
|
19
|
+
USMALLINT = { :name=>'USMALLINT', :unpacker=>'S', :size=>1 }
|
20
|
+
UINTEGER = { :name=>'UINTEGER', :unpacker=>'I', :size=>1 }
|
21
|
+
UBIGINT = { :name=>'UBIGINT', :unpacker=>'Q', :size=>1 }
|
22
|
+
|
23
|
+
# Array of all datatypes supported by the BTR engine.
|
24
|
+
SIZEOF = {'a'=>1, 'c'=>1, 's'=>1, 'i'=>1, 'f'=>1, 'd'=>1, 'ccs'=>4, 'cccc'=>4, 'A'=>1, 'C'=>1, 'S'=>1, 'I'=>1, 'Q'=>1}
|
25
|
+
|
26
|
+
# Creates a new instance of btrieve schema for a given session.
|
27
|
+
def initialize()
|
28
|
+
session=BtrieveSession.get_session
|
29
|
+
if(!session.cache[:schemas_loaded])
|
8
30
|
cache_system_table(:'x$file')
|
9
31
|
cache_system_table(:'x$field')
|
10
32
|
cache_system_table(:'x$index')
|
11
|
-
|
33
|
+
session.cache[:schemas_loaded] = true
|
12
34
|
end
|
13
35
|
end
|
14
36
|
|
15
|
-
|
37
|
+
# Loads the schema of a particular btrieve table.
|
38
|
+
def get_table_schema(tablename)
|
39
|
+
session=BtrieveSession.get_session
|
16
40
|
schema = nil
|
17
41
|
xfile_index = nil
|
18
|
-
|
42
|
+
session.cache[:'x$file'].each do |xfile|
|
19
43
|
next unless xfile[:'xf$name'].downcase.strip.to_sym == tablename
|
20
44
|
schema = {:filename=>xfile[:'xf$loc'].downcase.strip, :record_size=>0, :columns=>{}}
|
21
45
|
xfile_index = xfile[:'xf$id']
|
22
46
|
break
|
23
47
|
end
|
24
48
|
xfield_index = []
|
25
|
-
|
49
|
+
session.cache[:'x$field'].each do |xfield|
|
26
50
|
next unless xfile_index == xfield[:'xe$file']
|
27
51
|
next unless xfield[:'xe$datatype'] < MAX_DATATYPE
|
28
52
|
id = xfield[:'xe$id']
|
@@ -33,10 +57,9 @@ class BtrieveSchema
|
|
33
57
|
datatype = (name == 'kid - mult') ? 14 : datatype # Absolute BS line of code... Needed for CTA-FOS
|
34
58
|
unpacker = BtrieveSchema.unpacker(offset, datatype, size)
|
35
59
|
schema[:columns][name.to_sym] = {:unpacker=>unpacker, :id=>id, :offset=>offset, :datatype=>datatype, :size=>size}
|
36
|
-
schema[:record_size] += size
|
37
60
|
xfield_index[id] = name.to_sym
|
38
61
|
end
|
39
|
-
|
62
|
+
session.cache[:'x$index'].each do |xindex|
|
40
63
|
next unless xfile_index == xindex[:'xi$file']
|
41
64
|
indices = schema[:indices] ||= []
|
42
65
|
index = indices[xindex[:'xi$number']] ||= []
|
@@ -44,16 +67,12 @@ class BtrieveSchema
|
|
44
67
|
flags = schema[:index_flags] ||= []
|
45
68
|
flags[xindex[:'xi$number']] = flags[xindex[:'xi$number']].to_i | xindex[:'xi$flags'].to_i
|
46
69
|
end
|
70
|
+
last_field=schema[:columns].values.sort{|a,b| a[:offset] <=> b[:offset]}.last
|
71
|
+
schema[:record_size] = last_field[:offset]+last_field[:size]
|
47
72
|
raise Exception.new("Unknown table '#{tablename}'.") if schema.nil?
|
48
|
-
schema[:one_to_one] = {}
|
49
|
-
schema[:one_to_many] = {}
|
50
73
|
schema
|
51
74
|
end
|
52
75
|
|
53
|
-
def self.sizeof(code)
|
54
|
-
SIZEOF[code]
|
55
|
-
end
|
56
|
-
|
57
76
|
private
|
58
77
|
|
59
78
|
SYS_DDF = {
|
@@ -61,25 +80,7 @@ class BtrieveSchema
|
|
61
80
|
:'x$field'=>{:filename=>'field.ddf', :record_size=>32, :columns=>{:'xe$id'=>{:unpacker=>'@0S'}, :'xe$file'=>{:unpacker=>'@2S'}, :'xe$name'=>{:unpacker=>'@4a20'}, :'xe$datatype'=>{:unpacker=>'@24C'}, :'xe$offset'=>{:unpacker=>'@25S'}, :'xe$size'=>{:unpacker=>'@27S'}, :'xe$dec'=>{:unpacker=>'@29C'}, :'xe$flags'=>{:unpacker=>'@30S'}}},
|
62
81
|
:'x$index'=>{:filename=>'index.ddf', :record_size=>32, :columns=>{:'xi$file'=>{:unpacker=>'@0S'},:'xi$field'=>{:unpacker=>'@2S'},:'xi$number'=>{:unpacker=>'@4S'},:'xi$part'=>{:unpacker=>'@6S'},:'xi$flags'=>{:unpacker=>'@8S'}}}
|
63
82
|
}
|
64
|
-
|
65
|
-
CHAR = { :name=>'CHAR', :unpacker=>'a' }
|
66
|
-
TINYINT = { :name=>'TINYINT', :unpacker=>'c' }
|
67
|
-
SMALLINT = { :name=>'SMALLINT', :unpacker=>'s' }
|
68
|
-
INTEGER = { :name=>'INTEGER', :unpacker=>'i' }
|
69
|
-
REAL = { :name=>'REAL', :unpacker=>'f' }
|
70
|
-
DOUBLE = { :name=>'DOUBLE', :unpacker=>'d' }
|
71
|
-
DATE = { :name=>'DATE', :unpacker=>'ccs' } #day month year
|
72
|
-
TIME = { :name=>'TIME', :unpacker=>'cccc' } #100th sec min hour
|
73
|
-
BIT = { :name=>'BIT', :unpacker=>'c' }
|
74
|
-
VARCHAR = { :name=>'VARCHAR', :unpacker=>'A' }
|
75
|
-
LONGVARCHAR = { :name=>'LONGVARCHAR', :unpacker=>'A' }
|
76
|
-
UTINYINT = { :name=>'UTINYINT', :unpacker=>'C' }
|
77
|
-
USMALLINT = { :name=>'USMALLINT', :unpacker=>'S' }
|
78
|
-
UINTEGER = { :name=>'UINTEGER', :unpacker=>'I' }
|
79
|
-
UBIGINT = { :name=>'UBIGINT', :unpacker=>'Q' }
|
80
|
-
|
81
|
-
SIZEOF = {'a'=>1, 'c'=>1, 's'=>1, 'i'=>1, 'f'=>1, 'd'=>1, 'ccs'=>4, 'cccc'=>4, 'A'=>1, 'C'=>1, 'S'=>1, 'I'=>1, 'Q'=>1}
|
82
|
-
|
83
|
+
|
83
84
|
def self.unpacker(offset, datatype, size)
|
84
85
|
offsetter = "@#{offset}"
|
85
86
|
btrtype = lookup(datatype, size)
|
@@ -115,9 +116,10 @@ class BtrieveSchema
|
|
115
116
|
end
|
116
117
|
|
117
118
|
def cache_system_table(tablename)
|
118
|
-
|
119
|
-
|
120
|
-
schema_table
|
119
|
+
session=BtrieveSession.get_session
|
120
|
+
session.cache[tablename] = []
|
121
|
+
schema_table = BtrieveTable.new(SYS_DDF[tablename][:filename], SYS_DDF[tablename])
|
122
|
+
schema_table.each_record(ACCELERATED_MODE) {|value| session.cache[tablename] << value}
|
121
123
|
end
|
122
124
|
end
|
123
125
|
|
@@ -1,48 +1,57 @@
|
|
1
|
+
# Concept of a BTR session. Beware, this implementation is NOT thread safe!
|
1
2
|
class BtrieveSession
|
2
|
-
SESSIONS = {}
|
3
3
|
include Btrieve
|
4
|
-
attr_reader :
|
5
|
-
attr_accessor :
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
attr_reader :client_id, :cache, :btrieve_schema
|
5
|
+
attr_accessor :host, :data_share
|
6
|
+
private_class_method :new
|
7
|
+
@@instance=nil
|
8
|
+
|
9
|
+
# Factory of BTR sessions. A block can be passed into it, or alternatively the factory returns
|
10
|
+
# the session instance to the caller, who then must call 'dispose' on it when done with it.
|
11
|
+
# The 'host' argument can be a named server or its IP address.
|
12
|
+
# The 'data_share' argument is typically the share name of the data folder.
|
13
|
+
# The 'data_share' argument can also be the database name when "IDS" is used on the client.
|
14
|
+
def BtrieveSession.create_session(host, data_share)
|
15
|
+
raise "Cannot create more than one instance of session." if(@@instance != nil)
|
16
|
+
@@instance = new(host, data_share)
|
17
|
+
@@instance.set_schema(BtrieveSchema.new())
|
14
18
|
end
|
15
19
|
|
16
|
-
|
17
|
-
|
20
|
+
# Returns the session instance created.
|
21
|
+
def BtrieveSession.get_session()
|
22
|
+
@@instance
|
23
|
+
end
|
24
|
+
|
25
|
+
# Disposes of the session object, releasing any and all resources consumed/locked on the server.
|
26
|
+
def BtrieveSession.destroy_session()
|
27
|
+
@@instance.btr_op(RESET, NULL_BUFFER, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
28
|
+
@@instance=nil
|
18
29
|
end
|
19
30
|
|
20
|
-
|
21
|
-
|
31
|
+
# Executes a block of BTR directive in a transaction.
|
32
|
+
def BtrieveSession.transaction(&block)
|
22
33
|
begin
|
34
|
+
@@instance.btr_op(BEGIN_TRANSACTION, NULL_BUFFER, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
23
35
|
yield
|
24
36
|
rescue Exception => exception
|
25
|
-
|
26
|
-
|
37
|
+
# Oops! We must roll back the whole shebang!
|
38
|
+
@@instance.btr_op(ABORT_TRANSACTION, NULL_BUFFER, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
39
|
+
raise "PSQL TX ABORTED!!! - #{exception.message}\n#{exception.backtrace.join("\n\t")}"
|
40
|
+
ensure
|
41
|
+
@@instance.btr_op(END_TRANSACTION, NULL_BUFFER, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
27
42
|
end
|
28
|
-
btr_op(self, END_TRANSACTION, NULL_BUFFER, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
29
43
|
end
|
30
|
-
|
31
|
-
def
|
32
|
-
|
33
|
-
[table,table.version]
|
44
|
+
|
45
|
+
def set_schema(schema)
|
46
|
+
@btrieve_schema=schema
|
34
47
|
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def initialize(
|
39
|
-
@
|
40
|
-
@
|
41
|
-
@table_cache = {}
|
42
|
-
@pathname = pathname
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def initialize(host, data_share)
|
52
|
+
@host = host
|
53
|
+
@data_share = data_share
|
43
54
|
@client_id = "#{0.chr*12}#{[0x5257].pack('S')}#{rand(65535)}"
|
44
|
-
|
45
|
-
@btrieve_schema = BtrieveSchema.new(self)
|
55
|
+
@cache = {}
|
46
56
|
end
|
47
57
|
end
|
48
|
-
|
@@ -1,78 +1,52 @@
|
|
1
1
|
require 'digest/md5'
|
2
2
|
|
3
|
+
# Represents a single btrieve table for a particular BTR database.
|
3
4
|
class BtrieveTable
|
4
5
|
include Btrieve
|
5
|
-
attr_reader :tablename, :
|
6
|
+
attr_reader :tablename, :pos_buffer, :schema
|
6
7
|
attr_accessor :primary_key
|
7
8
|
|
8
|
-
|
9
|
+
# Initializes a btrieve table.
|
10
|
+
def initialize(tablename, schema=nil)
|
9
11
|
@tablename = tablename
|
10
|
-
@session = session
|
11
12
|
@pos_buffer = Btrieve.create_string_buffer(POS_BLOCK_SIZE)
|
12
|
-
@schema = schema ? schema :
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def relations()
|
28
|
-
@session.relations[@tablename] ||= {}
|
29
|
-
end
|
30
|
-
|
31
|
-
def add_relation(name, target_table, index, join)
|
32
|
-
relations[name.to_sym]={:target=>target_table.to_sym,:index=>index,:join=>join}
|
13
|
+
@schema = schema ? schema : session.btrieve_schema.get_table_schema(@tablename)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Opens this btrieve table in preparation for some BTR operation(s).
|
17
|
+
# If a block is passed to the call, the table is closed automatically.
|
18
|
+
# If a block is NOT passed to the call, the client code has to make sure to close the file.
|
19
|
+
def open(mode=NORMAL_MODE, &block)
|
20
|
+
file_unc="//#{session.host}/#{session.data_share}/#{@schema[:filename]}"
|
21
|
+
btr_op(OPEN, @pos_buffer, NULL_BUFFER, file_unc, mode)
|
22
|
+
if block_given?
|
23
|
+
yield
|
24
|
+
close()
|
25
|
+
end
|
33
26
|
end
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
28
|
+
# Finds an array of records in this table, using the specified index number and match hash.
|
29
|
+
# If a block is passed in, the entries of this set are passed into this block one by one. Otherwise,
|
30
|
+
# the set is returned to the caller.
|
31
|
+
def find(match, index_number, &block)
|
32
|
+
allows_duplicates=schema[:index_flags].map{|f|f&1==1;}[index_number]
|
33
|
+
set=nil
|
34
|
+
if(allows_duplicates)
|
35
|
+
set=find_in_index(match, index_number)
|
40
36
|
else
|
41
|
-
|
42
|
-
|
43
|
-
result
|
44
|
-
end
|
45
|
-
|
46
|
-
def find_in_unique_index(match, index_number=nil)
|
47
|
-
finder(match, index_number) do |key_buffer, index|
|
48
|
-
btr_record = BtrieveRecord.new(self)
|
49
|
-
batch(){ btr_op(@session, GET_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index) }
|
50
|
-
btr_record
|
37
|
+
rec=find_in_unique_index(match, index_number)
|
38
|
+
set=rec.nil? ? [] : [rec]
|
51
39
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
absolute_match = match.keys.inject({}){|vals, key| vals[key] = match[key] if(match[key] != nil); vals}
|
57
|
-
absolute_match_keys = absolute_match.keys
|
58
|
-
result = []
|
59
|
-
batch() do
|
60
|
-
btr_record = BtrieveRecord.new(self)
|
61
|
-
ops_result = btr_op(@session, GET_GREATER_THAN_OR_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index, [OK, EOF])
|
62
|
-
if OK==ops_result
|
63
|
-
result << btr_record
|
64
|
-
while(true)
|
65
|
-
btr_record = BtrieveRecord.new(self)
|
66
|
-
op_result = btr_op(@session, GET_NEXT, @pos_buffer, btr_record.data_buffer, key_buffer, index_number, [OK, EOF, 21])
|
67
|
-
break unless btr_record.get_attributes(absolute_match_keys)==absolute_match and op_result!=EOF and op_result!=21
|
68
|
-
result << btr_record
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
result
|
40
|
+
if(block_given?)
|
41
|
+
set.each{|record| yield record}
|
42
|
+
else
|
43
|
+
set
|
73
44
|
end
|
74
45
|
end
|
75
46
|
|
47
|
+
# Finds an array of records in this table, using the specified index number, a key and a range of values.
|
48
|
+
# If a block is passed in, the entries of this set are passed into this block one by one. Otherwise,
|
49
|
+
# the set is returned to the caller.
|
76
50
|
def find_in_range(index_num, key, range, &block)
|
77
51
|
index = @schema[:indices][index_num]
|
78
52
|
columns = @schema[:columns]
|
@@ -84,23 +58,32 @@ class BtrieveTable
|
|
84
58
|
packer
|
85
59
|
end
|
86
60
|
key_buffer = index_values.pack(key_packer)
|
87
|
-
|
88
61
|
btr_record = BtrieveRecord.new(self)
|
62
|
+
records = []
|
89
63
|
batch do
|
90
|
-
btr_op(
|
91
|
-
while(range.include?(btr_record[key]))
|
92
|
-
|
64
|
+
btr_op(GET_GREATER_THAN_OR_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index_num, [OK, EOF])
|
65
|
+
while(range.include?(btr_record[key]))
|
66
|
+
if(!block.nil?)
|
67
|
+
yield btr_record
|
68
|
+
else
|
69
|
+
records << btr_record
|
70
|
+
end
|
93
71
|
btr_record = BtrieveRecord.new(self)
|
94
|
-
btr_op(
|
72
|
+
btr_op(GET_NEXT, @pos_buffer, btr_record.data_buffer, key_buffer, index_num, [OK, EOF])
|
95
73
|
end
|
96
74
|
end
|
75
|
+
if(block.nil?)
|
76
|
+
records
|
77
|
+
end
|
97
78
|
end
|
98
79
|
|
80
|
+
# Closes this btrieve table to conclude a sequence of BTR operation(s).
|
99
81
|
def close
|
100
|
-
btr_op(
|
82
|
+
btr_op(CLOSE, @pos_buffer, NULL_BUFFER, NULL_BUFFER, NULL_KEY)
|
101
83
|
end
|
102
84
|
|
103
|
-
|
85
|
+
# Loops over the records of this BTR table and passes each record to the block passed in.
|
86
|
+
def each_record(mode=NORMAL_MODE, &block)
|
104
87
|
batch(mode) do
|
105
88
|
while(record = step_next) do
|
106
89
|
yield record
|
@@ -108,34 +91,24 @@ class BtrieveTable
|
|
108
91
|
end
|
109
92
|
end
|
110
93
|
|
111
|
-
|
94
|
+
# Wraps a sequence of BTR operations between a pair of open and close statements for this table.
|
95
|
+
# DEPRECATED - Use the "open" method instead and pass a block to it.
|
96
|
+
def batch(mode=NORMAL_MODE, &block)
|
112
97
|
open(mode)
|
113
98
|
yield
|
114
99
|
close
|
115
100
|
end
|
116
101
|
|
102
|
+
# Returns this table's version as a MD5 Digest value.
|
117
103
|
def version()
|
118
104
|
Digest::MD5.hexdigest(BtrieveSchema.sort(schema[:columns]).inject([]){|array,column|array << BtrieveSchema.readable_type(column);array}.to_s)
|
119
105
|
end
|
120
|
-
|
121
|
-
#def self.primary_key_match(pk)
|
122
|
-
# vals=pk.split('.')
|
123
|
-
# {PK_KEYS[0]=>vals[0].to_i,PK_KEYS[1]=>vals[1].to_i,PK_KEYS[2]=>vals[2],PK_KEYS[3]=>vals[3].to_i,PK_KEYS[4]=>vals[4].to_i}
|
124
|
-
#end
|
125
|
-
|
126
|
-
#def self.tablename_to_url(tablename)
|
127
|
-
# tablename.gsub(' & ','.and.').gsub('a/c ', 'ac.').gsub('es - o','es.o').gsub(' ','.')
|
128
|
-
#end
|
129
|
-
|
130
|
-
#def self.url_to_tablename(url_tablename)
|
131
|
-
# url_tablename.gsub('.and.', ' & ').gsub('ac.', 'a/c ').gsub('es.o','es - o').gsub('.',' ')
|
132
|
-
#end
|
133
106
|
|
134
107
|
private
|
135
108
|
|
136
109
|
def step_next
|
137
110
|
btr_record = BtrieveRecord.new(self)
|
138
|
-
result = btr_op(
|
111
|
+
result = btr_op(STEP_NEXT, @pos_buffer, btr_record.data_buffer, NULL_BUFFER, NULL_KEY, [OK, EOF])
|
139
112
|
return false if(result == EOF)
|
140
113
|
btr_record
|
141
114
|
end
|
@@ -158,6 +131,7 @@ class BtrieveTable
|
|
158
131
|
value = match[key]
|
159
132
|
value = value.to_f if FLOAT_TYPES.include?(datatype)
|
160
133
|
value = value.to_i if INTEGER_TYPES.include?(datatype)
|
134
|
+
value = value.ljust(columns[key][:size], ' ') if datatype=='CHAR'
|
161
135
|
vals << value
|
162
136
|
vals
|
163
137
|
end
|
@@ -173,5 +147,41 @@ class BtrieveTable
|
|
173
147
|
key_buffer = key_components.pack(key_packer)
|
174
148
|
yield(key_buffer, index_number)
|
175
149
|
end
|
150
|
+
|
151
|
+
def find_in_unique_index(match, index_number=nil)
|
152
|
+
finder(match, index_number) do |key_buffer, index|
|
153
|
+
btr_record = BtrieveRecord.new(self)
|
154
|
+
batch(){ btr_op(GET_EQUAL, @pos_buffer, btr_record.data_buffer, key_buffer, index, [KEY_NOT_FOUND, OK, EOF]) }
|
155
|
+
btr_record.nil? ? nil : btr_record
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def find_in_index(match, index_number=nil)
|
160
|
+
finder(match, index_number) do |org_key_buffer, index|
|
161
|
+
absolute_match = match.keys.inject({}){|vals, key| vals[key] = match[key] if(match[key] != nil); vals}
|
162
|
+
absolute_match_keys = absolute_match.keys
|
163
|
+
result = []
|
164
|
+
batch do
|
165
|
+
key_buffer="#{org_key_buffer}"
|
166
|
+
ops_result = btr_op(GET_EQUAL_KEY, @pos_buffer, NULL_BUFFER, key_buffer, index, [OK, EOF, KEY_NOT_FOUND])
|
167
|
+
ops=GET_EQUAL
|
168
|
+
while(ops_result == OK)
|
169
|
+
btr_record = BtrieveRecord.new(self)
|
170
|
+
ops_result = btr_op(ops, @pos_buffer, btr_record.data_buffer, key_buffer, index, [OK, EOF])
|
171
|
+
break if(key_buffer!=org_key_buffer)
|
172
|
+
ops=GET_NEXT
|
173
|
+
result << btr_record if(ops_result == OK)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
result
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def session
|
181
|
+
s=BtrieveSession.get_session
|
182
|
+
raise "Cannot manipulate a BtrieveTable when no BtrieveSession exists." if s.nil?
|
183
|
+
s
|
184
|
+
end
|
185
|
+
|
176
186
|
end
|
177
187
|
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestBeezwax < Test::Unit::TestCase
|
4
|
+
context "when a BtrieveSession is initialized" do
|
5
|
+
setup do
|
6
|
+
BtrieveSession.create_session(DB_SERVER,"Demodata")
|
7
|
+
end
|
8
|
+
|
9
|
+
teardown do
|
10
|
+
BtrieveSession.destroy_session
|
11
|
+
end
|
12
|
+
|
13
|
+
should "not allow the creation of another session" do
|
14
|
+
assert_raise(RuntimeError){BtrieveSession.create_session(DB_SERVER,"Demodata")}
|
15
|
+
end
|
16
|
+
|
17
|
+
should "return a handle to the session singleton" do
|
18
|
+
session=BtrieveSession.get_session
|
19
|
+
assert_not_nil session
|
20
|
+
end
|
21
|
+
|
22
|
+
should "permit the instanciation of a DB table" do
|
23
|
+
table=BtrieveTable.new(:course)
|
24
|
+
assert_not_nil table
|
25
|
+
end
|
26
|
+
|
27
|
+
should "allow an empty transaction to go through" do
|
28
|
+
BtrieveSession.transaction{}
|
29
|
+
end
|
30
|
+
|
31
|
+
context "and an instance of BtrieveTable is created, it" do
|
32
|
+
setup do
|
33
|
+
@table=BtrieveTable.new(:course)
|
34
|
+
end
|
35
|
+
|
36
|
+
teardown do
|
37
|
+
@table=nil
|
38
|
+
end
|
39
|
+
|
40
|
+
should "be possible to open it for operations, then close it" do
|
41
|
+
assert_nothing_raised(RuntimeError){@table.open}
|
42
|
+
assert_nothing_raised(RuntimeError){@table.close}
|
43
|
+
end
|
44
|
+
|
45
|
+
should "be possible to have the file automatically closed when a block is passed when opening a table" do
|
46
|
+
@table.open{}
|
47
|
+
assert_raise(RuntimeError){@table.close}
|
48
|
+
end
|
49
|
+
|
50
|
+
should "return a version number for that table" do
|
51
|
+
table_version=@table.version
|
52
|
+
assert_not_nil table_version
|
53
|
+
assert_equal 32, table_version.size
|
54
|
+
end
|
55
|
+
|
56
|
+
should "allow looping over each of its records to count them" do
|
57
|
+
counter = 0
|
58
|
+
assert_nothing_raised{@table.each_record{|record|counter+=1}}
|
59
|
+
assert_equal 145, counter
|
60
|
+
end
|
61
|
+
|
62
|
+
should "be possible to search the table for a given index" do
|
63
|
+
assert_nothing_raised {@table.find({:name=>'blah'}, 0)}
|
64
|
+
end
|
65
|
+
|
66
|
+
should "be possible to find a particular record in a unique index" do
|
67
|
+
name = 'PHI 101'
|
68
|
+
record=@table.find({:name=>name}, 0).first
|
69
|
+
assert_not_nil record
|
70
|
+
assert_equal record[:name], name
|
71
|
+
end
|
72
|
+
|
73
|
+
should "be possible to fetch a particular record and all its columns should be properly set" do
|
74
|
+
name = 'PHI 101'
|
75
|
+
record=@table.find({:name=>name}, 0).first
|
76
|
+
expected_values={:name=>name, :description=>'Introduction to Ethics', :credit_hours=>3, :dept_name=>'Philosophy'}
|
77
|
+
assert_equal expected_values, record.values
|
78
|
+
end
|
79
|
+
|
80
|
+
should "be possible to search for a unique key that doesn't exist and not cause an exception" do
|
81
|
+
name = 'FRA 333'
|
82
|
+
records=@table.find({:name=>name}, 0)
|
83
|
+
assert_not_nil records
|
84
|
+
assert records.empty?, "records is NOT empty"
|
85
|
+
end
|
86
|
+
|
87
|
+
should "be possible to find several records in an index that supports duplicates" do
|
88
|
+
dept_name = 'Mathematics'
|
89
|
+
records=@table.find({:dept_name=>dept_name}, 1)
|
90
|
+
assert_not_nil records
|
91
|
+
assert !records.empty?, "records is empty"
|
92
|
+
assert_equal 9, records.size
|
93
|
+
assert records.inject(true){|test, record| test |= record[:dept_name]==dept_name; test}
|
94
|
+
end
|
95
|
+
|
96
|
+
should "support finding records and looping over them if a block is passed in" do
|
97
|
+
dept_name = 'Mathematics'
|
98
|
+
counter = 0
|
99
|
+
@table.find({:dept_name=>dept_name}, 1) do
|
100
|
+
counter += 1
|
101
|
+
end
|
102
|
+
assert_equal 9, counter
|
103
|
+
end
|
104
|
+
|
105
|
+
should "be possible to search for a key that doesn't exit and not cause an exception" do
|
106
|
+
dept_name = 'Francais'
|
107
|
+
records=@table.find({:dept_name=>dept_name}, 1)
|
108
|
+
assert_not_nil records
|
109
|
+
assert records.empty?, "records is NOT empty"
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "A destroyed BtrieveSession" do
|
117
|
+
setup do
|
118
|
+
BtrieveSession.create_session(DB_SERVER,"Demodata")
|
119
|
+
@table = BtrieveTable.new(:course)
|
120
|
+
BtrieveSession.destroy_session
|
121
|
+
end
|
122
|
+
|
123
|
+
should "not allow the instanciation of a DB table" do
|
124
|
+
assert_raise(RuntimeError){BtrieveTable.new(:course)}
|
125
|
+
end
|
126
|
+
|
127
|
+
should "not allow exsting tables to search against the DB" do
|
128
|
+
records=nil
|
129
|
+
assert_raise(RuntimeError){records=@table.find({:dept_name=>'Mathematics'}, 1)}
|
130
|
+
assert_nil records
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/test/helper.rb
CHANGED
@@ -6,5 +6,13 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
6
6
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
7
|
require 'beezwax'
|
8
8
|
|
9
|
+
|
9
10
|
class Test::Unit::TestCase
|
11
|
+
#DB_SERVER=nil
|
12
|
+
DB_SERVER="10.211.55.4"
|
13
|
+
context "The database server name DB_SERVER" do
|
14
|
+
should "be defined to match your environment" do
|
15
|
+
assert_not_nil DB_SERVER
|
16
|
+
end
|
17
|
+
end
|
10
18
|
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beezwax
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 15
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
7
|
+
- 6
|
8
|
+
- 0
|
9
|
+
version: 0.6.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Patrick Lardin
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-
|
17
|
+
date: 2010-12-19 00:00:00 -08:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -26,7 +25,6 @@ dependencies:
|
|
26
25
|
requirements:
|
27
26
|
- - ">="
|
28
27
|
- !ruby/object:Gem::Version
|
29
|
-
hash: 1
|
30
28
|
segments:
|
31
29
|
- 0
|
32
30
|
- 6
|
@@ -54,29 +52,19 @@ files:
|
|
54
52
|
- lib/beezwax.rb
|
55
53
|
- lib/btrieve/btrieve_const.rb
|
56
54
|
- lib/btrieve/btrieve_core.rb
|
57
|
-
- lib/btrieve/btrieve_model.rb
|
58
55
|
- lib/btrieve/btrieve_record.rb
|
59
56
|
- lib/btrieve/btrieve_schema.rb
|
60
57
|
- lib/btrieve/btrieve_session.rb
|
61
58
|
- lib/btrieve/btrieve_table.rb
|
62
|
-
-
|
63
|
-
- pkg/beezwax-0.1.0.gem
|
64
|
-
- pkg/beezwax-0.1.1.gem
|
65
|
-
- pkg/beezwax-0.1.2.gem
|
66
|
-
- pkg/beezwax-0.1.3.gem
|
67
|
-
- pkg/beezwax-0.1.4.gem
|
68
|
-
- pkg/beezwax-0.2.0.gem
|
69
|
-
- pkg/beezwax-0.3.0.gem
|
70
|
-
- pkg/beezwax-0.4.0.gem
|
59
|
+
- test/btrieve/test_beezwax.rb
|
71
60
|
- test/helper.rb
|
72
|
-
- test/test_beezwax.rb
|
73
61
|
has_rdoc: true
|
74
62
|
homepage: http://github.com/plardin/beezwax
|
75
63
|
licenses: []
|
76
64
|
|
77
65
|
post_install_message:
|
78
|
-
rdoc_options:
|
79
|
-
|
66
|
+
rdoc_options: []
|
67
|
+
|
80
68
|
require_paths:
|
81
69
|
- lib
|
82
70
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -84,7 +72,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
72
|
requirements:
|
85
73
|
- - ">="
|
86
74
|
- !ruby/object:Gem::Version
|
87
|
-
hash: 3
|
88
75
|
segments:
|
89
76
|
- 0
|
90
77
|
version: "0"
|
@@ -93,7 +80,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
80
|
requirements:
|
94
81
|
- - ">="
|
95
82
|
- !ruby/object:Gem::Version
|
96
|
-
hash: 3
|
97
83
|
segments:
|
98
84
|
- 0
|
99
85
|
version: "0"
|
@@ -105,5 +91,5 @@ signing_key:
|
|
105
91
|
specification_version: 3
|
106
92
|
summary: Btrieve API wrapper.
|
107
93
|
test_files:
|
108
|
-
- test/test_beezwax.rb
|
94
|
+
- test/btrieve/test_beezwax.rb
|
109
95
|
- test/helper.rb
|
@@ -1,49 +0,0 @@
|
|
1
|
-
class BtrieveModel
|
2
|
-
include Btrieve
|
3
|
-
def initialize(classname, tablename)
|
4
|
-
session = BtrieveSession.get_session()
|
5
|
-
table = BtrieveTable.new(tablename, session)
|
6
|
-
session.model_classes[classname] = table
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.new_record()
|
10
|
-
BtrieveRecord.new(get_table())
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.find_in_unique_index(match, index_number)
|
14
|
-
get_table().find_in_unique_index(match, index_number)
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.find_in_index(match, index_number)
|
18
|
-
get_table().find_in_index(match, index_number)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.each_record(mode=ACCELERATED_MODE, &block)
|
22
|
-
get_table().each_record(mode, &block)
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.batch(mode=ACCELERATED_MODE, &block)
|
26
|
-
get_table().batch(mode, &block)
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.one_to_one(association)
|
30
|
-
get_table().schema[:one_to_one][association[:name]]=association
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.one_to_many(association)
|
34
|
-
get_table().schema[:one_to_many][association[:name]]=association
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def self.get_table()
|
40
|
-
session = BtrieveSession.get_session()
|
41
|
-
session.model_classes[self.name.to_sym]
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def BtrieveModel(classname, tablename)
|
46
|
-
raise Exception.new("The context is not set. Call 'Btrieve::Session.set_context( <pathname> )' before declaring model classes.") unless BtrieveSession.get_session() != nil
|
47
|
-
BtrieveModel.new(classname, tablename)
|
48
|
-
BtrieveModel
|
49
|
-
end
|
data/pkg/beezwax-0.0.0.gem
DELETED
Binary file
|
data/pkg/beezwax-0.1.0.gem
DELETED
Binary file
|
data/pkg/beezwax-0.1.1.gem
DELETED
Binary file
|
data/pkg/beezwax-0.1.2.gem
DELETED
Binary file
|
data/pkg/beezwax-0.1.3.gem
DELETED
Binary file
|
data/pkg/beezwax-0.1.4.gem
DELETED
Binary file
|
data/pkg/beezwax-0.2.0.gem
DELETED
Binary file
|
data/pkg/beezwax-0.3.0.gem
DELETED
Binary file
|
data/pkg/beezwax-0.4.0.gem
DELETED
Binary file
|