# QDBM付属Ruby用API仕様書

Copyright (C) 2000-2006 Mikio Hirabayashi
Last Update: Thu, 26 Oct 2006 15:00:20 +0900
[API] [English]

## 目次

1. 概要
2. インストール
3. サンプルコード
4. バグ

## 概要

QDBMにはRuby言語用のAPIがある。QDBMの基本APIと拡張APIと上級APIの関数群をRubyのクラス機構を用いてカプセル化したものである。また、Rubyのマルチスレッド機能においてスレッドセーフである。

クラス `Depot' か `Curia' か `Villa' のコンストラクタ `new' を呼び出してデータベースを開き、その戻り値のオブジェクトをハンドルにする。データベースを閉じるにはメソッド `close' を呼ぶ。ファイナライザは利用されないが、`new' のイテレータを用いることでデータベースの閉じ忘れを防ぐことができる。メソッド `put' はレコードを追加するために用いる。メソッド `out' はレコードを削除するために用いる。メソッド `get' はレコードを検索するために用いる。その他にも、C言語のAPIとほぼ同じ操作を利用することができる。各クラスは例外クラス `EANY' およびそのサブクラスを定数として持つ。それらは各メソッドが失敗した際に投げられる。

これらのクラスは `Enumerable' モジュールをMix-inしているので、`find' や `sort' といったメソッドが利用できる。また、`Hash' クラスに似せて '[]=' や '[]' 等のメソッドを定義しているので、普通のハッシュのように利用することができる。

データベースに格納するレコードのキーと値は文字列として扱われるが、バイナリデータをそのまま格納することも可能である。ただし、`Villa' は直列化可能かつ比較可能な任意のオブジェクトを格納することができる。`Depot' はファイルを用いてハッシュデータベースを実現し、`Curia' はディレクトリと複数のファイルを用いてハッシュデータベースを実現し、`Villa' はファイルを用いてB+木データベースを実現する。`Depot' は最も高速である。`Curia' は最もスケーラブルである。`Villa' はカーソルによって順序に基づく参照を可能にする。

`put' で既存のレコードの上書きがキャンセルされた際や `get' で存在しないレコードが検索された際には例外によって操作の失敗が通知されるが、それが鬱陶しい場合は `silent' フラグを真にするとよい。その場合は失敗が戻り値によって通知される。

APIの詳細に関しては、サブディレクトリ `rbapidoc' の文書を参照すること。

## インストール

Rubyの1.6.5以降のバージョンがインストールされ、QDBMが `/usr/local' 以下にインストールされていることが必要である。

インストール作業は、サブディレクトリ `ruby' をカレントディレクトリにして行う。

    cd ruby

ビルド環境を設定する。

    ./configure

プログラムをビルドする。

    make

プログラムの自己診断テストを行う。

    make check

プログラムをインストールする。作業は `root' ユーザで行う。

    make install

一連の作業が終ると、Rubyのインストールディレクトリに応じた適当な場所に `depot.rb'、`mod_depot.so' 、`curia.rb' 、`mod_curia.so' 、`villa.rb' 、`mod_villa.so' 等のライブラリがインストールされ、コマンド `rbdptest' と `rbcrtest' と `rbvltest' が `/usr/local/bin' にインストールされる。

アンインストールするには、`./configure' をした後の状態で以下のコマンドを実行する。作業は `root' ユーザで行う。

    make uninstall

## サンプルコード

名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。

    require 'depot'

    NAME = "mikio"
    NUMBER = "000-1234-5678"
    DBNAME = "book"

    def main
      depot = nil
      begin

        # データベースを開く
        depot = Depot::new(DBNAME, Depot::OWRITER | Depot::OCREAT)

        # レコードを格納する
        depot.put(NAME, NUMBER)

        # レコードを取得する
        printf("Name: %s\n", NAME)
        printf("Number: %s\n", depot.get(NAME))

      rescue Depot::EANY
        printf("%s\n", $!)
        return 1
      ensure

        # データベースを閉じる
        if(depot)
          begin
            depot.close()
          rescue Depot::EANY
            printf("%s\n", $!)
          end
        end

      end
      return 0
    end

    exit(main());

上記の例を `Hash' クラスに似せたインタフェースとイテレータを用いて書き直した例を以下に示す。

    require 'depot'

    NAME = "mikio"
    NUMBER = "000-1234-5678"
    DBNAME = "book"

    def main
      begin

        # データベースを開いて自動的に閉じる
        Depot::new(DBNAME, Depot::OWRITER | Depot::OCREAT) do |depot|

          # レコードを格納する
          depot[NAME] = NUMBER

          # レコードを取得する
          printf("Name: %s\n", NAME)
          printf("Number: %s\n", depot[NAME])

        end

      rescue Depot::EANY
        printf("%s\n", $!)
        return 1
      end
      return 0
    end

    exit(main());

`Villa' クラスを用いて文字列の前方一致検索を行う例を以下に示す。

    require 'villa'

    DBNAME = "words"
    PREFIX = "apple"

    def main
      begin

        # データベースを開いて自動的に閉じる
        Villa::new(DBNAME, Villa::OWRITER | Villa::OCREAT) do |villa|

          # レコードを格納する
          villa.put("applet", "little application", Villa::DDUP)
          villa.put("aurora", "polar wonderwork", Villa::DDUP)
          villa.put("apple", "delicious fruit", Villa::DDUP)
          villa.put("amigo", "good friend", Villa::DDUP)
          villa.put("apple", "big city", Villa::DDUP)

          begin

            # カーソルを候補の先頭に置く
            villa.curjump(PREFIX)

            # カーソルを走査する
            while(true)
              key = villa.curkey()
              (key.index(PREFIX) == 0) || break
              val = villa.curval()
              printf("%s: %s\n", key, val)
              villa.curnext()
            end

          rescue Villa::ENOITEM
          end

        end

      rescue Villa::EANY
        printf("%s\n", $!)
        return 1
      end
      return 0
    end

    exit(main());

## バグ

標準添付ライブラリの `DBM' クラスのインタフェースと微妙に違うところがある。

もしもRubyハッカー達の手にかかれば、より効率的な実装がなされるだろう。

インタフェースを簡潔にするため、Ruby用のCuriaにはラージオブジェクトを扱う機能はない。