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 CHANGED
@@ -1 +1 @@
1
- 0.6.2
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.6.2"
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-21}
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
 
@@ -23,6 +23,8 @@ module BtrCodes
23
23
  STEP_NEXT = 24
24
24
  STOP = 25
25
25
  RESET = 28
26
+ GET_NEXT_EXTENDED = 36
27
+ STEP_NEXT_EXTENDED = 38
26
28
  GET_EQUAL_KEY = 55
27
29
  GET_NEXT_KEY = 56
28
30
  POS_BLOCK_SIZE = 128
@@ -41,3 +41,4 @@ require_relative 'btrieve_session'
41
41
  require_relative 'btrieve_table'
42
42
  require_relative 'btrieve_schema'
43
43
  require_relative 'btrieve_record'
44
+ require_relative 'btrieve_extended'
@@ -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
- column_info = get_column_info(key_name)
61
- return nil if(column_info.nil?)
62
- val = @data_buffer.unpack(column_info[:unpacker])[0]
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
- schema_key = get_column_info(key_name)
70
- packer_string = schema_key[:unpacker]
71
- match = packer_string.match(/@(\d+)([a-zA-Z]+)(\d*)/)
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
- # TODO - Add a unit test for this method.
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
- - 6
8
- - 2
9
- version: 0.6.2
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-21 00:00:00 -08:00
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