rufus-tokyo 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,155 +28,164 @@
28
28
  # jmettraux@gmail.com
29
29
  #
30
30
 
31
- require 'rufus/tokyo/base'
32
-
33
-
34
- module Rufus::Tokyo
35
-
36
- #
37
- # The libtokyocabinet.so methods get bound to this module
38
- #
39
- module CabinetLib #:nodoc#
40
- extend FFI::Library
41
-
42
- Rufus::Tokyo.generate_lib_mixin(self)
43
- # module Rufus::Tokyo::CabinetLibMixin
31
+ module Rufus
32
+ module Tokyo
44
33
 
45
34
  #
46
- # find Tokyo Cabinet lib
35
+ # The libtokyocabinet.so methods get bound to this module
36
+ #
37
+ module CabinetLib #:nodoc#
38
+ extend ::FFI::Library
39
+
40
+ #
41
+ # find Tokyo Cabinet lib
47
42
 
48
- paths = Array(ENV['TOKYO_CABINET_LIB'] || %w{
43
+ paths = Array(ENV['TOKYO_CABINET_LIB'] || %w{
49
44
  /opt/local/lib/libtokyocabinet.dylib
50
45
  /usr/local/lib/libtokyocabinet.dylib
51
46
  /usr/local/lib/libtokyocabinet.so
52
47
  })
53
48
 
54
- ffi_lib(paths.find { |path| File.exist?(path) })
49
+ path = paths.find { |path| File.exist?(path) }
55
50
 
56
- #
57
- # maybe put that in a standalone c_lib.rb
51
+ raise(
52
+ "didn't find Tokyo Cabinet libs on your system. " +
53
+ "Please install Tokyo Cabinet (http://tokyocabinet.sf.net)"
54
+ ) unless path
58
55
 
59
- # length of a string
60
- #
61
- attach_function :strlen, [ :string ], :int
56
+ ffi_lib(path)
62
57
 
63
- # frees a mem zone (TC style)
64
- #
65
- attach_function :tcfree, [ :pointer ], :void
58
+ class << self
59
+ alias :attfunc :attach_function
60
+ end
66
61
 
67
- #
68
- # tcadb functions
69
- #
70
- # http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
62
+ #
63
+ # maybe put that in a standalone c_lib.rb
71
64
 
72
- attach_function :tcadbnew, [], :pointer
65
+ # length of a string
66
+ #
67
+ attfunc :strlen, [ :string ], :int
73
68
 
74
- attach_function :tcadbopen, [ :pointer, :string ], :int
75
- attach_function :tcadbclose, [ :pointer ], :int
69
+ # frees a mem zone (TC style)
70
+ #
71
+ attfunc :tcfree, [ :pointer ], :void
76
72
 
77
- attach_function :tcadbdel, [ :pointer ], :void
73
+ #
74
+ # tcadb functions
75
+ #
76
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
78
77
 
79
- attach_function :tcadbrnum, [ :pointer ], :uint64
80
- attach_function :tcadbsize, [ :pointer ], :uint64
78
+ attfunc :tcadbnew, [], :pointer
81
79
 
82
- attach_function :tcadbput2, [ :pointer, :string, :string ], :int
83
- attach_function :tcadbget2, [ :pointer, :string ], :string
84
- attach_function :tcadbout2, [ :pointer, :string ], :int
80
+ attfunc :tcadbopen, [ :pointer, :string ], :int
81
+ attfunc :abs_close, :tcadbclose, [ :pointer ], :int
85
82
 
86
- attach_function :tcadbiterinit, [ :pointer ], :int
87
- attach_function :tcadbiternext2, [ :pointer ], :string
83
+ attfunc :abs_del, :tcadbdel, [ :pointer ], :void
88
84
 
89
- attach_function :tcadbvanish, [ :pointer ], :int
85
+ attfunc :abs_rnum, :tcadbrnum, [ :pointer ], :uint64
86
+ attfunc :abs_size, :tcadbsize, [ :pointer ], :uint64
90
87
 
91
- attach_function :tcadbsync, [ :pointer ], :int
92
- attach_function :tcadbcopy, [ :pointer, :string ], :int
88
+ attfunc :abs_put2, :tcadbput2, [ :pointer, :string, :string ], :int
89
+ attfunc :abs_get2, :tcadbget2, [ :pointer, :string ], :string
90
+ attfunc :abs_out2, :tcadbout2, [ :pointer, :string ], :int
93
91
 
94
- #
95
- # tctdb functions
96
- #
97
- # http://tokyocabinet.sourceforge.net/spex-en.html#tctdbapi
92
+ attfunc :abs_iterinit, :tcadbiterinit, [ :pointer ], :int
93
+ attfunc :abs_iternext2, :tcadbiternext2, [ :pointer ], :string
98
94
 
99
- attach_function :tctdbnew, [], :pointer
95
+ attfunc :abs_vanish, :tcadbvanish, [ :pointer ], :int
100
96
 
101
- attach_function :tctdbopen, [ :pointer, :string, :int ], :int
97
+ attfunc :abs_sync, :tcadbsync, [ :pointer ], :int
98
+ attfunc :abs_copy, :tcadbcopy, [ :pointer, :string ], :int
102
99
 
103
- attach_function :tctdbgenuid, [ :pointer ], :int64
100
+ #
101
+ # tctdb functions
102
+ #
103
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tctdbapi
104
104
 
105
- attach_function :tctdbget, [ :pointer, :string, :int ], :pointer
105
+ attfunc :tctdbnew, [], :pointer
106
106
 
107
- attach_function :tctdbiterinit, [ :pointer ], :int
108
- attach_function :tctdbiternext2, [ :pointer ], :string
107
+ attfunc :tctdbopen, [ :pointer, :string, :int ], :int
109
108
 
110
- attach_function :tctdbput, [ :pointer, :string, :int, :pointer ], :int
111
- attach_function :tctdbput3, [ :pointer, :string, :string ], :int
112
- attach_function :tctdbout2, [ :pointer, :string ], :int
109
+ attfunc :tab_close, :tctdbclose, [ :pointer ], :int
113
110
 
114
- attach_function :tctdbecode, [ :pointer ], :int
115
- attach_function :tctdberrmsg, [ :int ], :string
111
+ attfunc :tab_genuid, :tctdbgenuid, [ :pointer ], :int64
116
112
 
117
- attach_function :tctdbclose, [ :pointer ], :int
118
- attach_function :tctdbdel, [ :pointer ], :void
113
+ attfunc :tab_get, :tctdbget, [ :pointer, :string, :int ], :pointer
119
114
 
120
- attach_function :tctdbrnum, [ :pointer ], :uint64
115
+ attfunc :tab_iterinit, :tctdbiterinit, [ :pointer ], :int
116
+ attfunc :tab_iternext2, :tctdbiternext2, [ :pointer ], :string
121
117
 
122
- attach_function :tctdbvanish, [ :pointer ], :int
118
+ attfunc :tab_put, :tctdbput, [ :pointer, :string, :int, :pointer ], :int
119
+ attfunc :tctdbput3, [ :pointer, :string, :string ], :int
123
120
 
124
- #
125
- # tctdbqry functions
126
- #
127
- # http://tokyocabinet.sourceforge.net/spex-en.html#tctdbapi
121
+ attfunc :tab_out, :tctdbout, [ :pointer, :string, :int ], :int
128
122
 
129
- attach_function :tctdbqrynew, [ :pointer ], :pointer
130
- attach_function :tctdbqrydel, [ :pointer ], :void
123
+ attfunc :tab_ecode, :tctdbecode, [ :pointer ], :int
124
+ attfunc :tab_errmsg, :tctdberrmsg, [ :int ], :string
131
125
 
132
- attach_function :tctdbqryaddcond, [ :pointer, :string, :int, :string ], :void
133
- attach_function :tctdbqrysetorder, [ :pointer, :string, :int ], :void
134
- attach_function :tctdbqrysetmax, [ :pointer, :int ], :void
126
+ attfunc :tab_del, :tctdbdel, [ :pointer ], :void
135
127
 
136
- attach_function :tctdbqrysearch, [ :pointer ], :pointer
128
+ attfunc :tab_rnum, :tctdbrnum, [ :pointer ], :uint64
137
129
 
138
- attach_function :tctdbqrydel, [ :pointer ], :void
130
+ attfunc :tab_vanish, :tctdbvanish, [ :pointer ], :int
139
131
 
140
- #
141
- # tcmap functions
142
- #
143
- # http://tokyocabinet.sourceforge.net/spex-en.html#tcutilapi
132
+ attfunc :tab_setindex, :tctdbsetindex, [ :pointer, :string, :int ], :int
144
133
 
145
- attach_function :tcmapnew, [], :pointer
134
+ #
135
+ # tctdbqry functions
136
+ #
137
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tctdbapi
146
138
 
147
- attach_function :tcmapput2, [ :pointer, :string, :string ], :void
148
- attach_function :tcmapout2, [ :pointer, :string ], :int
149
- attach_function :tcmapclear, [ :pointer ], :void
139
+ attfunc :qry_new, :tctdbqrynew, [ :pointer ], :pointer
140
+ attfunc :qry_del, :tctdbqrydel, [ :pointer ], :void
150
141
 
151
- attach_function :tcmapdel, [ :pointer ], :void
142
+ attfunc :qry_addcond, :tctdbqryaddcond, [ :pointer, :string, :int, :string ], :void
143
+ attfunc :qry_setorder, :tctdbqrysetorder, [ :pointer, :string, :int ], :void
144
+ attfunc :qry_setmax, :tctdbqrysetmax, [ :pointer, :int ], :void
152
145
 
153
- attach_function :tcmapget2, [ :pointer, :string ], :string
146
+ attfunc :qry_search, :tctdbqrysearch, [ :pointer ], :pointer
154
147
 
155
- attach_function :tcmapiterinit, [ :pointer ], :void
156
- attach_function :tcmapiternext2, [ :pointer ], :string
148
+ #
149
+ # tcmap functions
150
+ #
151
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tcutilapi
157
152
 
158
- attach_function :tcmaprnum, [ :pointer ], :uint64
153
+ attfunc :tcmapnew, [], :pointer
159
154
 
160
- #
161
- # tclist functions
162
- #
163
- # http://tokyocabinet.sourceforge.net/spex-en.html#tcutilapi
155
+ attfunc :tcmapput2, [ :pointer, :string, :string ], :void
156
+ attfunc :tcmapout2, [ :pointer, :string ], :int
157
+ attfunc :tcmapclear, [ :pointer ], :void
158
+
159
+ attfunc :tcmapdel, [ :pointer ], :void
164
160
 
165
- attach_function :tclistnew, [], :pointer
161
+ attfunc :tcmapget2, [ :pointer, :string ], :string
166
162
 
167
- attach_function :tclistnum, [ :pointer ], :int
168
- attach_function :tclistval2, [ :pointer, :int ], :string
163
+ attfunc :tcmapiterinit, [ :pointer ], :void
164
+ attfunc :tcmapiternext2, [ :pointer ], :string
169
165
 
170
- attach_function :tclistpush2, [ :pointer, :string ], :void
171
- attach_function :tclistpop2, [ :pointer ], :string
172
- attach_function :tclistshift2, [ :pointer ], :string
173
- attach_function :tclistunshift2, [ :pointer, :string ], :void
174
- attach_function :tclistover2, [ :pointer, :int, :string ], :void
166
+ attfunc :tcmaprnum, [ :pointer ], :uint64
175
167
 
176
- attach_function :tclistremove2, [ :pointer, :int ], :string
168
+ #
169
+ # tclist functions
170
+ #
171
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tcutilapi
172
+
173
+ attfunc :tclistnew, [], :pointer
174
+
175
+ attfunc :tclistnum, [ :pointer ], :int
176
+ attfunc :tclistval2, [ :pointer, :int ], :string
177
+
178
+ attfunc :tclistpush2, [ :pointer, :string ], :void
179
+ attfunc :tclistpop2, [ :pointer ], :string
180
+ attfunc :tclistshift2, [ :pointer ], :string
181
+ attfunc :tclistunshift2, [ :pointer, :string ], :void
182
+ attfunc :tclistover2, [ :pointer, :int, :string ], :void
183
+
184
+ attfunc :tclistremove2, [ :pointer, :int ], :string
177
185
  # beware, seems like have to free the return string self
178
186
 
179
- attach_function :tclistdel, [ :pointer ], :void
187
+ attfunc :tclistdel, [ :pointer ], :void
188
+ end
189
+
180
190
  end
181
191
  end
182
-
@@ -28,515 +28,607 @@
28
28
  # jmettraux@gmail.com
29
29
  #
30
30
 
31
- require 'rufus/tokyo/cabinet/lib'
32
- require 'rufus/tokyo/cabinet/util'
33
-
34
-
35
- module Rufus::Tokyo
36
-
37
- #
38
- # A 'table' a table database.
39
- #
40
- # http://alpha.mixi.co.jp/blog/?p=290
41
- # http://tokyocabinet.sourceforge.net/spex-en.html#tctdbapi
42
- #
43
- # A short example :
44
- #
45
- # require 'rubygems'
46
- # require 'rufus/tokyo/cabinet/table'
47
- #
48
- # t = Rufus::Tokyo::Table.new('table.tdb', :create, :write)
49
- # # '.tdb' suffix is a must
50
- #
51
- # t['pk0'] = { 'name' => 'alfred', 'age' => '22' }
52
- # t['pk1'] = { 'name' => 'bob', 'age' => '18' }
53
- # t['pk2'] = { 'name' => 'charly', 'age' => '45' }
54
- # t['pk3'] = { 'name' => 'doug', 'age' => '77' }
55
- # t['pk4'] = { 'name' => 'ephrem', 'age' => '32' }
56
- #
57
- # p t.query { |q|
58
- # q.add_condition 'age', :numge, '32'
59
- # q.order_by 'age'
60
- # q.limit 2
61
- # }
62
- # # => [ {"name"=>"ephrem", :pk=>"pk4", "age"=>"32"},
63
- # # {"name"=>"charly", :pk=>"pk2", "age"=>"45"} ]
64
- #
65
- # t.close
66
- #
67
- class Table
68
- include CabinetLibMixin
69
- include TokyoContainerMixin
70
- include HashMethods
31
+ module Rufus
32
+ module Tokyo
71
33
 
72
- #
73
- # Creates a Table instance (creates or opens it depending on the args)
74
- #
75
- # For example,
76
- #
77
- # t = Rufus::Tokyo::Table.new('table.tdb', :create, :write)
78
- # # '.tdb' suffix is a must
79
- #
80
- # will create the table.tdb (or simply open it if already present)
81
- # and make sure we have write access to it.
82
- # Note that the suffix (.tdc) is relevant to Tokyo Cabinet, using another
83
- # will result in a Tokyo Cabinet error.
84
- #
85
- def initialize (*args)
34
+ module OpenModes
86
35
 
87
- path = args.first # car
88
- params = args[1..-1] # cdr
36
+ #
37
+ # some Tokyo constants
89
38
 
90
- mode = compute_open_mode(params)
39
+ OREADER = 1 << 0 # open as a reader
40
+ OWRITER = 1 << 1 # open as a writer
41
+ OCREAT = 1 << 2 # writer creating
42
+ OTRUNC = 1 << 3 # writer truncating
43
+ ONOLCK = 1 << 4 # open without locking
44
+ OLCKNB = 1 << 5 # lock without blocking
91
45
 
92
- @db = self.lib.tctdbnew
46
+ OTSYNC = 1 << 6 # synchronize every transaction (tctdb.h)
93
47
 
94
- (lib.tctdbopen(@db, path, compute_open_mode(params)) == 1 ) || raise_error
95
- end
48
+ #
49
+ # Makes sure that a set of parameters is a hash (will transform an
50
+ # array into a hash if necessary)
51
+ #
52
+ def params_to_h (params)
96
53
 
97
- #
98
- # Closes the table (and frees the datastructure allocated for it),
99
- # returns true in case of success.
100
- #
101
- def close
102
- result = lib.tctdbclose(@db)
103
- lib.tctdbdel(@db)
104
- (result == 1)
54
+ params.is_a?(Hash) ?
55
+ params :
56
+ Array(params).inject({}) { |h, e| h[e] = true; h }
57
+ end
58
+
59
+ #
60
+ # Given params (array or hash), computes the open mode (an int)
61
+ # for the Tokyo Cabinet object.
62
+ #
63
+ def compute_open_mode (params)
64
+
65
+ params = params_to_h(params)
66
+
67
+ i = {
68
+ :read => OREADER,
69
+ :reader => OREADER,
70
+ :write => OWRITER,
71
+ :writer => OWRITER,
72
+ :create => OCREAT,
73
+ :truncate => OTRUNC,
74
+ :no_lock => ONOLCK,
75
+ :lock_no_block => OLCKNB,
76
+ :sync_every => OTSYNC
77
+
78
+ }.inject(0) { |r, (k, v)|
79
+
80
+ r = r | v if params[k]; r
81
+ }
82
+
83
+ unless params[:read_only] || params[:readonly]
84
+ i = i | OCREAT
85
+ i = i | OWRITER
86
+ end
87
+
88
+ i
89
+ end
105
90
  end
106
91
 
107
92
  #
108
- # Generates a unique id (in the context of this Table instance)
93
+ # A 'table' a table database.
109
94
  #
110
- def generate_unique_id
111
- lib.tctdbgenuid(@db)
112
- end
113
- alias :genuid :generate_unique_id
114
-
95
+ # http://alpha.mixi.co.jp/blog/?p=290
96
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tctdbapi
115
97
  #
116
- # Accepts a variable number of arguments, at least two. First one
117
- # is the primary key of the record, the others are the columns.
98
+ # A short example :
118
99
  #
119
- # One can also directly write
100
+ # require 'rubygems'
101
+ # require 'rufus/tokyo/cabinet/table'
120
102
  #
121
- # table['one'] = [ 'name', 'toto', 'age', '33' ]
122
- # table['two'] = [ 'name', 'fred', 'age', '45' ]
103
+ # t = Rufus::Tokyo::Table.new('table.tdb', :create, :write)
104
+ # # '.tdb' suffix is a must
123
105
  #
124
- # instead of
106
+ # t['pk0'] = { 'name' => 'alfred', 'age' => '22' }
107
+ # t['pk1'] = { 'name' => 'bob', 'age' => '18' }
108
+ # t['pk2'] = { 'name' => 'charly', 'age' => '45' }
109
+ # t['pk3'] = { 'name' => 'doug', 'age' => '77' }
110
+ # t['pk4'] = { 'name' => 'ephrem', 'age' => '32' }
125
111
  #
126
- # table.tabbed_put('one', 'name', 'toto', 'age', '33')
127
- # table.tabbed_put('two', 'name', 'fred', 'age', '45')
112
+ # p t.query { |q|
113
+ # q.add_condition 'age', :numge, '32'
114
+ # q.order_by 'age'
115
+ # q.limit 2
116
+ # }
117
+ # # => [ {"name"=>"ephrem", :pk=>"pk4", "age"=>"32"},
118
+ # # {"name"=>"charly", :pk=>"pk2", "age"=>"45"} ]
128
119
  #
129
- # beware : inserting an array uses a tab separator...
120
+ # t.close
130
121
  #
131
- def tabbed_put (pk, *args)
122
+ class Table
132
123
 
133
- cols = args.collect { |e| e.to_s }.join("\t")
124
+ include HashMethods
125
+ include OpenModes
134
126
 
135
- (lib.tctdbput3(@db, pk, cols) == 1) || raise_error
127
+ #
128
+ # Creates a Table instance (creates or opens it depending on the args)
129
+ #
130
+ # For example,
131
+ #
132
+ # t = Rufus::Tokyo::Table.new('table.tdb', :create, :write)
133
+ # # '.tdb' suffix is a must
134
+ #
135
+ # will create the table.tdb (or simply open it if already present)
136
+ # and make sure we have write access to it.
137
+ # Note that the suffix (.tdc) is relevant to Tokyo Cabinet, using another
138
+ # will result in a Tokyo Cabinet error.
139
+ #
140
+ def initialize (*args)
136
141
 
137
- args
138
- end
142
+ path = args.first # car
143
+ params = args[1..-1] # cdr
139
144
 
140
- #
141
- # Inserts a record in the table db
142
- #
143
- # table['pk0'] = [ 'name', 'fred', 'age', '45' ]
144
- # table['pk1'] = { 'name' => 'jeff', 'age' => '46' }
145
- #
146
- def []= (pk, h_or_a)
145
+ mode = compute_open_mode(params)
147
146
 
148
- return tabbed_put(pk, *h_or_a) if h_or_a.is_a?(Array)
147
+ @db = lib.tctdbnew
149
148
 
150
- pklen = lib.strlen(pk)
149
+ (lib.tctdbopen(@db, path, mode) == 1 ) || raise_error
150
+ end
151
151
 
152
- m = Rufus::Tokyo::Map.from_h(h_or_a)
152
+ #
153
+ # using the cabinet lib
154
+ #
155
+ def lib
156
+ CabinetLib
157
+ end
153
158
 
154
- r = lib.tctdbput(@db, pk, pklen, m.pointer)
159
+ #
160
+ # Closes the table (and frees the datastructure allocated for it),
161
+ # returns true in case of success.
162
+ #
163
+ def close
164
+ result = lib.tab_close(@db)
165
+ lib.tab_del(@db)
166
+ (result == 1)
167
+ end
155
168
 
156
- m.free
169
+ #
170
+ # Generates a unique id (in the context of this Table instance)
171
+ #
172
+ def generate_unique_id
173
+ lib.tab_genuid(@db)
174
+ end
175
+ alias :genuid :generate_unique_id
176
+
177
+ INDEX_TYPES = {
178
+ :lexical => 0,
179
+ :decimal => 1,
180
+ :void => 9999,
181
+ :remove => 9999,
182
+ :keep => 1 << 24
183
+ }
184
+
185
+ #
186
+ # Sets an index on a column of the table.
187
+ #
188
+ # Types maybe be :lexical or :decimal, use :keep to "add" and
189
+ # :remove (or :void) to "remove" an index.
190
+ #
191
+ # If column_name is :pk or "", the index will be set on the primary key.
192
+ #
193
+ # Returns true in case of success.
194
+ #
195
+ def set_index (column_name, *types)
196
+
197
+ column_name = '' if column_name == :pk
198
+
199
+ i = types.inject(0) { |i, t| i = i & INDEX_TYPES[t]; i }
200
+
201
+ (lib.tab_setindex(@db, column_name, i) == 1)
202
+ end
157
203
 
158
- (r == 1) || raise_error
204
+ #
205
+ # Accepts a variable number of arguments, at least two. First one
206
+ # is the primary key of the record, the others are the columns.
207
+ #
208
+ # One can also directly write
209
+ #
210
+ # table['one'] = [ 'name', 'toto', 'age', '33' ]
211
+ # table['two'] = [ 'name', 'fred', 'age', '45' ]
212
+ #
213
+ # instead of
214
+ #
215
+ # table.tabbed_put('one', 'name', 'toto', 'age', '33')
216
+ # table.tabbed_put('two', 'name', 'fred', 'age', '45')
217
+ #
218
+ # beware : inserting an array uses a tab separator...
219
+ #
220
+ def tabbed_put (pk, *args)
221
+
222
+ cols = args.collect { |e| e.to_s }.join("\t")
223
+
224
+ (CabinetLib.tctdbput3(@db, pk, cols) == 1) || raise_error
225
+
226
+ args
227
+ end
159
228
 
160
- h_or_a
161
- end
229
+ #
230
+ # Inserts a record in the table db
231
+ #
232
+ # table['pk0'] = [ 'name', 'fred', 'age', '45' ]
233
+ # table['pk1'] = { 'name' => 'jeff', 'age' => '46' }
234
+ #
235
+ def []= (pk, h_or_a)
162
236
 
163
- #
164
- # Removes an entry in the table
165
- #
166
- # (might raise an error if the delete itself failed, but returns nil
167
- # if there was no entry for the given key)
168
- #
169
- def delete (k)
170
- v = self[k]
171
- return nil unless v
172
- (lib.tctdbout2(@db, k) == 1) || raise_error
173
- v
174
- end
237
+ return tabbed_put(pk, *h_or_a) if h_or_a.is_a?(Array)
175
238
 
176
- #
177
- # Removes all records in this table database
178
- #
179
- def clear
180
- (lib.tctdbvanish(@db) == 1) || raise_error
181
- end
239
+ pklen = CabinetLib.strlen(pk)
182
240
 
183
- #
184
- # Returns the value (as a Ruby Hash) else nil
185
- #
186
- # (the actual #[] method is provided by HashMethods)
187
- #
188
- def get (k)
189
- m = lib.tctdbget(@db, k, lib.strlen(k))
190
- return nil if m.address == 0 # :( too bad, but it works
191
- Rufus::Tokyo::Map.to_h(m) # which frees the map
192
- end
193
- protected :get
241
+ m = Map.from_h(h_or_a)
194
242
 
195
- #
196
- # Returns an array of all the primary keys in the table
197
- #
198
- def keys
199
- a = []
200
- lib.tctdbiterinit(@db)
201
- while (k = (lib.tctdbiternext2(@db) rescue nil)); a << k; end
202
- a
203
- end
243
+ r = lib.tab_put(@db, pk, pklen, m.pointer)
204
244
 
205
- #
206
- # Returns the number of records in this table db
207
- #
208
- def size
209
- lib.tctdbrnum(@db)
210
- end
245
+ m.free
211
246
 
212
- #
213
- # Prepares a query instance (block is optional)
214
- #
215
- def prepare_query (&block)
216
- q = TableQuery.new(self)
217
- block.call(q) if block
218
- q
219
- end
247
+ (r == 1) || raise_error
220
248
 
221
- #
222
- # Prepares and runs a query, returns a ResultSet instance
223
- # (takes care of freeing the query structure)
224
- #
225
- def do_query (&block)
226
- q = prepare_query(&block)
227
- rs = q.run
228
- q.free
229
- rs
230
- end
249
+ h_or_a
250
+ end
231
251
 
232
- #
233
- # Prepares and runs a query, returns an array of hashes (all Ruby)
234
- # (takes care of freeing the query and the result set structures)
235
- #
236
- def query (&block)
237
- rs = do_query(&block)
238
- a = rs.to_a
239
- rs.free
240
- a
241
- end
252
+ #
253
+ # Removes an entry in the table
254
+ #
255
+ # (might raise an error if the delete itself failed, but returns nil
256
+ # if there was no entry for the given key)
257
+ #
258
+ def delete (k)
259
+ v = self[k]
260
+ return nil unless v
261
+ (lib.tab_out(@db, k, CabinetLib.strlen(k)) == 1) || raise_error
262
+ v
263
+ end
242
264
 
243
- #
244
- # Returns the actual pointer to the Tokyo Cabinet table
245
- #
246
- def pointer
247
- @db
248
- end
265
+ #
266
+ # Removes all records in this table database
267
+ #
268
+ def clear
269
+ (lib.tab_vanish(@db) == 1) || raise_error
270
+ end
249
271
 
250
- protected
272
+ #
273
+ # Returns the value (as a Ruby Hash) else nil
274
+ #
275
+ # (the actual #[] method is provided by HashMethods)
276
+ #
277
+ def get (k)
278
+ m = lib.tab_get(@db, k, CabinetLib.strlen(k))
279
+ return nil if m.address == 0 # :( too bad, but it works
280
+ Map.to_h(m) # which frees the map
281
+ end
282
+ protected :get
283
+
284
+ #
285
+ # Returns an array of all the primary keys in the table
286
+ #
287
+ def keys
288
+ a = []
289
+ lib.tab_iterinit(@db)
290
+ while (k = (lib.tab_iternext2(@db) rescue nil)); a << k; end
291
+ a
292
+ end
251
293
 
252
- #
253
- # Obviously something got wrong, let's ask the db about it and raise
254
- # a TokyoError
255
- #
256
- def raise_error
294
+ #
295
+ # Returns the number of records in this table db
296
+ #
297
+ def size
298
+ lib.tab_rnum(@db)
299
+ end
257
300
 
258
- err_code = lib.tctdbecode(@db)
259
- err_msg = lib.tctdberrmsg(err_code)
301
+ #
302
+ # Prepares a query instance (block is optional)
303
+ #
304
+ def prepare_query (&block)
305
+ q = TableQuery.new(self)
306
+ block.call(q) if block
307
+ q
308
+ end
260
309
 
261
- raise TokyoError, "(err #{err_code}) #{err_msg}"
262
- end
263
- end
310
+ #
311
+ # Prepares and runs a query, returns a ResultSet instance
312
+ # (takes care of freeing the query structure)
313
+ #
314
+ def do_query (&block)
315
+ q = prepare_query(&block)
316
+ rs = q.run
317
+ q.free
318
+ rs
319
+ end
264
320
 
265
- #
266
- # A query on a Tokyo Cabinet table db
267
- #
268
- class TableQuery
269
- include CabinetLibMixin
270
-
271
- OPERATORS = {
272
-
273
- # strings...
274
-
275
- :streq => 0, # string equality
276
- :eq => 0,
277
- :eql => 0,
278
- :equals => 0,
279
-
280
- :strinc => 1, # string include
281
- :inc => 1, # string include
282
- :includes => 1, # string include
283
-
284
- :strbw => 2, # string begins with
285
- :bw => 2,
286
- :starts_with => 2,
287
- :strew => 3, # string ends with
288
- :ew => 3,
289
- :ends_with => 3,
290
-
291
- :strand => 4, # string which include all the tokens in the given exp
292
- :and => 4,
293
-
294
- :stror => 5, # string which include at least one of the tokens
295
- :or => 5,
296
-
297
- :stroreq => 6, # string which is equal to at least one token
298
-
299
- :strorrx => 7, # string which matches the given regex
300
- :regex => 7,
301
- :matches => 7,
302
-
303
- # numbers...
304
-
305
- :numeq => 8, # equal
306
- :numequals => 8,
307
- :numgt => 9, # greater than
308
- :gt => 9,
309
- :numge => 10, # greater or equal
310
- :ge => 10,
311
- :gte => 10,
312
- :numlt => 11, # greater or equal
313
- :lt => 11,
314
- :numle => 12, # greater or equal
315
- :le => 12,
316
- :lte => 12,
317
- :numbt => 13, # a number between two tokens in the given exp
318
- :bt => 13,
319
- :between => 13,
320
-
321
- :numoreq => 14 # number which is equal to at least one token
322
- }
323
-
324
- TDBQCNEGATE = 1 << 24
325
- TDBQCNOIDX = 1 << 25
326
-
327
- DIRECTIONS = {
328
- :strasc => 0,
329
- :strdesc => 1,
330
- :asc => 0,
331
- :desc => 1,
332
- :numasc => 2,
333
- :numdesc => 3
334
- }
321
+ #
322
+ # Prepares and runs a query, returns an array of hashes (all Ruby)
323
+ # (takes care of freeing the query and the result set structures)
324
+ #
325
+ def query (&block)
326
+ rs = do_query(&block)
327
+ a = rs.to_a
328
+ rs.free
329
+ a
330
+ end
335
331
 
336
- #
337
- # Creates a query for a given Rufus::Tokyo::Table
338
- #
339
- # Queries are usually created via the #query (#prepare_query #do_query)
340
- # of the Table instance.
341
- #
342
- # Methods of interest here are :
343
- #
344
- # * #add (or #add_condition)
345
- # * #order_by
346
- # * #limit
347
- #
348
- # also
349
- #
350
- # * #pk_only
351
- # * #no_pk
352
- #
353
- def initialize (table)
354
- @table = table
355
- @query = lib.tctdbqrynew(@table.pointer)
356
- @opts = {}
357
- end
332
+ #
333
+ # Returns the actual pointer to the Tokyo Cabinet table
334
+ #
335
+ def pointer
336
+ @db
337
+ end
358
338
 
359
- #
360
- # Adds a condition
361
- #
362
- # table.query { |q|
363
- # q.add 'name', :equals, 'Oppenheimer'
364
- # q.add 'age', :numgt, 35
365
- # }
366
- #
367
- # Understood 'operators' :
368
- #
369
- # :streq # string equality
370
- # :eq
371
- # :eql
372
- # :equals
373
- #
374
- # :strinc # string include
375
- # :inc # string include
376
- # :includes # string include
377
- #
378
- # :strbw # string begins with
379
- # :bw
380
- # :starts_with
381
- # :strew # string ends with
382
- # :ew
383
- # :ends_with
384
- #
385
- # :strand # string which include all the tokens in the given exp
386
- # :and
387
- #
388
- # :stror # string which include at least one of the tokens
389
- # :or
390
- #
391
- # :stroreq # string which is equal to at least one token
392
- #
393
- # :strorrx # string which matches the given regex
394
- # :regex
395
- # :matches
396
- #
397
- # # numbers...
398
- #
399
- # :numeq # equal
400
- # :numequals
401
- # :numgt # greater than
402
- # :gt
403
- # :numge # greater or equal
404
- # :ge
405
- # :gte
406
- # :numlt # greater or equal
407
- # :lt
408
- # :numle # greater or equal
409
- # :le
410
- # :lte
411
- # :numbt # a number between two tokens in the given exp
412
- # :bt
413
- # :between
414
- #
415
- # :numoreq # number which is equal to at least one token
416
- #
417
- def add (colname, operator, val, affirmative=true, no_index=true)
418
- op = operator.is_a?(Fixnum) ? operator : OPERATORS[operator]
419
- op = op | TDBQCNEGATE unless affirmative
420
- op = op | TDBQCNOIDX if no_index
421
- lib.tctdbqryaddcond(@query, colname, op, val)
422
- end
423
- alias :add_condition :add
339
+ protected
424
340
 
425
- #
426
- # Sets the max number of records to return for this query.
427
- #
428
- # (sorry no 'offset' as of now)
429
- #
430
- def limit (i)
431
- lib.tctdbqrysetmax(@query, i)
432
- end
341
+ #
342
+ # Obviously something got wrong, let's ask the db about it and raise
343
+ # a TokyoError
344
+ #
345
+ def raise_error
433
346
 
434
- #
435
- # Sets the sort order for the result of the query
436
- #
437
- # The 'direction' may be :
438
- #
439
- # :strasc # string ascending
440
- # :strdesc
441
- # :asc # string ascending
442
- # :desc
443
- # :numasc # number ascending
444
- # :numdesc
445
- #
446
- def order_by (colname, direction=:strasc)
447
- lib.tctdbqrysetorder(@query, colname, DIRECTIONS[direction])
448
- end
347
+ err_code = lib.tab_ecode(@db)
348
+ err_msg = lib.tab_errmsg(err_code)
449
349
 
450
- #
451
- # When set to true, only the primary keys of the matching records will
452
- # be returned.
453
- #
454
- def pk_only (on=true)
455
- @opts[:pk_only] = on
350
+ raise TokyoError, "(err #{err_code}) #{err_msg}"
351
+ end
456
352
  end
457
353
 
458
354
  #
459
- # When set to true, the :pk (primary key) is not inserted in the record
460
- # (hashes) returned
461
- #
462
- def no_pk (on=true)
463
- @opts[:no_pk] = on
464
- end
355
+ # A query on a Tokyo Cabinet table db
356
+ #
357
+ class TableQuery
358
+
359
+ OPERATORS = {
360
+
361
+ # strings...
362
+
363
+ :streq => 0, # string equality
364
+ :eq => 0,
365
+ :eql => 0,
366
+ :equals => 0,
367
+
368
+ :strinc => 1, # string include
369
+ :inc => 1, # string include
370
+ :includes => 1, # string include
371
+
372
+ :strbw => 2, # string begins with
373
+ :bw => 2,
374
+ :starts_with => 2,
375
+ :strew => 3, # string ends with
376
+ :ew => 3,
377
+ :ends_with => 3,
378
+
379
+ :strand => 4, # string which include all the tokens in the given exp
380
+ :and => 4,
381
+
382
+ :stror => 5, # string which include at least one of the tokens
383
+ :or => 5,
384
+
385
+ :stroreq => 6, # string which is equal to at least one token
386
+
387
+ :strorrx => 7, # string which matches the given regex
388
+ :regex => 7,
389
+ :matches => 7,
390
+
391
+ # numbers...
392
+
393
+ :numeq => 8, # equal
394
+ :numequals => 8,
395
+ :numgt => 9, # greater than
396
+ :gt => 9,
397
+ :numge => 10, # greater or equal
398
+ :ge => 10,
399
+ :gte => 10,
400
+ :numlt => 11, # greater or equal
401
+ :lt => 11,
402
+ :numle => 12, # greater or equal
403
+ :le => 12,
404
+ :lte => 12,
405
+ :numbt => 13, # a number between two tokens in the given exp
406
+ :bt => 13,
407
+ :between => 13,
408
+
409
+ :numoreq => 14 # number which is equal to at least one token
410
+ }
411
+
412
+ TDBQCNEGATE = 1 << 24
413
+ TDBQCNOIDX = 1 << 25
414
+
415
+ DIRECTIONS = {
416
+ :strasc => 0,
417
+ :strdesc => 1,
418
+ :asc => 0,
419
+ :desc => 1,
420
+ :numasc => 2,
421
+ :numdesc => 3
422
+ }
423
+
424
+ #
425
+ # Creates a query for a given Rufus::Tokyo::Table
426
+ #
427
+ # Queries are usually created via the #query (#prepare_query #do_query)
428
+ # of the Table instance.
429
+ #
430
+ # Methods of interest here are :
431
+ #
432
+ # * #add (or #add_condition)
433
+ # * #order_by
434
+ # * #limit
435
+ #
436
+ # also
437
+ #
438
+ # * #pk_only
439
+ # * #no_pk
440
+ #
441
+ def initialize (table)
442
+ @table = table
443
+ @query = @table.lib.qry_new(@table.pointer)
444
+ @opts = {}
445
+ end
465
446
 
466
- #
467
- # Runs this query (returns a TableResultSet instance)
468
- #
469
- def run
470
- TableResultSet.new(@table, lib.tctdbqrysearch(@query), @opts)
471
- end
447
+ def lib
448
+ @table.lib
449
+ end
472
450
 
473
- #
474
- # Frees this data structure
475
- #
476
- def free
477
- lib.tctdbqrydel(@query)
478
- @query = nil
479
- end
451
+ #
452
+ # Adds a condition
453
+ #
454
+ # table.query { |q|
455
+ # q.add 'name', :equals, 'Oppenheimer'
456
+ # q.add 'age', :numgt, 35
457
+ # }
458
+ #
459
+ # Understood 'operators' :
460
+ #
461
+ # :streq # string equality
462
+ # :eq
463
+ # :eql
464
+ # :equals
465
+ #
466
+ # :strinc # string include
467
+ # :inc # string include
468
+ # :includes # string include
469
+ #
470
+ # :strbw # string begins with
471
+ # :bw
472
+ # :starts_with
473
+ # :strew # string ends with
474
+ # :ew
475
+ # :ends_with
476
+ #
477
+ # :strand # string which include all the tokens in the given exp
478
+ # :and
479
+ #
480
+ # :stror # string which include at least one of the tokens
481
+ # :or
482
+ #
483
+ # :stroreq # string which is equal to at least one token
484
+ #
485
+ # :strorrx # string which matches the given regex
486
+ # :regex
487
+ # :matches
488
+ #
489
+ # # numbers...
490
+ #
491
+ # :numeq # equal
492
+ # :numequals
493
+ # :numgt # greater than
494
+ # :gt
495
+ # :numge # greater or equal
496
+ # :ge
497
+ # :gte
498
+ # :numlt # greater or equal
499
+ # :lt
500
+ # :numle # greater or equal
501
+ # :le
502
+ # :lte
503
+ # :numbt # a number between two tokens in the given exp
504
+ # :bt
505
+ # :between
506
+ #
507
+ # :numoreq # number which is equal to at least one token
508
+ #
509
+ def add (colname, operator, val, affirmative=true, no_index=true)
510
+ op = operator.is_a?(Fixnum) ? operator : OPERATORS[operator]
511
+ op = op | TDBQCNEGATE unless affirmative
512
+ op = op | TDBQCNOIDX if no_index
513
+ lib.qry_addcond(@query, colname, op, val)
514
+ end
515
+ alias :add_condition :add
516
+
517
+ #
518
+ # Sets the max number of records to return for this query.
519
+ #
520
+ # (sorry no 'offset' as of now)
521
+ #
522
+ def limit (i)
523
+ lib.qry_setmax(@query, i)
524
+ end
480
525
 
481
- alias :close :free
482
- alias :destroy :free
483
- end
526
+ #
527
+ # Sets the sort order for the result of the query
528
+ #
529
+ # The 'direction' may be :
530
+ #
531
+ # :strasc # string ascending
532
+ # :strdesc
533
+ # :asc # string ascending
534
+ # :desc
535
+ # :numasc # number ascending
536
+ # :numdesc
537
+ #
538
+ def order_by (colname, direction=:strasc)
539
+ lib.qry_setorder(@query, colname, DIRECTIONS[direction])
540
+ end
541
+
542
+ #
543
+ # When set to true, only the primary keys of the matching records will
544
+ # be returned.
545
+ #
546
+ def pk_only (on=true)
547
+ @opts[:pk_only] = on
548
+ end
549
+
550
+ #
551
+ # When set to true, the :pk (primary key) is not inserted in the record
552
+ # (hashes) returned
553
+ #
554
+ def no_pk (on=true)
555
+ @opts[:no_pk] = on
556
+ end
557
+
558
+ #
559
+ # Runs this query (returns a TableResultSet instance)
560
+ #
561
+ def run
562
+ TableResultSet.new(@table, lib.qry_search(@query), @opts)
563
+ end
484
564
 
485
- #
486
- # The thing queries return
487
- #
488
- class TableResultSet
489
- include CabinetLibMixin
490
- include Enumerable
491
-
492
- def initialize (table, list_pointer, query_opts)
493
- @table = table
494
- @list = list_pointer
495
- @opts = query_opts
565
+ #
566
+ # Frees this data structure
567
+ #
568
+ def free
569
+ lib.qry_del(@query)
570
+ @query = nil
571
+ end
572
+
573
+ alias :close :free
574
+ alias :destroy :free
496
575
  end
497
576
 
498
577
  #
499
- # Returns the count of element in this result set
578
+ # The thing queries return
500
579
  #
501
- def size
502
- lib.tclistnum(@list)
503
- end
580
+ class TableResultSet
581
+ include Enumerable
504
582
 
505
- alias :length :size
583
+ def initialize (table, list_pointer, query_opts)
584
+ @table = table
585
+ @list = list_pointer
586
+ @opts = query_opts
587
+ end
506
588
 
507
- #
508
- # The classical each
509
- #
510
- def each
511
- (0..size-1).each do |i|
512
- pk = lib.tclistval2(@list, i)
513
- if @opts[:pk_only]
514
- yield(pk)
515
- else
516
- val = @table[pk]
517
- val[:pk] = pk unless @opts[:no_pk]
518
- yield(val)
589
+ #
590
+ # Returns the count of element in this result set
591
+ #
592
+ def size
593
+ CabinetLib.tclistnum(@list)
594
+ end
595
+
596
+ alias :length :size
597
+
598
+ #
599
+ # The classical each
600
+ #
601
+ def each
602
+ (0..size-1).each do |i|
603
+ pk = CabinetLib.tclistval2(@list, i)
604
+ if @opts[:pk_only]
605
+ yield(pk)
606
+ else
607
+ val = @table[pk]
608
+ val[:pk] = pk unless @opts[:no_pk]
609
+ yield(val)
610
+ end
519
611
  end
520
612
  end
521
- end
522
613
 
523
- #
524
- # Returns an array of hashes
525
- #
526
- def to_a
527
- collect { |m| m }
528
- end
614
+ #
615
+ # Returns an array of hashes
616
+ #
617
+ def to_a
618
+ collect { |m| m }
619
+ end
529
620
 
530
- #
531
- # Frees this query (the underlying Tokyo Cabinet list structure)
532
- #
533
- def free
534
- lib.tclistdel(@list)
535
- @list = nil
621
+ #
622
+ # Frees this query (the underlying Tokyo Cabinet list structure)
623
+ #
624
+ def free
625
+ CabinetLib.tclistdel(@list)
626
+ @list = nil
627
+ end
628
+
629
+ alias :close :free
630
+ alias :destroy :free
536
631
  end
537
632
 
538
- alias :close :free
539
- alias :destroy :free
540
633
  end
541
634
  end
542
-