about summary refs log tree commit
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-12-06 15:43:46 -0800
committerEric Wong <normalperson@yhbt.net>2010-12-06 15:43:46 -0800
commit88fa90b7f039f62962cc8d11031446412b951be2 (patch)
tree04c2d961f6e3e41b069f074d7b93e1d91d8d716e
parent74ca34be2d54809822447ff185d88d83fcd566ae (diff)
downloadmetropolis-88fa90b7f039f62962cc8d11031446412b951be2.tar.gz
allow easier, single-file options for TC and TDB
Most (other) users only need a single file, even though
my primary use of this is for multiple files.
-rw-r--r--lib/metropolis.rb30
-rw-r--r--lib/metropolis/common.rb15
-rw-r--r--lib/metropolis/hash.rb2
-rw-r--r--lib/metropolis/tc.rb11
-rw-r--r--lib/metropolis/tc/hdb.rb19
-rw-r--r--lib/metropolis/tdb.rb29
-rw-r--r--lib/metropolis/tdb/multi.rb19
-rw-r--r--lib/metropolis/tdb/single.rb16
-rw-r--r--test/test_tc_hdb.rb13
-rw-r--r--test/test_tc_hdb_single.rb22
-rw-r--r--test/test_tdb_single.rb21
11 files changed, 133 insertions, 64 deletions
diff --git a/lib/metropolis.rb b/lib/metropolis.rb
index 425ce1c..c38d4af 100644
--- a/lib/metropolis.rb
+++ b/lib/metropolis.rb
@@ -13,28 +13,22 @@ module Metropolis
   def self.new(opts = {})
     opts = opts.dup
     rv = Object.new
-    uri = opts[:uri] = URI.parse(opts[:uri])
-    if uri.path != '/' && opts[:path_pattern]
-      raise ArgumentError, ":path_pattern may only be used if path is '/'"
+    uri = URI.parse(opts[:uri])
+    rv.instance_eval do
+      @uri = uri
+      @query = @uri.query ? Rack::Utils.parse_query(@uri.query) : nil
+      @path_pattern = opts[:path_pattern]
+      @path = @uri.path if @uri.path != '/'
     end
-    case uri.scheme
-    when 'hash'
-      opts[:path] = uri.path if uri.path != '/'
-      rv.extend Metropolis::Hash
-    when 'tdb'
-      opts[:query] = Rack::Utils.parse_query(uri.query) if uri.query
-      rv.extend Metropolis::TDB
-    when 'tc'
-      opts[:query] = Rack::Utils.parse_query(uri.query) if uri.query
-      case ext = File.extname(opts[:path_pattern] || uri.path)
-      when '.tch'
-        rv.extend Metropolis::TC::HDB
-      else
-        raise ArgumentError, "unsupported suffix: #{ext}"
-      end
+
+    base = case uri.scheme
+    when 'hash' then Metropolis::Hash
+    when 'tdb' then Metropolis::TDB
+    when 'tc' then Metropolis::TC
     else
       raise ArgumentError, "unsupported URI scheme: #{uri.scheme}"
     end
+    rv.extend(base)
     rv.setup(opts)
     rv
   end
diff --git a/lib/metropolis/common.rb b/lib/metropolis/common.rb
index 971accd..d9cadba 100644
--- a/lib/metropolis/common.rb
+++ b/lib/metropolis/common.rb
@@ -4,10 +4,21 @@ module Metropolis::Common
   autoload :RO, 'metropolis/common/ro'
 
   def setup(opts)
-    @uri = opts[:uri]
     @headers = { 'Content-Type' => 'application/octet-stream' }
     @headers.merge!(opts[:response_headers] || {})
-    @nr_slots = opts[:nr_slots] || 3
+    @nr_slots = opts[:nr_slots]
+
+    if @path_pattern
+      @nr_slots ||= 3
+      @uri.path == '/' or
+        raise ArgumentError, ":path_pattern may only be used if path is '/'"
+      @path_pattern.scan(/%\d*x/).size == 1 or
+        raise ArgumentError, "only one '/%\d*x/' may appear in #@path_pattern"
+    else
+      @nr_slots and
+        raise ArgumentError, ":nr_slots may be used with :path_pattern"
+    end
+
     @readonly = !!opts[:readonly]
     @exclusive = !!opts[:exclusive]
     if @readonly && @exclusive
diff --git a/lib/metropolis/hash.rb b/lib/metropolis/hash.rb
index fb4d272..d5c70cb 100644
--- a/lib/metropolis/hash.rb
+++ b/lib/metropolis/hash.rb
@@ -8,7 +8,7 @@ module Metropolis::Hash
 
   def setup(opts)
     super
-    if @path = opts[:path]
+    if @path
       begin
         @db = Marshal.load(File.open(@path, "rb") { |fp| fp.read })
         Hash === @db or raise ArgumentError, "#@path is not a marshaled Hash"
diff --git a/lib/metropolis/tc.rb b/lib/metropolis/tc.rb
index 84dfaff..1f15e7b 100644
--- a/lib/metropolis/tc.rb
+++ b/lib/metropolis/tc.rb
@@ -3,4 +3,15 @@ require 'tokyocabinet'
 
 module Metropolis::TC
   autoload :HDB, 'metropolis/tc/hdb'
+
+  def self.extended(obj)
+    obj.instance_eval do
+      case ext = File.extname(@path_pattern || @path)
+      when '.tch'
+        extend Metropolis::TC::HDB
+      else
+        raise ArgumentError, "unsupported suffix: #{ext}"
+      end
+    end
+  end
 end
diff --git a/lib/metropolis/tc/hdb.rb b/lib/metropolis/tc/hdb.rb
index 97e9050..d0833f8 100644
--- a/lib/metropolis/tc/hdb.rb
+++ b/lib/metropolis/tc/hdb.rb
@@ -11,16 +11,12 @@ module Metropolis::TC::HDB
 
   def setup(opts)
     super
-    path_pattern = opts[:path_pattern]
-    path_pattern.scan(/%\d*x/).size == 1 or
-      raise ArgumentError, "only one '/%\d*x/' may appear in #{path_pattern}"
-
     @rd_flags = TCHDB::OREADER
     @wr_flags = TCHDB::OWRITER
 
     @optimize = nil
-    if query = opts[:query]
-      case query['rdlock']
+    if @query
+      case @query['rdlock']
       when 'true', nil
       when 'false'
         @rd_flags |= TCHDB::ONOLCK
@@ -28,7 +24,7 @@ module Metropolis::TC::HDB
         raise ArgumentError, "'rdlock' must be 'true' or 'false'"
       end
 
-      case query['wrlock']
+      case @query['wrlock']
       when 'true', nil
       when 'false'
         @wr_flags |= TCHDB::ONOLCK
@@ -38,11 +34,11 @@ module Metropolis::TC::HDB
 
       flags = 0
       @optimize = %w(bnum apow fpow).map do |x|
-        v = query[x]
+        v = @query[x]
         v ? v.to_i : nil
       end
 
-      case large = query['large']
+      case large = @query['large']
       when 'false', nil
       when 'true'
         flags |= TCHDB::TLARGE
@@ -50,7 +46,7 @@ module Metropolis::TC::HDB
         raise ArgumentError, "invalid 'large' value: #{large}"
       end
 
-      case compress = query['compress']
+      case compress = @query['compress']
       when nil
       when 'deflate', 'bzip', 'tcbs'
         flags |= TCHDB.const_get("T#{compress.upcase}")
@@ -59,8 +55,9 @@ module Metropolis::TC::HDB
       end
       @optimize << flags
     end
+    @nr_slots = 1 unless @path_pattern
     @dbv = (0...@nr_slots).to_a.map do |slot|
-      path = sprintf(path_pattern, slot)
+      path = @path_pattern ? sprintf(@path_pattern, slot) : @uri.path
       hdb = TCHDB.new
       unless @readonly
         hdb.open(path, TCHDB::OWRITER | TCHDB::OCREAT) or ex!(:open, hdb)
diff --git a/lib/metropolis/tdb.rb b/lib/metropolis/tdb.rb
index 14aa3aa..d734443 100644
--- a/lib/metropolis/tdb.rb
+++ b/lib/metropolis/tdb.rb
@@ -4,44 +4,35 @@ require 'tdb'
 
 module Metropolis::TDB
   include Metropolis::Common
+  autoload :Single, 'metropolis/tdb/single'
+  autoload :Multi, 'metropolis/tdb/multi'
 
   def setup(opts)
     super
-    path_pattern = opts[:path_pattern]
-    path_pattern.scan(/%\d*x/).size == 1 or
-      raise ArgumentError, "only one '/%\d*x/' may appear in #{path_pattern}"
     @tdb_opts = { :tdb_flags => 0 }
     if @readonly
       @tdb_opts[:open_flags] = IO::RDONLY
       extend Metropolis::Common::RO
     end
-    if query = opts[:query]
-      size = query['hash_size'] and @tdb_opts[:hash_size] = size.to_i
-      hash = query['hash'] and @tdb_opts[:hash] = hash.to_sym
+    if @query
+      size = @query['hash_size'] and @tdb_opts[:hash_size] = size.to_i
+      hash = @query['hash'] and @tdb_opts[:hash] = hash.to_sym
 
-      case query['volatile']
+      case @query['volatile']
       when 'true'; @tdb_opts[:tdb_flags] |= TDB::VOLATILE
       when 'false', nil
       else
         raise ArgumentError, "'volatile' must be 'true' or 'false'"
       end
 
-      case query['sync']
+      case @query['sync']
       when 'true', nil
       when 'false'; @tdb_opts[:tdb_flags] |= TDB::NOSYNC
       else
         raise ArgumentError, "'sync' must be 'true' or 'false'"
       end
     end
-
-    @dbv = (0...@nr_slots).to_a.map do |slot|
-      path = sprintf(path_pattern, slot)
-      ::TDB.new(path, @tdb_opts)
-    end
-  end
-
-  def db(key, &block)
-    yield @dbv[key.hash % @nr_slots]
+    extend(@path_pattern ? Metropolis::TDB::Multi : Metropolis::TDB::Single)
   end
 
   def put(key, env)
@@ -67,8 +58,4 @@ module Metropolis::TDB
     value = db(key) { |tdb| tdb.fetch(key) } or return r(404)
     [ 200, { 'Content-Length' => value.size.to_s }.merge!(@headers), [ value ] ]
   end
-
-  def close!
-    @dbv.each { |tdb| tdb.close }
-  end
 end
diff --git a/lib/metropolis/tdb/multi.rb b/lib/metropolis/tdb/multi.rb
new file mode 100644
index 0000000..b22e63b
--- /dev/null
+++ b/lib/metropolis/tdb/multi.rb
@@ -0,0 +1,19 @@
+# -*- encoding: binary -*-
+module Metropolis::TDB::Multi
+  def self.extended(obj)
+    obj.instance_eval do
+      @dbv = (0...@nr_slots).to_a.map do |slot|
+        path = sprintf(@path_pattern, slot)
+        ::TDB.new(path, @tdb_opts)
+      end
+    end
+  end
+
+  def db(key, &block)
+    yield @dbv[key.hash % @nr_slots]
+  end
+
+  def close!
+    @dbv.each { |tdb| tdb.close }
+  end
+end
diff --git a/lib/metropolis/tdb/single.rb b/lib/metropolis/tdb/single.rb
new file mode 100644
index 0000000..840b57a
--- /dev/null
+++ b/lib/metropolis/tdb/single.rb
@@ -0,0 +1,16 @@
+# -*- encoding: binary -*-
+module Metropolis::TDB::Single
+  def self.extended(obj)
+    obj.instance_eval do
+      @db = ::TDB.new(@uri.path, @tdb_opts)
+    end
+  end
+
+  def db(key, &block)
+    yield @db
+  end
+
+  def close!
+    @db.close
+  end
+end
diff --git a/test/test_tc_hdb.rb b/test/test_tc_hdb.rb
index a5d27b8..690fb82 100644
--- a/test/test_tc_hdb.rb
+++ b/test/test_tc_hdb.rb
@@ -21,12 +21,7 @@ class Test_TC_HDB < Test::Unit::TestCase
   end
 
   def osetup
-    o = Object.new
-    o.extend Metropolis::TC::HDB
-    assert_nothing_raised do
-      o.setup :path_pattern => @path_pattern
-    end
-    o
+    Metropolis.new(@app_opts)
   end
 
   def test_create_put_get_delete
@@ -135,11 +130,7 @@ class Test_TC_HDB < Test::Unit::TestCase
     key = "x"
     wr = osetup
     wr.put(key, { "rack.input" => StringIO.new("OK") })
-    o = Object.new
-    o.extend Metropolis::TC::HDB
-    assert_nothing_raised do
-      o.setup :path_pattern => @path_pattern, :readonly => true
-    end
+    o = Metropolis.new(@app_opts.merge(:readonly => true))
     %w(PUT DELETE).each do |rm|
       env = {
         "rack.input" => StringIO.new("FAIL"),
diff --git a/test/test_tc_hdb_single.rb b/test/test_tc_hdb_single.rb
new file mode 100644
index 0000000..c8e33a9
--- /dev/null
+++ b/test/test_tc_hdb_single.rb
@@ -0,0 +1,22 @@
+# -*- encoding: binary -*-
+require './test/rack_read_write.rb'
+require 'tokyocabinet' # FIXME: emits warning with 1.29 gem
+$-w = true
+require 'metropolis'
+
+class Test_TC_HDB_Single < Test::Unit::TestCase
+  attr_reader :tmp, :o, :uri
+  include TestRackReadWrite
+
+  def setup
+    @tmp = Tempfile.new('tchdb')
+    @path = @tmp.path + '.tch'
+    @uri = "tc://#{@path}"
+    @app_opts = { :uri => @uri }
+  end
+
+  def teardown
+    @tmp.close!
+    File.unlink(@path)
+  end
+end
diff --git a/test/test_tdb_single.rb b/test/test_tdb_single.rb
new file mode 100644
index 0000000..e04fe2f
--- /dev/null
+++ b/test/test_tdb_single.rb
@@ -0,0 +1,21 @@
+# -*- encoding: binary -*-
+require './test/rack_read_write.rb'
+$-w = true
+require 'metropolis'
+
+class Test_TDB_Single < Test::Unit::TestCase
+  attr_reader :tmp, :o, :uri
+  include TestRackReadWrite
+
+  def setup
+    @tmp = Tempfile.new('tdb')
+    @path = @tmp.path + '.tdb'
+    @uri = "tdb://#{@path}"
+    @app_opts = { :uri => @uri }
+  end
+
+  def teardown
+    @tmp.close!
+    File.unlink(@path)
+  end
+end