rufus-tokyo 1.0.1 → 1.0.2

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.
@@ -49,6 +49,7 @@ module Rufus::Edo
49
49
 
50
50
  include Rufus::Edo::TableCore
51
51
  include Rufus::Tokyo::TyrantCommons
52
+ include Rufus::Tokyo::NoTransactions
52
53
 
53
54
  attr_reader :host, :port
54
55
 
@@ -87,6 +88,8 @@ module Rufus::Edo
87
88
  @db = TokyoTyrant::RDBTBL.new
88
89
  @db.open(host, port) || raise_error
89
90
 
91
+ @default_proc = nil
92
+
90
93
  if self.stat['type'] != 'table'
91
94
 
92
95
  @db.close
@@ -99,9 +102,9 @@ module Rufus::Edo
99
102
 
100
103
  # Gets multiple records in one sweep.
101
104
  #
102
- def lget (keys)
105
+ def lget (*keys)
103
106
 
104
- h = keys.inject({}) { |h, k| h[k] = nil; h }
107
+ h = keys.flatten.inject({}) { |hh, k| hh[k] = nil; hh }
105
108
  r = @db.mget(h)
106
109
 
107
110
  raise 'lget failure' if r == -1
@@ -111,22 +114,6 @@ module Rufus::Edo
111
114
 
112
115
  alias :mget :lget
113
116
 
114
- def transaction #:nodoc#
115
- raise NoMethodError.new("NetTyrant : transactions not supported")
116
- end
117
- def abort #:nodoc#
118
- raise NoMethodError.new("NetTyrant : transactions not supported")
119
- end
120
- def tranbegin #:nodoc#
121
- raise NoMethodError.new("NetTyrant : transactions not supported")
122
- end
123
- def trancommit #:nodoc#
124
- raise NoMethodError.new("NetTyrant : transactions not supported")
125
- end
126
- def tranabort #:nodoc#
127
- raise NoMethodError.new("NetTyrant : transactions not supported")
128
- end
129
-
130
117
  # Calls a lua embedded function
131
118
  # (http://tokyocabinet.sourceforge.net/tyrantdoc/#luaext)
132
119
  #
@@ -26,12 +26,13 @@
26
26
  require 'rufus/tokyo/utils'
27
27
  require 'rufus/tokyo/query'
28
28
  require 'rufus/tokyo/transactions'
29
+ require 'rufus/tokyo/openable'
29
30
 
30
31
 
31
32
  module Rufus::Edo
32
33
 
33
34
  #
34
- # Methods common to the two table classes (cabinet + ntyrant) found in
35
+ # Methods common to the two table classes (table + ntyrant) found in
35
36
  # Rufus::Edo
36
37
  #
37
38
  module TableCore
@@ -39,6 +40,11 @@ module Rufus::Edo
39
40
  include Rufus::Tokyo::HashMethods
40
41
  include Rufus::Tokyo::Transactions
41
42
 
43
+ # Add the open() method to all Table type classes.
44
+ def self.included(target)
45
+ target.extend(Rufus::Tokyo::Openable)
46
+ end
47
+
42
48
  # Closes the table (and frees the datastructure allocated for it),
43
49
  # raises an exception in case of failure.
44
50
  #
@@ -88,7 +94,7 @@ module Rufus::Edo
88
94
 
89
95
  column_name = column_name == :pk ? '' : column_name.to_s
90
96
 
91
- i = types.inject(0) { |i, t| i = i | INDEX_TYPES[t]; i }
97
+ i = types.inject(0) { |ii, t| ii | INDEX_TYPES[t] }
92
98
 
93
99
  @db.setindex(column_name, i) || raise_error
94
100
  end
@@ -154,46 +160,24 @@ module Rufus::Edo
154
160
  #
155
161
  def keys (options={})
156
162
 
157
- if pref = options[:prefix]
158
-
159
- @db.fwmkeys(pref, options[:limit] || -1)
160
-
161
- else
162
-
163
- limit = options[:limit] || -1
164
- limit = nil if limit < 1
163
+ pref = options.fetch(:prefix, "")
165
164
 
166
- @db.iterinit
167
-
168
- l = []
169
-
170
- while (k = @db.iternext)
171
- break if limit and l.size >= limit
172
- l << k
173
- end
174
-
175
- l
176
- end
165
+ @db.fwmkeys(pref, options[:limit] || -1)
177
166
  end
178
167
 
179
168
  # Deletes all the entries whose key begin with the given prefix.
180
169
  #
181
170
  def delete_keys_with_prefix (prefix)
182
171
 
183
- if @db.respond_to?(:misc)
184
- @db.misc('outlist', @db.fwmkeys(prefix, -1))
185
- else
186
- ks = @db.fwmkeys(prefix, -1) # -1 for no limit
187
- ks.each { |k| self.delete(k) }
188
- end
172
+ query_delete { |q| q.add('', :strbw, prefix) }
189
173
  end
190
174
 
191
175
  # Returns a hash { key => record } of all the records matching the
192
176
  # given keys.
193
177
  #
194
- def lget (keys)
178
+ def lget (*keys)
195
179
 
196
- keys = Rufus::Tokyo::h_or_a_to_s(keys)
180
+ keys = Rufus::Tokyo::h_or_a_to_s(keys.flatten)
197
181
 
198
182
  if @db.respond_to?(:mget)
199
183
  @db.mget(keys)
@@ -236,6 +220,15 @@ module Rufus::Edo
236
220
  prepare_query(&block).delete
237
221
  end
238
222
 
223
+ # Prepares, runs AND counts all the matching records.
224
+ #
225
+ def query_count (&block)
226
+
227
+ prepare_query { |q|
228
+ q.pk_only # improve efficiency, since we have to do the query
229
+ }.count
230
+ end
231
+
239
232
  # Warning : this method is low-level, you probably only need
240
233
  # to use #transaction and a block.
241
234
  #
@@ -443,10 +436,10 @@ module Rufus::Edo
443
436
  #
444
437
  def initialize (query_class, table)
445
438
 
446
- @table = table
447
- @query = query_class.new(table.original)
448
-
449
- @opts = {}
439
+ @table = table
440
+ @query = query_class.new(table.original)
441
+ @opts = {}
442
+ @has_run = false
450
443
  end
451
444
 
452
445
  # Returns the original, underlying RDBQUERY instance.
@@ -624,7 +617,7 @@ module Rufus::Edo
624
617
  # Runs this query (returns a TableResultSet instance)
625
618
  #
626
619
  def run
627
-
620
+ @has_run = true
628
621
  @last_resultset = TableResultSet.new(@table, @query.search, @opts)
629
622
  end
630
623
 
@@ -642,7 +635,7 @@ module Rufus::Edo
642
635
 
643
636
  #@query.count
644
637
  # not yet implemented by Hirabayashi-san
645
-
638
+ run.free unless @has_run
646
639
  @last_resultset ? @last_resultset.size : 0
647
640
  end
648
641
 
data/lib/rufus/tokyo.rb CHANGED
@@ -29,7 +29,7 @@ require 'ffi' # sudo gem install ffi
29
29
  module Rufus
30
30
  module Tokyo
31
31
 
32
- VERSION = '1.0.1'
32
+ VERSION = '1.0.2'
33
33
 
34
34
  #
35
35
  # A common error class
@@ -25,6 +25,8 @@
25
25
 
26
26
  require 'rufus/tokyo/transactions'
27
27
  require 'rufus/tokyo/outlen'
28
+ #require 'rufus/tokyo/config'
29
+ require 'rufus/tokyo/openable'
28
30
 
29
31
 
30
32
  module Rufus::Tokyo
@@ -54,6 +56,8 @@ module Rufus::Tokyo
54
56
  include HashMethods
55
57
  include Transactions
56
58
  include Outlen
59
+ #include CabinetConfig
60
+ extend Openable
57
61
 
58
62
  # Creates/opens the cabinet, raises an exception in case of
59
63
  # creation/opening failure.
@@ -124,11 +128,29 @@ module Rufus::Tokyo
124
128
  # db = Rufus::Tokyo::Cabinet.new(
125
129
  # 'data', :type => :hash, :opts => 'ld', :mode => 'w')
126
130
  #
127
- # === mode
131
+ # === :mode
128
132
  #
129
133
  # * :mode a set of chars ('r'ead, 'w'rite, 'c'reate, 't'runcate,
130
134
  # 'e' non locking, 'f' non blocking lock), default is 'wc'
131
135
  #
136
+ # === :default and :default_proc
137
+ #
138
+ # Much like a Ruby Hash, a Cabinet accepts a default value or a default_proc
139
+ #
140
+ # db = Rufus::Tokyo::Cabinet.new('data.tch', :default => 'xxx')
141
+ # db['fred'] = 'Astaire'
142
+ # p db['fred'] # => 'Astaire'
143
+ # p db['ginger'] # => 'xxx'
144
+ #
145
+ # db = Rufus::Tokyo::Cabinet.new(
146
+ # 'data.tch',
147
+ # :default_proc => lambda { |cab, key| "not found : '#{k}'" }
148
+ # p db['ginger'] # => "not found : 'ginger'"
149
+ #
150
+ # The first arg passed to the default_proc is the cabinet itself, so this
151
+ # opens up interesting possibilities.
152
+ #
153
+ #
132
154
  # === other parameters
133
155
  #
134
156
  # 'On-memory hash database supports "bnum", "capnum", and "capsiz".
@@ -178,6 +200,9 @@ module Rufus::Tokyo
178
200
  #
179
201
  def initialize (name, params={})
180
202
 
203
+ #conf = determine_conf(path, params)
204
+ # not using it
205
+
181
206
  @db = lib.tcadbnew
182
207
 
183
208
  name = '*' if name == :mem_hash # in memory hash database
@@ -188,35 +213,20 @@ module Rufus::Tokyo
188
213
  end
189
214
 
190
215
  @path = name
216
+ @type = File.extname(@path)[1..-1]
191
217
 
192
218
  name = name + params.collect { |k, v| "##{k}=#{v}" }.join('')
193
219
 
194
- (lib.tcadbopen(@db, name) == 1) || raise(
220
+ (lib.tcadbopen(@db, name) != 0) || raise(
195
221
  TokyoError.new("failed to open/create db '#{name}' #{params.inspect}"))
196
222
 
223
+ #
224
+ # default value|proc
225
+
197
226
  self.default = params[:default]
198
227
  @default_proc ||= params[:default_proc]
199
228
  end
200
229
 
201
- # Same args as initialize, but can take a block form that will
202
- # close the db when done. Similar to File.open
203
- #
204
- def self.open (name, params={})
205
-
206
- db = self.new(name, params)
207
-
208
- if block_given?
209
- yield db
210
- nil
211
- else
212
- db
213
- end
214
-
215
- ensure
216
-
217
- db.close if block_given? && db
218
- end
219
-
220
230
  # Returns a new in-memory hash. Accepts the same optional params hash
221
231
  # as new().
222
232
  #
@@ -382,41 +392,20 @@ module Rufus::Tokyo
382
392
  #
383
393
  def keys (options={})
384
394
 
385
- outlen = nil
386
-
387
- if pre = options[:prefix]
395
+ if @type == "tcf"
396
+ min, max = "min", "max"
397
+ l = lib.tcfdbrange2( as_fixed, min, Rufus::Tokyo.blen(min),
398
+ max, Rufus::Tokyo.blen(max), -1)
399
+ else
400
+ pre = options.fetch(:prefix, "")
388
401
 
389
402
  l = lib.abs_fwmkeys(
390
403
  @db, pre, Rufus::Tokyo.blen(pre), options[:limit] || -1)
391
-
392
- l = Rufus::Tokyo::List.new(l)
393
-
394
- options[:native] ? l : l.release
395
-
396
- else
397
-
398
- limit = options[:limit] || -1
399
- limit = nil if limit < 1
400
-
401
- l = options[:native] ? Rufus::Tokyo::List.new : []
402
-
403
- lib.abs_iterinit(@db)
404
-
405
- outlen = FFI::MemoryPointer.new(:int)
406
-
407
- loop do
408
- break if limit and l.size >= limit
409
- out = lib.abs_iternext(@db, outlen)
410
- break if out.address == 0
411
- l << out.get_bytes(0, outlen.get_int(0))
412
- end
413
-
414
- l
415
404
  end
416
405
 
417
- ensure
406
+ l = Rufus::Tokyo::List.new(l)
418
407
 
419
- outlen.free if outlen
408
+ options[:native] ? l : l.release
420
409
  end
421
410
 
422
411
  # Deletes all the entries whose keys begin with the given prefix
@@ -433,9 +422,9 @@ module Rufus::Tokyo
433
422
  # Given a list of keys, returns a Hash { key => value } of the
434
423
  # matching entries (in one sweep).
435
424
  #
436
- def lget (keys)
425
+ def lget (*keys)
437
426
 
438
- keys = keys.collect { |k| k.to_s }
427
+ keys = keys.flatten.collect { |k| k.to_s }
439
428
 
440
429
  Hash[*call_misc('getlist', Rufus::Tokyo::List.new(keys))]
441
430
  end
@@ -460,11 +449,11 @@ module Rufus::Tokyo
460
449
 
461
450
  # Given a list of keys, deletes all the matching entries (in one sweep).
462
451
  #
463
- def ldelete (keys)
452
+ def ldelete (*keys)
464
453
 
465
- keys = keys.collect { |k| k.to_s }
466
-
467
- call_misc('outlist', Rufus::Tokyo::List.new(keys))
454
+ call_misc(
455
+ 'outlist',
456
+ Rufus::Tokyo::List.new(keys.flatten.collect { |k| k.to_s }))
468
457
  end
469
458
 
470
459
  # Increments the value stored under the given key with the given increment
@@ -472,6 +461,11 @@ module Rufus::Tokyo
472
461
  #
473
462
  # Accepts an integer or a double value.
474
463
  #
464
+ # Warning : Tokyo Cabinet/Tyrant doesn't store counter values as regular
465
+ # strings (db['key'] won't yield something that replies properly to #to_i)
466
+ #
467
+ # Use #counter_value(k) to get the current value set for the counter.
468
+ #
475
469
  def incr (key, inc=1)
476
470
 
477
471
  key = key.to_s
@@ -482,7 +476,7 @@ module Rufus::Tokyo
482
476
 
483
477
  raise(TokyoError.new(
484
478
  "incr failed, there is probably already a string value set " +
485
- "for the key '#{key}'"
479
+ "for the key '#{key}'. Make sure there is no value before incrementing"
486
480
  )) if v == Rufus::Tokyo::INT_MIN || (v.respond_to?(:nan?) && v.nan?)
487
481
 
488
482
  v
@@ -492,6 +486,15 @@ module Rufus::Tokyo
492
486
  alias :add_int :incr
493
487
  alias :add_double :incr
494
488
 
489
+ # Returns the current value for a counter (a float or an int).
490
+ #
491
+ # See #incr
492
+ #
493
+ def counter_value (key)
494
+
495
+ incr(key, 0.0) rescue incr(key, 0)
496
+ end
497
+
495
498
  # Triggers a defrag run (TC >= 1.4.21 only)
496
499
  #
497
500
  def defrag
@@ -581,6 +584,27 @@ module Rufus::Tokyo
581
584
  lib.tcadbreveal(@db)
582
585
  end
583
586
 
587
+ # Returns the pointer to the fixed-width database hiding behind the
588
+ # abstract structure.
589
+ #
590
+ # Will raise an argument error if the structure behind the abstract db
591
+ # is not a fixed-width structure.
592
+ #
593
+ def as_fixed
594
+
595
+ raise(NoMethodError.new("cannot call Fixed-width function on #{@path}")) \
596
+ if ! @path.match(/\.tcf$/)
597
+
598
+ lib.tcadbreveal(@db)
599
+ end
600
+
601
+ # Advanced function. Initially added to uncover tc{b|f|h}dbsetmutex().
602
+ #
603
+ def call_non_abstract_function (funcname, *args)
604
+
605
+ lib.send(lib.tcadbreveal(@db), "#{@type}#{funcname}", *args)
606
+ end
607
+
584
608
  #--
585
609
  #def check_transaction_support
586
610
  # raise(TokyoError.new(
@@ -616,8 +640,8 @@ module Rufus::Tokyo
616
640
 
617
641
  def libcall (lib_method, *args)
618
642
 
619
- (eval(%{ lib.#{lib_method}(@db, *args) }) == 1) or \
620
- raise TokyoError.new("call to #{lib_method} failed")
643
+ raise TokyoError.new("call to #{lib_method} failed") \
644
+ unless lib.send(lib_method, @db, *args) == 1
621
645
  end
622
646
 
623
647
  end
@@ -198,6 +198,13 @@ module Rufus::Tokyo
198
198
  attfunc :tcbdbputdup, [ :pointer, :pointer, :int, :pointer, :int ], :int
199
199
  attfunc :tcbdbget4, [ :pointer, :pointer, :int ], :pointer
200
200
 
201
+ #
202
+ # tcfdb functions
203
+ #
204
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tcfdbapi
205
+
206
+ attfunc :tcfdbrange2, [ :pointer, :pointer, :int, :pointer, :int, :int ], :pointer
207
+
201
208
  #
202
209
  # tcmap functions
203
210
  #