beezwax 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/beezwax.gemspec +5 -2
- data/lib/btrieve/btrieve_const.rb +2 -0
- data/lib/btrieve/btrieve_core.rb +1 -0
- data/lib/btrieve/btrieve_extended.rb +70 -0
- data/lib/btrieve/btrieve_record.rb +7 -19
- data/lib/btrieve/btrieve_schema.rb +7 -7
- data/lib/btrieve/btrieve_table.rb +65 -2
- data/test/btrieve/test_extended_beezwax.rb +41 -0
- metadata +7 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/beezwax.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{beezwax}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.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-
|
12
|
+
s.date = %q{2010-12-22}
|
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 = [
|
@@ -27,11 +27,13 @@ Gem::Specification.new do |s|
|
|
27
27
|
"lib/beezwax.rb",
|
28
28
|
"lib/btrieve/btrieve_const.rb",
|
29
29
|
"lib/btrieve/btrieve_core.rb",
|
30
|
+
"lib/btrieve/btrieve_extended.rb",
|
30
31
|
"lib/btrieve/btrieve_record.rb",
|
31
32
|
"lib/btrieve/btrieve_schema.rb",
|
32
33
|
"lib/btrieve/btrieve_session.rb",
|
33
34
|
"lib/btrieve/btrieve_table.rb",
|
34
35
|
"test/btrieve/test_beezwax.rb",
|
36
|
+
"test/btrieve/test_extended_beezwax.rb",
|
35
37
|
"test/helper.rb"
|
36
38
|
]
|
37
39
|
s.homepage = %q{http://github.com/plardin/beezwax}
|
@@ -40,6 +42,7 @@ Gem::Specification.new do |s|
|
|
40
42
|
s.summary = %q{Btrieve API wrapper.}
|
41
43
|
s.test_files = [
|
42
44
|
"test/btrieve/test_beezwax.rb",
|
45
|
+
"test/btrieve/test_extended_beezwax.rb",
|
43
46
|
"test/helper.rb"
|
44
47
|
]
|
45
48
|
|
data/lib/btrieve/btrieve_core.rb
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
class BtrieveSearchBuffer
|
2
|
+
include Btrieve
|
3
|
+
COMPARISON = {:equal=>1,:greater_than=>2,:less_than=>3, :not_equal=>4, :greater_than_or_equal=>5, :less_than_or_equal=>6}
|
4
|
+
BOOLEAN = {:and=>1, :or=>2}
|
5
|
+
|
6
|
+
def initialize(table, filter, fields_to_return, records_to_fetch=10, start_point=:next_record, max_reject_count=0)
|
7
|
+
@table=table
|
8
|
+
@filter=filter
|
9
|
+
@records_to_fetch=records_to_fetch
|
10
|
+
@fields_to_return=fields_to_return
|
11
|
+
@start_point = start_point == :current_record ? 'UC' : 'EG'
|
12
|
+
@max_reject_count=max_reject_count
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_encoded_buffer
|
16
|
+
filter_buff = []
|
17
|
+
@filter.each_with_index() do |token, i|
|
18
|
+
is_expr = (i%2 == 0)
|
19
|
+
if(is_expr)
|
20
|
+
column = @table.schema[:columns][token[0]]
|
21
|
+
comparison = COMPARISON[token[1]]
|
22
|
+
right_operand = nil
|
23
|
+
if(is_symbol?(token[2]))
|
24
|
+
# We're comparing against a value stored in another field in this record
|
25
|
+
comparison += 64
|
26
|
+
right_operand=[@table.schema[:columns][token[2]][:offset]].pack('S')
|
27
|
+
else
|
28
|
+
# We're comparing against a constant in the exact same format as the column itself
|
29
|
+
right_operand=@table.pack_value(column, token[2])
|
30
|
+
right_operand=right_operand.gsub("\x00",' ') if [0,11,12,21].include?(column[:datatype])
|
31
|
+
end
|
32
|
+
#filter_buff << "DATATYPE=#{column[:datatype]} SIZE=#{column[:size]} OFFSET=#{column[:offset]} COMPARISON=#{comparison} OPERAND=#{right_operand}"
|
33
|
+
filter_item = [column[:datatype], column[:size], column[:offset], comparison, 0].pack('CSSCC')
|
34
|
+
filter_item << right_operand
|
35
|
+
#p filter_item
|
36
|
+
filter_buff << filter_item
|
37
|
+
else
|
38
|
+
boolean = BOOLEAN[token]
|
39
|
+
filter_buff.last[6..6]=[boolean].pack('C')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
num_logical_expressions = filter_buff.size
|
43
|
+
#p "num_logical_expressions = #{num_logical_expressions}"
|
44
|
+
#p filter_buff
|
45
|
+
result_descriptor=[@records_to_fetch, @fields_to_return.size].pack('SS')
|
46
|
+
#p "result_descriptor before = #{result_descriptor}"
|
47
|
+
@fields_to_return.inject(result_descriptor) do |desc, field|
|
48
|
+
column = @table.schema[:columns][field]
|
49
|
+
#p "#{field} => column[:size], column[:offset] = #{column[:size]}, #{column[:offset]}"
|
50
|
+
desc << [column[:size], column[:offset]].pack('SS')
|
51
|
+
desc
|
52
|
+
end
|
53
|
+
#p "result_descriptor after = #{result_descriptor}"
|
54
|
+
buffer_size=8+result_descriptor.size+filter_buff.join('').size
|
55
|
+
#p "buffer_size=#{buffer_size}"
|
56
|
+
search_buffer=[buffer_size, @start_point, @max_reject_count, num_logical_expressions].pack('Sa2SS')
|
57
|
+
#p "search_buffer.size=#{search_buffer.size}"
|
58
|
+
search_buffer << filter_buff.join('') << result_descriptor
|
59
|
+
#p "search_buffer.size=#{search_buffer.size}"
|
60
|
+
#p "buffer_size - search_buffer.size=#{buffer_size - search_buffer.size}"
|
61
|
+
search_buffer
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def is_symbol?(arg)
|
67
|
+
arg.object_id == arg.to_sym.object_id rescue false
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -57,27 +57,20 @@ class BtrieveRecord
|
|
57
57
|
|
58
58
|
# Returns this record's value of the column passed in.
|
59
59
|
def [](key_name)
|
60
|
-
|
61
|
-
return nil if(
|
62
|
-
val = @data_buffer.unpack(
|
60
|
+
column = @btrieve_table.schema[:columns][key_name]
|
61
|
+
return nil if(column.nil?)
|
62
|
+
val = @data_buffer.unpack(column[:unpacker])[0]
|
63
63
|
val = val.strip if val.respond_to?('strip')
|
64
64
|
val
|
65
65
|
end
|
66
66
|
|
67
67
|
# Sets this record's value for a particular column.
|
68
68
|
def []=(key_name, value)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
offset = match[1].to_i
|
73
|
-
datatype = match[2]
|
74
|
-
thesizeof = BtrieveSchema::SIZEOF[datatype]
|
75
|
-
thesizeof *= match[3].empty? ? 1 : match[3].to_i
|
76
|
-
packer = "#{datatype}#{thesizeof}"
|
77
|
-
array = [value]
|
78
|
-
packed_value = array.pack(packer)
|
69
|
+
column = @btrieve_table.schema[:columns][key_name]
|
70
|
+
packed_value = @btrieve_table.pack_value(column, value)
|
71
|
+
offset = column[:offset]
|
79
72
|
range = (offset..offset+packed_value.size-1)
|
80
|
-
data_buffer[range] = packed_value
|
73
|
+
@data_buffer[range] = packed_value
|
81
74
|
end
|
82
75
|
|
83
76
|
# Determines if a record is nil.
|
@@ -107,9 +100,4 @@ class BtrieveRecord
|
|
107
100
|
@position = Btrieve.create_string_buffer(RECORD_POSITION_SIZE)
|
108
101
|
end
|
109
102
|
|
110
|
-
private
|
111
|
-
|
112
|
-
def get_column_info(column_name)
|
113
|
-
@btrieve_table.schema[:columns][column_name]
|
114
|
-
end
|
115
103
|
end
|
@@ -72,7 +72,13 @@ class BtrieveSchema
|
|
72
72
|
raise Exception.new("Unknown table '#{tablename}'.") if schema.nil?
|
73
73
|
schema
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
|
+
def self.readable_type(column)
|
77
|
+
c=column.to_a[0]
|
78
|
+
btrtype = lookup(c[1][:datatype],c[1][:size])
|
79
|
+
[:name=>c[0], :datatype=>btrtype[:name], :size=>c[1][:size]]
|
80
|
+
end
|
81
|
+
|
76
82
|
private
|
77
83
|
|
78
84
|
SYS_DDF = {
|
@@ -88,12 +94,6 @@ class BtrieveSchema
|
|
88
94
|
"#{offsetter}#{btrtype[:unpacker]}#{multiplier}"
|
89
95
|
end
|
90
96
|
|
91
|
-
def self.readable_type(column)
|
92
|
-
c=column.to_a[0]
|
93
|
-
btrtype = lookup(c[1][:datatype],c[1][:size])
|
94
|
-
[:name=>c[0], :datatype=>btrtype[:name], :size=>c[1][:size]]
|
95
|
-
end
|
96
|
-
|
97
97
|
def self.sort(schema)
|
98
98
|
schema.inject([]){|array,column|array[column[1][:offset]]={column[0]=>column[1]};array}.compact
|
99
99
|
end
|
@@ -47,8 +47,8 @@ class BtrieveTable
|
|
47
47
|
# Finds an array of records in this table, using the specified index number, a key and a range of values.
|
48
48
|
# If a block is passed in, the entries of this set are passed into this block one by one. Otherwise,
|
49
49
|
# the set is returned to the caller.
|
50
|
-
# WARNING - NO UNIT TEST EXIST!
|
51
|
-
#
|
50
|
+
# WARNING - NO UNIT TEST EXIST YET!
|
51
|
+
# CONSIDER - Deprecation coming soon... Should use the 'find_extended' method instead.
|
52
52
|
def find_in_range(index_num, key, range, &block)
|
53
53
|
index = @schema[:indices][index_num]
|
54
54
|
columns = @schema[:columns]
|
@@ -107,6 +107,69 @@ class BtrieveTable
|
|
107
107
|
Digest::MD5.hexdigest(BtrieveSchema.sort(schema[:columns]).inject([]){|array,column|array << BtrieveSchema.readable_type(column);array}.to_s)
|
108
108
|
end
|
109
109
|
|
110
|
+
# Packs a value according to the metadata of a particular column.
|
111
|
+
def pack_value(column, value)
|
112
|
+
packer_string = column[:unpacker]
|
113
|
+
match = packer_string.match(/@(\d+)([a-zA-Z]+)(\d*)/)
|
114
|
+
datatype = match[2]
|
115
|
+
size = match[3].empty? ? BtrieveSchema::SIZEOF[datatype] : BtrieveSchema::SIZEOF[datatype]*match[3].to_i
|
116
|
+
packer = "#{datatype}#{size}"
|
117
|
+
array = [value]
|
118
|
+
array.pack(packer)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns an array of hashes that match the criteria defined by the filter parameter.
|
122
|
+
# An example of the filter parameter is given below -
|
123
|
+
#
|
124
|
+
# filter = [[:dept_name, :equal, 'Philosophy'], :and, [:name, :not_equal, 'PHI 101'], :and, [:name, :not_equal, :dept_name]]
|
125
|
+
#
|
126
|
+
# The following COMPARISON operators are supported in the logical comparison clauses -
|
127
|
+
# :equal
|
128
|
+
# :greater_than
|
129
|
+
# :less_than
|
130
|
+
# :not_equal
|
131
|
+
# :greater_than_or_equal
|
132
|
+
# :less_than_or_equal
|
133
|
+
#
|
134
|
+
# Logical comparison clauses can chained together using the following boolean operators -
|
135
|
+
# :and
|
136
|
+
# :or
|
137
|
+
#
|
138
|
+
# NOTE - The logical comparisons can compare a columns to a constant OR to another column in the same record.
|
139
|
+
def find_extended(index, filter, fields_to_return, records_to_fetch)
|
140
|
+
record_size = @schema[:record_size]
|
141
|
+
records_buffer = record_size*records_to_fetch
|
142
|
+
max_reject_count = 65535
|
143
|
+
start_point = :next_record
|
144
|
+
searcher = BtrieveSearchBuffer.new(self, filter, fields_to_return, records_to_fetch, max_reject_count, max_reject_count)
|
145
|
+
search_buffer = searcher.get_encoded_buffer
|
146
|
+
buff_diff = records_buffer - search_buffer.size
|
147
|
+
search_buffer << Btrieve.create_string_buffer(buff_diff) if(buff_diff > 0)
|
148
|
+
tmp_buff = Btrieve.create_string_buffer(record_size)
|
149
|
+
key_buff = Btrieve.create_string_buffer(record_size)
|
150
|
+
btr_op(GET_FIRST, @pos_buffer, tmp_buff, key_buff, index, [OK])
|
151
|
+
ops_result = btr_op(GET_NEXT_EXTENDED, @pos_buffer, search_buffer, key_buff, index, [OK, EOF])
|
152
|
+
num_records = search_buffer.unpack('S')[0]
|
153
|
+
record_byte_pointer=2
|
154
|
+
records = []
|
155
|
+
(0...num_records).each do |index|
|
156
|
+
record_length = search_buffer.unpack("@#{record_byte_pointer}S")[0]
|
157
|
+
record_position = search_buffer.unpack("@#{2+record_byte_pointer}i")[0]
|
158
|
+
record=search_buffer.unpack("@#{6+record_byte_pointer}a#{record_length}")[0]
|
159
|
+
record_byte_pointer += 6+record_length
|
160
|
+
unpacker = fields_to_return.inject(["", 0]) do |arr, c|
|
161
|
+
column = @schema[:columns][c]
|
162
|
+
str = BtrieveSchema.unpacker(arr[1], column[:datatype], column[:size])
|
163
|
+
arr[0] << str
|
164
|
+
arr[1] += column[:size]
|
165
|
+
arr
|
166
|
+
end
|
167
|
+
fields = record.unpack(unpacker[0])
|
168
|
+
records << {:position=>record_position, :values=>Hash[*fields_to_return.zip(fields).flatten]}
|
169
|
+
end
|
170
|
+
records
|
171
|
+
end
|
172
|
+
|
110
173
|
private
|
111
174
|
|
112
175
|
def step_next
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestExtendedBeezwax < Test::Unit::TestCase
|
4
|
+
context "when a BtrieveSession is initialized" do
|
5
|
+
setup do
|
6
|
+
BtrieveSession.create_session(PSQL_DB)
|
7
|
+
end
|
8
|
+
|
9
|
+
teardown do
|
10
|
+
BtrieveSession.destroy_session
|
11
|
+
end
|
12
|
+
|
13
|
+
should "support the creation of an extended query search buffer" do
|
14
|
+
table=BtrieveTable.new(:course)
|
15
|
+
filter = [[:name, :not_equal, 'PHI 101'], :and, [:dept_name, :equal, 'Philosophy'], :and, [:name, :not_equal, :dept_name]]
|
16
|
+
fields_to_return = table.schema[:columns].keys
|
17
|
+
encoded_buffer = nil
|
18
|
+
assert_nothing_raised do
|
19
|
+
search_buffer = BtrieveSearchBuffer.new(table, filter, fields_to_return)
|
20
|
+
encoded_buffer = search_buffer.get_encoded_buffer
|
21
|
+
end
|
22
|
+
assert_not_nil encoded_buffer
|
23
|
+
end
|
24
|
+
|
25
|
+
should "support calls to BtrieveTable.find_extended()" do
|
26
|
+
table = BtrieveTable.new(:course)
|
27
|
+
table.open do
|
28
|
+
index = 0
|
29
|
+
filter = [[:dept_name, :equal, 'Philosophy'], :and, [:name, :not_equal, 'PHI 101'], :and, [:name, :not_equal, :dept_name]]
|
30
|
+
fields_to_return = table.schema[:columns].keys
|
31
|
+
records_to_fetch = 10
|
32
|
+
records = nil
|
33
|
+
assert_nothing_raised do
|
34
|
+
records = table.find_extended(index, filter, fields_to_return, records_to_fetch)
|
35
|
+
end
|
36
|
+
assert_not_nil records
|
37
|
+
assert_equal 6, records.size
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 7
|
8
|
+
- 0
|
9
|
+
version: 0.7.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Patrick Lardin
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-12-
|
17
|
+
date: 2010-12-22 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -52,11 +52,13 @@ files:
|
|
52
52
|
- lib/beezwax.rb
|
53
53
|
- lib/btrieve/btrieve_const.rb
|
54
54
|
- lib/btrieve/btrieve_core.rb
|
55
|
+
- lib/btrieve/btrieve_extended.rb
|
55
56
|
- lib/btrieve/btrieve_record.rb
|
56
57
|
- lib/btrieve/btrieve_schema.rb
|
57
58
|
- lib/btrieve/btrieve_session.rb
|
58
59
|
- lib/btrieve/btrieve_table.rb
|
59
60
|
- test/btrieve/test_beezwax.rb
|
61
|
+
- test/btrieve/test_extended_beezwax.rb
|
60
62
|
- test/helper.rb
|
61
63
|
has_rdoc: true
|
62
64
|
homepage: http://github.com/plardin/beezwax
|
@@ -92,4 +94,5 @@ specification_version: 3
|
|
92
94
|
summary: Btrieve API wrapper.
|
93
95
|
test_files:
|
94
96
|
- test/btrieve/test_beezwax.rb
|
97
|
+
- test/btrieve/test_extended_beezwax.rb
|
95
98
|
- test/helper.rb
|