rufus-tokyo 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -32,17 +32,20 @@ require 'ffi' # sudo gem install ffi
32
32
 
33
33
 
34
34
  module Rufus
35
- module Tokyo
35
+ module Tokyo
36
36
 
37
- VERSION = '0.1.7'
37
+ VERSION = '0.1.8'
38
38
 
39
- #
40
- # A common error class
41
- #
42
- class TokyoError < RuntimeError; end
39
+ #
40
+ # A common error class
41
+ #
42
+ class TokyoError < RuntimeError; end
43
43
 
44
- end
44
+ end
45
45
  end
46
46
 
47
- require 'rufus/tokyo/cabinet'
47
+ require 'rufus/tokyo/cabinet/lib'
48
+ require 'rufus/tokyo/cabinet/util'
49
+ require 'rufus/tokyo/cabinet/abstract'
50
+ require 'rufus/tokyo/cabinet/table'
48
51
 
@@ -28,336 +28,398 @@
28
28
  # jmettraux@gmail.com
29
29
  #
30
30
 
31
- module Rufus
32
- module Tokyo
31
+ module Rufus::Tokyo
32
+
33
+ #
34
+ # A 'cabinet', ie a Tokyo Cabinet [abstract] database.
35
+ #
36
+ # Follows the abstract API described at :
37
+ #
38
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
39
+ #
40
+ # An usage example :
41
+ #
42
+ # db = Rufus::Tokyo::Cabinet.new('test_data.tch')
43
+ # db['pillow'] = 'Shonagon'
44
+ #
45
+ # db.size # => 1
46
+ # db['pillow'] # => 'Shonagon'
47
+ #
48
+ # db.delete('pillow') # => 'Shonagon'
49
+ # db.size # => 0
50
+ #
51
+ # db.close
52
+ #
53
+ class Cabinet
54
+
55
+ include HashMethods
33
56
 
34
57
  #
35
- # A 'cabinet', ie a Tokyo Cabinet [abstract] database.
58
+ # Creates/opens the cabinet, raises an exception in case of
59
+ # creation/opening failure.
36
60
  #
37
- # Follows the abstract API described at :
61
+ # This method accepts a 'name' parameter and an optional 'params' hash
62
+ # parameter.
63
+ #
64
+ # 'name' follows the syntax described at
38
65
  #
39
66
  # http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
40
67
  #
41
- # An usage example :
42
- #
43
- # db = Rufus::Tokyo::Cabinet.new('test_data.tch')
44
- # db['pillow'] = 'Shonagon'
45
- #
46
- # db.size # => 1
47
- # db['pillow'] # => 'Shonagon'
48
- #
49
- # db.delete('pillow') # => 'Shonagon'
50
- # db.size # => 0
51
- #
52
- # db.close
53
- #
54
- class Cabinet
55
-
56
- include HashMethods
57
-
58
- #
59
- # Creates/opens the cabinet, raises an exception in case of
60
- # creation/opening failure.
61
- #
62
- # This method accepts a 'name' parameter and an optional 'params' hash
63
- # parameter.
64
- #
65
- # 'name' follows the syntax described at
66
- #
67
- # http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
68
- #
69
- # under tcadbopen(). For example :
70
- #
71
- # db = Rufus::Tokyo::Cabinet.new('casket.tch#bnum=100000#opts=ld')
72
- #
73
- # will open (eventually create) a hash database backed in the file
74
- # 'casket.tch' with a bucket number of 100000 and the 'large' and
75
- # 'deflate' options (opts) turned on.
76
- #
77
- # Note that there is an #open method similar to File#open for openening
78
- # a db and closing it when it's no longer needed :
79
- #
80
- # Rufus::Tokyo::Cabinet.new('data.tch') do |db|
81
- # db['key'] = value
82
- # end
83
- #
84
- # == database name
85
- #
86
- # From http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi :
87
- #
88
- # 'If it is "*", the database will be an on-memory hash database. If it is
89
- # "+", the database will be an on-memory tree database. If its suffix is
90
- # ".tch", the database will be a hash database. If its suffix is ".tcb",
91
- # the database will be a B+ tree database. If its suffix is ".tcf", the
92
- # database will be a fixed-length database. If its suffix is ".tct", the
93
- # database will be a table database.'
94
- #
95
- # You're supposed to give a path to the database file you want to use and
96
- # Cabinet expects you to give the proper prefix.
97
- #
98
- # db = Rufus::Tokyo::Cabinet.new('data.tch') # hash database
99
- # db = Rufus::Tokyo::Cabinet.new('data.tcb') # B+ tree db
100
- # db = Rufus::Tokyo::Cabinet.new('data.tcf') # fixed-length db
101
- #
102
- # will result with the same file names :
103
- #
104
- # db = Rufus::Tokyo::Cabinet.new('data', :type => :hash) # hash database
105
- # db = Rufus::Tokyo::Cabinet.new('data', :type => :btree) # B+ tree db
106
- # db = Rufus::Tokyo::Cabinet.new('data', :type => :fixed) # fixed-length db
107
- #
108
- # You can open an in-memory hash and an in-memory B+ tree with :
109
- #
110
- # h = Rufus::Tokyo::Cabinet.new(:mem_hash) # or
111
- # h = Rufus::Tokyo::Cabinet.new('*')
112
- #
113
- # t = Rufus::Tokyo::Cabinet.new(:mem_tree) # or
114
- # t = Rufus::Tokyo::Cabinet.new('+')
115
- #
116
- # == parameters
117
- #
118
- # There are two ways to pass parameters at the opening of a db :
119
- #
120
- # db = Rufus::Tokyo::Cabinet.new('data.tch#opts=ld#mode=w') # or
121
- # db = Rufus::Tokyo::Cabinet.new('data.tch', :opts => 'ld', :mode => 'w')
122
- #
123
- # most verbose :
124
- #
125
- # db = Rufus::Tokyo::Cabinet.new(
126
- # 'data', :type => :hash, :opts => 'ld', :mode => 'w')
127
- #
128
- # === mode
129
- #
130
- # * :mode a set of chars ('r'ead, 'w'rite, 'c'reate, 't'runcate,
131
- # 'e' non locking, 'f' non blocking lock), default is 'wc'
132
- #
133
- # === other parameters
134
- #
135
- # 'On-memory hash database supports "bnum", "capnum", and "capsiz".
136
- # On-memory tree database supports "capnum" and "capsiz".
137
- # Hash database supports "mode", "bnum", "apow", "fpow", "opts",
138
- # "rcnum", and "xmsiz".
139
- # B+ tree database supports "mode", "lmemb", "nmemb", "bnum", "apow",
140
- # "fpow", "opts", "lcnum", "ncnum", and "xmsiz".
141
- # Fixed-length database supports "mode", "width", and "limsiz"'
142
- #
143
- # * :opts a set of chars ('l'arge, 'd'eflate, 'b'zip2, 't'cbs)
144
- # (usually empty or something like 'ld' or 'lb')
145
- #
146
- # * :bnum number of elements of the bucket array
147
- # * :apow size of record alignment by power of 2 (defaults to 4)
148
- # * :fpow maximum number of elements of the free block pool by
149
- # power of 2 (defaults to 10)
150
- # * :mutex when set to true, makes sure only 1 thread at a time
151
- # accesses the table (well, Ruby, global thread lock, ...)
152
- #
153
- # * :rcnum specifies the maximum number of records to be cached.
154
- # If it is not more than 0, the record cache is disabled.
155
- # It is disabled by default.
156
- # * :lcnum specifies the maximum number of leaf nodes to be cached.
157
- # If it is not more than 0, the default value is specified.
158
- # The default value is 2048.
159
- # * :ncnum specifies the maximum number of non-leaf nodes to be
160
- # cached. If it is not more than 0, the default value is
161
- # specified. The default value is 512.
162
- #
163
- # * :xmsiz specifies the size of the extra mapped memory. If it is
164
- # not more than 0, the extra mapped memory is disabled.
165
- # The default size is 67108864.
166
- #
167
- # * :capnum specifies the capacity number of records.
168
- # * :capsiz specifies the capacity size of using memory.
169
- #
170
- #
171
- # = NOTE :
172
- #
173
- # On reopening a file, Cabinet will tend to stick to the parameters as
174
- # set when the file was opened. To change that, have a look at the
175
- # man pages of the various command line tools coming with Tokyo Cabinet.
176
- #
177
- def initialize (name, params={})
178
-
179
- @db = lib.tcadbnew
180
-
181
- name = '*' if name == :mem_hash # in memory hash database
182
- name = '+' if name == :mem_tree # in memory B+ tree database
183
-
184
- if type = params.delete(:type)
185
- name += { :hash => '.tch', :btree => '.tcb', :fixed => '.tcf' }[type]
186
- end
68
+ # under tcadbopen(). For example :
69
+ #
70
+ # db = Rufus::Tokyo::Cabinet.new('casket.tch#bnum=100000#opts=ld')
71
+ #
72
+ # will open (eventually create) a hash database backed in the file
73
+ # 'casket.tch' with a bucket number of 100000 and the 'large' and
74
+ # 'deflate' options (opts) turned on.
75
+ #
76
+ # Note that there is an #open method similar to File#open for openening
77
+ # a db and closing it when it's no longer needed :
78
+ #
79
+ # Rufus::Tokyo::Cabinet.new('data.tch') do |db|
80
+ # db['key'] = value
81
+ # end
82
+ #
83
+ # == database name
84
+ #
85
+ # From http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi :
86
+ #
87
+ # 'If it is "*", the database will be an on-memory hash database. If it is
88
+ # "+", the database will be an on-memory tree database. If its suffix is
89
+ # ".tch", the database will be a hash database. If its suffix is ".tcb",
90
+ # the database will be a B+ tree database. If its suffix is ".tcf", the
91
+ # database will be a fixed-length database. If its suffix is ".tct", the
92
+ # database will be a table database.'
93
+ #
94
+ # You're supposed to give a path to the database file you want to use and
95
+ # Cabinet expects you to give the proper prefix.
96
+ #
97
+ # db = Rufus::Tokyo::Cabinet.new('data.tch') # hash database
98
+ # db = Rufus::Tokyo::Cabinet.new('data.tcb') # B+ tree db
99
+ # db = Rufus::Tokyo::Cabinet.new('data.tcf') # fixed-length db
100
+ #
101
+ # will result with the same file names :
102
+ #
103
+ # db = Rufus::Tokyo::Cabinet.new('data', :type => :hash) # hash database
104
+ # db = Rufus::Tokyo::Cabinet.new('data', :type => :btree) # B+ tree db
105
+ # db = Rufus::Tokyo::Cabinet.new('data', :type => :fixed) # fixed-length db
106
+ #
107
+ # You can open an in-memory hash and an in-memory B+ tree with :
108
+ #
109
+ # h = Rufus::Tokyo::Cabinet.new(:mem_hash) # or
110
+ # h = Rufus::Tokyo::Cabinet.new('*')
111
+ #
112
+ # t = Rufus::Tokyo::Cabinet.new(:mem_tree) # or
113
+ # t = Rufus::Tokyo::Cabinet.new('+')
114
+ #
115
+ # == parameters
116
+ #
117
+ # There are two ways to pass parameters at the opening of a db :
118
+ #
119
+ # db = Rufus::Tokyo::Cabinet.new('data.tch#opts=ld#mode=w') # or
120
+ # db = Rufus::Tokyo::Cabinet.new('data.tch', :opts => 'ld', :mode => 'w')
121
+ #
122
+ # most verbose :
123
+ #
124
+ # db = Rufus::Tokyo::Cabinet.new(
125
+ # 'data', :type => :hash, :opts => 'ld', :mode => 'w')
126
+ #
127
+ # === mode
128
+ #
129
+ # * :mode a set of chars ('r'ead, 'w'rite, 'c'reate, 't'runcate,
130
+ # 'e' non locking, 'f' non blocking lock), default is 'wc'
131
+ #
132
+ # === other parameters
133
+ #
134
+ # 'On-memory hash database supports "bnum", "capnum", and "capsiz".
135
+ # On-memory tree database supports "capnum" and "capsiz".
136
+ # Hash database supports "mode", "bnum", "apow", "fpow", "opts",
137
+ # "rcnum", and "xmsiz".
138
+ # B+ tree database supports "mode", "lmemb", "nmemb", "bnum", "apow",
139
+ # "fpow", "opts", "lcnum", "ncnum", and "xmsiz".
140
+ # Fixed-length database supports "mode", "width", and "limsiz"'
141
+ #
142
+ # * :opts a set of chars ('l'arge, 'd'eflate, 'b'zip2, 't'cbs)
143
+ # (usually empty or something like 'ld' or 'lb')
144
+ #
145
+ # * :bnum number of elements of the bucket array
146
+ # * :apow size of record alignment by power of 2 (defaults to 4)
147
+ # * :fpow maximum number of elements of the free block pool by
148
+ # power of 2 (defaults to 10)
149
+ # * :mutex when set to true, makes sure only 1 thread at a time
150
+ # accesses the table (well, Ruby, global thread lock, ...)
151
+ #
152
+ # * :rcnum specifies the maximum number of records to be cached.
153
+ # If it is not more than 0, the record cache is disabled.
154
+ # It is disabled by default.
155
+ # * :lcnum specifies the maximum number of leaf nodes to be cached.
156
+ # If it is not more than 0, the default value is specified.
157
+ # The default value is 2048.
158
+ # * :ncnum specifies the maximum number of non-leaf nodes to be
159
+ # cached. If it is not more than 0, the default value is
160
+ # specified. The default value is 512.
161
+ #
162
+ # * :xmsiz specifies the size of the extra mapped memory. If it is
163
+ # not more than 0, the extra mapped memory is disabled.
164
+ # The default size is 67108864.
165
+ #
166
+ # * :capnum specifies the capacity number of records.
167
+ # * :capsiz specifies the capacity size of using memory.
168
+ #
169
+ #
170
+ # = NOTE :
171
+ #
172
+ # On reopening a file, Cabinet will tend to stick to the parameters as
173
+ # set when the file was opened. To change that, have a look at the
174
+ # man pages of the various command line tools coming with Tokyo Cabinet.
175
+ #
176
+ def initialize (name, params={})
187
177
 
188
- name = name + params.collect { |k, v| "##{k}=#{v}" }.join('')
178
+ @db = lib.tcadbnew
189
179
 
190
- (lib.tcadbopen(@db, name) == 1) ||
191
- raise("failed to open/create db '#{name}' #{params.inspect}")
180
+ name = '*' if name == :mem_hash # in memory hash database
181
+ name = '+' if name == :mem_tree # in memory B+ tree database
192
182
 
193
- self.default = params[:default]
194
- @default_proc ||= params[:default_proc]
183
+ if type = params.delete(:type)
184
+ name += { :hash => '.tch', :btree => '.tcb', :fixed => '.tcf' }[type]
195
185
  end
196
186
 
197
- # Same args as initialize, but can take a block form that will
198
- # close the db when done. Similar to File.open
199
- def self.open (name, params={})
200
- db = self.new(name, params)
201
- if block_given?
202
- yield db
203
- nil
204
- else
205
- db
206
- end
207
- ensure
208
- db.close if block_given? && db
209
- end
187
+ name = name + params.collect { |k, v| "##{k}=#{v}" }.join('')
210
188
 
211
- #
212
- # Returns a new in-memory hash. Accepts the same optional params hash
213
- # as new().
214
- #
215
- def self.new_hash (params={})
216
- self.new(:hash, params)
217
- end
189
+ (lib.tcadbopen(@db, name) == 1) ||
190
+ raise("failed to open/create db '#{name}' #{params.inspect}")
218
191
 
219
- #
220
- # Returns a new in-memory B+ tree. Accepts the same optional params hash
221
- # as new().
222
- #
223
- def self.new_tree (params={})
224
- self.new(:tree, params)
225
- end
192
+ self.default = params[:default]
193
+ @default_proc ||= params[:default_proc]
194
+ end
226
195
 
227
- #
228
- # using the cabinet lib
229
- #
230
- def lib
231
- CabinetLib
196
+ # Same args as initialize, but can take a block form that will
197
+ # close the db when done. Similar to File.open
198
+ def self.open (name, params={})
199
+ db = self.new(name, params)
200
+ if block_given?
201
+ yield db
202
+ nil
203
+ else
204
+ db
232
205
  end
206
+ ensure
207
+ db.close if block_given? && db
208
+ end
233
209
 
234
- #
235
- # No comment
236
- #
237
- def []= (k, v)
238
- lib.abs_put2(@db, k, v)
239
- end
210
+ #
211
+ # Returns a new in-memory hash. Accepts the same optional params hash
212
+ # as new().
213
+ #
214
+ def self.new_hash (params={})
215
+ self.new(:hash, params)
216
+ end
240
217
 
241
- #
242
- # (The actual #[] method is provided by HashMethods
243
- #
244
- def get (k)
245
- lib.abs_get2(@db, k) rescue nil
246
- end
247
- protected :get
248
-
249
- #
250
- # Removes a record from the cabinet, returns the value if successful
251
- # else nil.
252
- #
253
- def delete (k)
254
- v = self[k]
255
- (lib.abs_out2(@db, k) == 1) ? v : nil
256
- end
218
+ #
219
+ # Returns a new in-memory B+ tree. Accepts the same optional params hash
220
+ # as new().
221
+ #
222
+ def self.new_tree (params={})
223
+ self.new(:tree, params)
224
+ end
257
225
 
258
- #
259
- # Returns the number of records in the 'cabinet'
260
- #
261
- def size
262
- lib.abs_rnum(@db)
263
- end
226
+ #
227
+ # using the cabinet lib
228
+ #
229
+ def lib
230
+ CabinetLib
231
+ end
264
232
 
265
- #
266
- # Removes all the records in the cabinet (use with care)
267
- #
268
- # Returns self (like Ruby's Hash does).
269
- #
270
- def clear
271
- lib.abs_vanish(@db)
272
- self
273
- end
233
+ #
234
+ # No comment
235
+ #
236
+ def []= (k, v)
237
+ lib.abs_put2(@db, k, v)
238
+ end
274
239
 
275
- #
276
- # Returns the 'weight' of the db (in bytes)
277
- #
278
- def weight
279
- lib.abs_size(@db)
280
- end
240
+ #
241
+ # (The actual #[] method is provided by HashMethods
242
+ #
243
+ def get (k)
244
+ lib.abs_get2(@db, k) rescue nil
245
+ end
246
+ protected :get
281
247
 
282
- #
283
- # Closes the cabinet (and frees the datastructure allocated for it),
284
- # returns true in case of success.
285
- #
286
- def close
287
- result = lib.abs_close(@db)
288
- lib.abs_del(@db)
289
- (result == 1)
290
- end
248
+ #
249
+ # Removes a record from the cabinet, returns the value if successful
250
+ # else nil.
251
+ #
252
+ def delete (k)
253
+ v = self[k]
254
+ (lib.abs_out2(@db, k) == 1) ? v : nil
255
+ end
291
256
 
292
- #
293
- # Copies the current cabinet to a new file.
294
- #
295
- # Returns true if it was successful.
296
- #
297
- def copy (target_path)
298
- (lib.abs_copy(@db, target_path) == 1)
299
- end
257
+ #
258
+ # Returns the number of records in the 'cabinet'
259
+ #
260
+ def size
261
+ lib.abs_rnum(@db)
262
+ end
300
263
 
301
- #
302
- # Copies the current cabinet to a new file.
303
- #
304
- # Does it by copying each entry afresh to the target file. Spares some
305
- # space, hence the 'compact' label...
306
- #
307
- def compact_copy (target_path)
308
- @other_db = Cabinet.new(target_path)
309
- self.each { |k, v| @other_db[k] = v }
310
- @other_db.close
311
- end
264
+ #
265
+ # Removes all the records in the cabinet (use with care)
266
+ #
267
+ # Returns self (like Ruby's Hash does).
268
+ #
269
+ def clear
270
+ lib.abs_vanish(@db)
271
+ self
272
+ end
312
273
 
313
- #
314
- # "synchronize updated contents of an abstract database object with
315
- # the file and the device"
316
- #
317
- def sync
318
- (lib.abs_sync(@db) == 1)
319
- end
274
+ #
275
+ # Returns the 'weight' of the db (in bytes)
276
+ #
277
+ def weight
278
+ lib.abs_size(@db)
279
+ end
280
+
281
+ #
282
+ # Closes the cabinet (and frees the datastructure allocated for it),
283
+ # returns true in case of success.
284
+ #
285
+ def close
286
+ result = lib.abs_close(@db)
287
+ lib.abs_del(@db)
288
+ (result == 1)
289
+ end
290
+
291
+ #
292
+ # Copies the current cabinet to a new file.
293
+ #
294
+ # Returns true if it was successful.
295
+ #
296
+ def copy (target_path)
297
+ (lib.abs_copy(@db, target_path) == 1)
298
+ end
320
299
 
321
- #
322
- # Returns an array with all the keys in the databse
323
- #
324
- # With no options given, this method will return all the keys (strings)
325
- # in a Ruby array.
326
- #
327
- # :prefix --> returns only the keys who match a given string prefix
328
- #
329
- # :limit --> returns a limited number of keys
330
- #
331
- # :native --> returns an instance of Rufus::Tokyo::List instead of
332
- # a Ruby Hash, you have to call #free on that List when done with it !
333
- # Else you're exposing yourself to a memory leak.
334
- #
335
- def keys (options={})
300
+ #
301
+ # Copies the current cabinet to a new file.
302
+ #
303
+ # Does it by copying each entry afresh to the target file. Spares some
304
+ # space, hence the 'compact' label...
305
+ #
306
+ def compact_copy (target_path)
307
+ @other_db = Cabinet.new(target_path)
308
+ self.each { |k, v| @other_db[k] = v }
309
+ @other_db.close
310
+ end
336
311
 
337
- if pref = options[:prefix]
312
+ #
313
+ # "synchronize updated contents of an abstract database object with
314
+ # the file and the device"
315
+ #
316
+ def sync
317
+ (lib.abs_sync(@db) == 1)
318
+ end
319
+
320
+ #
321
+ # Returns an array with all the keys in the databse
322
+ #
323
+ # With no options given, this method will return all the keys (strings)
324
+ # in a Ruby array.
325
+ #
326
+ # :prefix --> returns only the keys who match a given string prefix
327
+ #
328
+ # :limit --> returns a limited number of keys
329
+ #
330
+ # :native --> returns an instance of Rufus::Tokyo::List instead of
331
+ # a Ruby Hash, you have to call #free on that List when done with it !
332
+ # Else you're exposing yourself to a memory leak.
333
+ #
334
+ def keys (options={})
338
335
 
339
- l = lib.abs_fwmkeys2(@db, pref, options[:limit] || -1)
340
- l = Rufus::Tokyo::List.new(l)
341
- options[:native] ? l : l.release
336
+ if pref = options[:prefix]
342
337
 
343
- else
338
+ l = lib.abs_fwmkeys2(@db, pref, options[:limit] || -1)
339
+ l = Rufus::Tokyo::List.new(l)
340
+ options[:native] ? l : l.release
344
341
 
345
- limit = options[:limit] || -1
346
- limit = nil if limit < 1
342
+ else
347
343
 
348
- l = options[:native] ? Rufus::Tokyo::List.new : []
344
+ limit = options[:limit] || -1
345
+ limit = nil if limit < 1
349
346
 
350
- lib.abs_iterinit(@db)
347
+ l = options[:native] ? Rufus::Tokyo::List.new : []
351
348
 
352
- while (k = (lib.abs_iternext2(@db) rescue nil))
353
- break if limit and l.size >= limit
354
- l << k
355
- end
349
+ lib.abs_iterinit(@db)
356
350
 
357
- l
351
+ while (k = (lib.abs_iternext2(@db) rescue nil))
352
+ break if limit and l.size >= limit
353
+ l << k
358
354
  end
355
+
356
+ l
359
357
  end
360
358
  end
361
359
 
360
+ #
361
+ # Deletes all the entries whose keys begin with the given prefix
362
+ #
363
+ def delete_keys_with_prefix (prefix)
364
+
365
+ call_misc('outlist', lib.abs_fwmkeys2(@db, prefix, -1))
366
+ # -1 for no limits
367
+ nil
368
+ end
369
+
370
+ #
371
+ # Given a list of keys, returns a Hash { key => value } of the
372
+ # matching entries (in one sweep).
373
+ #
374
+ def lget (keys)
375
+
376
+ Hash[*call_misc('getlist', Rufus::Tokyo::List.new(keys))]
377
+ end
378
+
379
+ #
380
+ # Merges the given hash into this Cabinet (or Tyrant) and returns self.
381
+ #
382
+ def merge! (hash)
383
+
384
+ call_misc(
385
+ 'putlist',
386
+ hash.inject(Rufus::Tokyo::List.new) { |l, (k, v)| l << k; l << v; l })
387
+ self
388
+ end
389
+ alias :lput :merge!
390
+
391
+ #
392
+ # Given a list of keys, deletes all the matching entries (in one sweep).
393
+ #
394
+ def ldelete (keys)
395
+
396
+ call_misc('outlist', Rufus::Tokyo::List.new(keys))
397
+ end
398
+
399
+ protected
400
+
401
+ #
402
+ # wrapping tcadbmisc or tcrdbmisc
403
+ # (and taking care of freeing the list_pointer)
404
+ #
405
+ def call_misc (function, list_pointer)
406
+
407
+ list_pointer = list_pointer.pointer \
408
+ if list_pointer.is_a?(Rufus::Tokyo::List)
409
+
410
+ begin
411
+ l = do_call_misc(function, list_pointer)
412
+ raise "function '#{function}' failed" unless l
413
+ Rufus::Tokyo::List.new(l).release
414
+ ensure
415
+ Rufus::Tokyo::List.free(list_pointer)
416
+ end
417
+ end
418
+
419
+ def do_call_misc (function, list_pointer)
420
+
421
+ lib.tcadbmisc(@db, function, list_pointer)
422
+ end
362
423
  end
363
424
  end
425
+