beezwax 0.6.2 → 0.7.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 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