Tripletail::DB - DBI のラッパ
$TL->startCgi( -DB => 'DB', -main => \&main, ); sub main { my $DB = $TL->getDB('DB'); $DB->setDefaultSet('R_Trans'); $DB->tx(sub{ my $sth = $DB->execute(q{SELECT a, b FROM foo WHERE a = ?}, 999); while (my $hash = $sth->fetchHash) { $TL->print($hash->{a}); } # commit is done implicitly. }); $DB->tx('W_Trans' => sub{ $DB->execute(q{UPDATE counter SET counter = counter + 1 WHERE id = ?}, 1); $DB->commit; # can commit explicitly. } }
手動で接続/切断する場合は、connect/disconnectを使うこともできるが、なるべく使用しないことを推奨。
$db->execute(q{select * from a where mode in (??)}, ['a', 'b'])
と記述すると、
$db->execute(q{select * from a where mode in (?, ?)}, 'a', 'b')
のように解釈される。
$db->execute(q{select * from a limit ??}, [10, \'SQL_INTEGER'])
型指定ができるのは拡張プレースホルダのみです. 通常の ? によるプレースホルダではエラーとなります.
?
Tripletail::DBでは、レプリケーションを利用してロードバランスすることを支援するため、 1つのDBグループの中に、複数のDBセットを定義することが可能となっている。 DBセットの中には、複数のDBコネクションを定義できる。
更新用DBセット、参照用DBセット、などの形で定義しておき、プログラム中で トランザクション単位でどのDBセットを使用するか指定することで、 更新用クエリはマスタDB、参照用クエリはスレーブDB、といった 使い分けをすることが可能となる。
DBセットには複数のDBコネクションを定義でき、複数定義した場合は プロセス単位でプロセスIDを元に1つのコネクションが選択される。 (プロセスIDを定義数で割り、その余りを使用して決定する。)
同じDBグループの中の複数のDBセットで同じDBコネクション名が使用された場合は、 実際にDBに接続されるコネクション数は1つとなる。 このため、縮退運転時に参照用DBセットのDBコネクションを更新用の ものに差し替えたり、予め将来を想定して多くのDBセットに分散 させておくことが可能となっている。
DBセットの名称はSET_XXXX(XXXXは任意の文字列)でなければならない。 DBコネクションの名称はCON_XXXX(XXXXは任意の文字列)でなければならない。
いずれのDBコネクションも利用可能である必要があり、 接続できなかった場合はエラーとなる。
DBのフェイルオーバーには(現時点では)対応していない。
Tripletail の DB クラスは DBI に対するラッパの形となっており、多くのインタフェースは DBI のものとは異なる。 ただし、いつでも $DB->getDbh() メソッドにより元の DBI オブジェクトを取得できるので、 DBI のインタフェースで利用することも可能となっている。
$DB->getDbh()
DBI のインタフェースは以下のようなケースで利用できる。 ただし、 DBI を直接利用する場合は、TLの拡張プレースホルダやデバッグ機能、トランザクション整合性の管理などの機能は利用できない。
DBI に対するラッパであるため、大量の SQL を実行する場合などはパフォーマンス上のデメリットがある。
DBI での SELECT は、以下のように置き換えられる。
# DBI my $sth = $DB->prepare(q{SELECT * FROM test WHERE id = ?}); $sth->execute($id); while(my $data = $sth->fetchrow_hashref) { } # TL my $sth = $DB->execute(q{SELECT * FROM test WHERE id = ?}, $id); while(my $data = $sth->fetchHash) { }
TL では prepare/execute は一括で行い、 prepared statement は利用できない。
INSERT・UPDATEは、以下のように置き換えられる。
INSERT
UPDATE
# DBI my $sth = $DB->prepare(q{INSERT INTO test VALUES (?, ?)}); my $ret = $sth->execute($id, $data); # TL my $sth = $DB->execute(q{INSERT INTO test VALUES (?, ?)}, $id, $data); my $ret = $sth->ret;
prepare/execute を一括で行うのは同様であるが、 execute の戻り値は$sthオブジェクトであり、影響した行数を取得するためには $sth->ret メソッドを呼ぶ必要がある。
$sth
$sth->ret
プレースホルダの型指定は以下のように行う。
# DBI my $sth = $DB->prepare(q{SELECT * FROM test LIMIT ?}); $sth->bind_param(1, $limit, { TYPE => SQL_INTEGER }); $sth->execute; # TL my $sth = $DB->execute(q{SELECT * FROM test LIMIT ??}, [$limit, \'SQL_INTEGER']);
TLの拡張プレースホルダ(??で表記される)を利用し、配列のリファレンスの最後に型をスカラのリファレンスの形で渡す。 拡張プレースホルダでは、複数の値を渡すことも可能である。
# DBI my $sth = $DB->prepare(q{SELECT * FROM test LIMIT ?, ?}); $sth->bind_param(1, $limit, { TYPE => SQL_INTEGER }); $sth->bind_param(2, $offset, { TYPE => SQL_INTEGER }); $sth->execute; # TL my $sth = $DB->execute(q{SELECT * FROM test LIMIT ??}, [$limit, $offset, \'SQL_INTEGER']);
INSERTした行のAUTO_INCREMENT値の取得は、getLastInsertId で行える。
# DBI my $id = $DB->{mysql_insertid}; # TL my $id = $DB->getLastInsertId;
拡張ラッパでは制御できない機能にアクセスする場合などは、 DBI のハンドラを直接利用する。
# DBI my $id = $DB->{RowCacheSize}; # TL my $id = $DB->getDbh()->{RowCacheSize};
トランザクションには $DB->tx(sub{...}) メソッドを用いる。 DBセットを指定する時には $DB->tx(dbset_name=>sub{...}) となる。 渡したコードをトランザクション内で実行する。 die なしにコードを抜けた時に自動的にコミットされる。 途中で die した場合にはトランザクションはロールバックされる。
$DB->tx(sub{...})
$DB->tx(dbset_name=>sub{...})
# DBI $DB->do(q{BEGIN WORK}); # do something. $DB->commit; # TL $DB->tx(sub{ # do something. });
begin() メソッドも実装はされているがその使用は非推奨である。 また、 $DB->execute(q{BEGIN WORK}); として利用することはできない。
begin()
$DB->execute(q{BEGIN WORK});
"execute" に渡される SQL 文には、通常のプレースホルダの他に、 拡張プレースホルダ "??" を埋め込む事が出来る。 拡張プレースホルダの置かれた場所には、パラメータとして通常のスカラー値でなく、 配列へのリファレンスを与えなければならない。配列が複数の値を持っている場合には、 それらが通常のプレースホルダをカンマで繋げたものに展開される。
例: 以下の二文は等価
$DB->execute( q{SELECT * FROM a WHERE a IN (??) AND b = ?}, ['AAA', 'BBB', 'CCC'], 800); $DB->execute( q{SELECT * FROM a WHERE a IN (?, ?, ?) AND b = ?}, 'AAA', 'BBB', 'CCC', 800);
パラメータとしての配列の最後の項目が文字列へのリファレンスである時、その文字列は SQL 型名として扱われる。配列が複数の値を持つ時には、その全ての要素に対して 型指定が適用される。型名はDBI.pmで定義される。
例:
$DB->execute(q{SELECT * FROM a LIMIT ??}, [20, \'SQL_INTEGER']); ==> SELECT * FROM a LIMIT 20 $DB->execute(q{SELECT * FROM a LIMIT ??}, [20, 5, \'SQL_INTEGER']); ==> SELECT * FROM a LIMIT 20, 5
配列内の要素を更に2要素の配列とし、二番目の要素を文字列へのリファレンスと する事で、要素の型を個別に指定出来る。
$DB->execute( q{SELECT * FROM a WHERE a IN (??) AND b = ?}, [[100, \'SQL_INTEGER'], 'foo', \'SQL_VARCHAR'], 800); ==> SELECT * FROM a WHERE a IN (100, 'foo') AND b = '800'
Tripletail::DB
$TL->getDB
$DB = $TL->getDB $DB = $TL->getDB($inigroup)
Tripletail::DB オブジェクトを取得。 引数には Ini で設定したグループ名を渡す。 引数省略時は 'DB' グループが使用される。
$TL->startCgi / $TL->trapError の関数内でDBオブジェクトを取得する場合に使用する。
$TL->newDB
$DB = $TL->newDB $DB = $TL->newDB($inigroup)
新しく Tripletail::DB オブジェクト作成。 引数には Ini で設定したグループ名を渡す。 引数省略時は 'DB' グループが使用される。
動的にコネクションを作成したい場合などに使用する。 この方法で Tripletail::DB オブジェクトを取得した場合、"connect" / "disconnect" を呼び出し、接続の制御を行う必要がある。
connect
DBに接続する。
$TL->startCgi / $TL->trapError の関数内でDBオブジェクトを取得する場合には自動的に接続が管理されるため、このメソッドを呼び出してはならない。
$TL->newDB で作成した Tripletail::DB オブジェクトに関しては、このメソッドを呼び出し、DBへ接続する必要がある。
connect時には、AutoCommit 及び RaiseError オプションは 1 が指定され、PrintError オプションは 0 が指定される。
AutoCommit
RaiseError
PrintError
disconnect
DBから切断する。
$TL->newDB で作成した Tripletail::DB オブジェクトに関しては、このメソッドを呼び出し、DBへの接続を切断する必要がある。
tx
$DB->tx(sub{...}) $DB->tx('SET_W_Trans' => sub{...})
指定されたDBセット名でトランザクションを開始し、その中でコードを 実行する。トランザクション名(DBセット名) は ini で定義されていな ければならない。名前を省略した場合は、デフォルトのDBセットが使われるが、 setDefaultSetによってデフォルトが選ばれていない場合には例外を発生させる。
コードを die なしに終了した時にトランザクションは暗黙にコミットされる。 die した場合にはロールバックされる。 コードの中で明示的にコミット若しくはロールバックを行うこともできる。 明示的にコミット若しくはロールバックをした後は、 tx を抜けるまで DB 操作は禁止される。 この間の DB 操作は例外を発生させる。
rollback
$DB->rollback
現在実行中のトランザクションを取り消す。
commit
$DB->commit
現在実行中のトランザクションを確定する。
inTx
$DB->inTx() and die "double transaction"; $DB->inTx('SET_W_Trans') or die "transaction required";
既にトランザクション中であるかを確認する。 既にトランザクション中であれば真を、 他にトランザクションが走っていなければ偽を返す。 トランザクションの指定も可能。 異なるDBセット名のトランザクションが実行中だった場合には 例外を発生させる。
begin
$DB->begin $DB->begin('SET_W_Trans')
非推奨。"tx" を使用のこと。
指定されたDBセット名でトランザクションを開始する。トランザクション名 (DBセット名) は ini で定義されていなければならない。 名前を省略した場合は、デフォルトのDBセットが使われるが、 setDefaultSetによってデフォルトが選ばれていない場合には例外を発生させる。
CGIの中でトランザクションを開始し、終了せずに Main 関数を抜けた場合は、自動的に rollbackされる。
トランザクション実行中にこのメソッドを呼んだ場合には、例外を発生させる。 1度に開始出来るトランザクションは、1つのDBグループにつき1つだけとなる。
setDefaultSet
$DB->setDefaultSet('SET_W_Trans')
デフォルトのDBセットを選択する。ここで設定されたDBセットは、引数無しのbegin() や、beginせずに行ったexecuteの際に使われる。このメソッドは Main 関数 の先頭で呼ばれる事を想定している。
execute
$DB->execute($sql, $param...) $DB->execute(\'SET_W_Trans' => $sql, $param...)
SELECT/UPDATE/DELETEなどの SQL 文を実行する。 第1引数に SQL 、第2引数以降にプレースホルダの引数を渡す。 ただし、第1引数にリファレンスでDBセットを渡すことにより、 トランザクション外での実行時にDBセットを指定することが可能。
SELECT
DELETE
第2引数以降の引数では、拡張プレースホルダが使用できる。 "拡張プレースホルダ詳細" を参照。
既にトランザクションが実行されていれば、そのトランザクションの DBセットで SQL が実行される。
トランザクションが開始されておらず、かつ "lock" により テーブルがロックされていれば、ロックをかけているDBセットで SQL が実行される。
いずれの場合でもない場合は、"setDefaultSet" で指定された トランザクションが使用される。 "setDefaultSet" による設定がされていない場合は、例外を発生させる。
このメソッドを使用して、LOCK/UNLOCK/BEGIN/COMMITといった SQL 文を 実行してはならない。実行しようとした場合は例外を発生させる。 代わりに専用のメソッドを使用する事。
LOCK
UNLOCK
BEGIN
COMMIT
selectAllHash
$DB->selectAllHash($sql, $param...) $DB->selectAllHash(\'SET_W_Trans' => $sql, $param...)
SELECT結果をハッシュの配列へのリファレンスで返す。 データがない場合は [] が返る。
my $arrayofhash = $DB->selectAllHash($sql, $param...); foreach my $hash (@$arrayofhash){ $TL->log(DBDATA => "name of id $hash->{id} is $hash->{name}"); }
selectAllArray
$DB->selectAllArray($sql, $param...) $DB->selectAllArray(\'SET_W_Trans' => $sql, $param...)
SELECT結果を配列の配列へのリファレンスで返す。 データがない場合は [] が返る。
my $arrayofarray = $DB->selectAllArray($sql, $param...); foreach my $array (@$arrayofarray){ $TL->log(DBDATA => $array->[0]); }
selectRowHash
$DB->selectRowHash($sql, $param...) $DB->selectRowHash(\'SET_W_Trans' => $sql, $param...)
SELECT結果の最初の1行をハッシュへのリファレンスで返す。 実行後、内部でfinishする。 データがない場合は undef が返る。
finish
my $hash = $DB->selectRowHash($sql, $param...); $TL->log(DBDATA => "name of id $hash->{id} is $hash->{name}");
selectRowArray
$DB->selectRowArray($sql, $param...) $DB->selectRowArray(\'SET_W_Trans' => $sql, $param...)
SELECT結果の最初の1行を配列へのリファレンスで返す。 実行後、内部でfinishする。 データがない場合は undef が返る。
my $array = $DB->selectRowArray($sql, $param...); $TL->log(DBDATA => $array->[0]);
upsert
$DB->upsert( 'table1', {key1 => 'val1', key2 => 'val2'}, # $keys {val3 => 'val3', val4 => 'val4'}); # $values $DB->upsert( 'schema1', # スキーマ名 'table1', {key1 => 'val1', key2 => 'val2'}, # $keys {val3 => 'val3', val4 => 'val4'}); # $values $DB->upsert( \'SET_W_Trans', 'table1', {key1 => 'val1', key2 => 'val2'}, # $keys {val3 => 'val3', val4 => 'val4'}); # $values
テーブルの特定の一行に対して UPDATE を実行し、もし該当行が存在しなければ INSERT を実行するという処理を、たとえトランザクションの外で行われた場合であってもアトミックに行う。
与えられたテーブル名やスキーマ名は、内部で自動的に symquote した上で SQL 文に埋め込まれる。
$keys はテーブルの一意キー(通常は主キー)からその値へのハッシュテーブル。 その内容はテーブルの一意キーが渡っているカラムと一致していなければならない。 つまりこのキー集合によってテーブル上のカラムを常に一意に特定する事ができなければならない。
$keys
$values はそれ以外のカラム名からその値へのハッシュテーブルであり、省略も可能。 上記の例に挙げたコードは、まず次のような UPDATE 文を実行し、
$values
UPDATE table1 SET val3 = 'val3', val4 = 'val4' WHERE key1 = 'val1', key2 = 'val2'
該当する行が存在しなければ次のような INSERT 文を実行する。
INSERT INTO table1 ( key1 , key2 , val3 , val4 ) VALUES ('val1', 'val2', 'val3', 'val4')
現在 pgsql のみで利用可能。
findTables
my $sth = $DB->findTables({ set => 'SET_W_Trans', # 省略可能 schema => 'schema', # 省略可能 table => 'table\\_%' # 省略可能 }); while (my $row = $sth->fetchHash) { print $row->{TABLE_NAME}, "\n"; }
データベース内に存在するテーブルの一覧を得るための Tripletail::DB::Sth オブジェクトを返す。
Tripletail::DB::Sth
スキーマ名やテーブル名に _ または % 記号が含まれていた場合は、その文字列は LIKE 演算子のワイルドカードと見倣される。省略も可能であり、その場合は全てのスキーマ名または全てのテーブル名にマッチする '%' が指定された場合と同様の結果になる。undef の場合も同様。
_
%
'%'
undef
このメソッドにより得られた Tripletail::DB::Sth オブジェクトの返す各行は DBI->table_info() と同等である。詳しくは http://search.cpan.org/dist/DBI/DBI.pm#table_info を参照。
DBI->table_info()
getTableColumns
my $columns_ref = $DB->getTableColumns(\'SET_W_Trans', 'schema', 'table'); my $columns_ref = $DB->getTableColumns(\'SET_W_Trans', 'table'); my $columns_ref = $DB->getTableColumns('schema', 'table'); my $columns_ref = $DB->getTableColumns('table'); if ($columns_ref) { foreach my $column_ref (@$columns_ref) { printf( "%s :: %s\n", $column_ref->{ COLUMN_NAME }, $column_ref->{ TYPE_NAME }); } } else { print "table not found\n"; }
テーブルの持つカラムの一覧を、ハッシュリファレンスを要素とする配列リファレンスで返す。 その要素であるカラム情報は DBI->column_info() から得られるものと同等である。 詳しくは http://search.cpan.org/dist/DBI/DBI.pm#column_info を参照。
DBI->column_info()
指定されたテーブルが存在しない場合、このメソッドは undef を返す。
lock
$DB->lock(set => 'SET_W_Trans', read => ['A', 'B'], write => 'C')
指定されたDBセットに対してLOCK TABLESを実行する。setが省略された場合はデフォルト のDBセットが選ばれる。 CGI の中でロックした場合は、 Main 関数 を抜けた時点で自動的に unlock される。
LOCK TABLES
set
ロック実行中にこのメソッドを呼んだ場合には、例外を発生させる。 1度に開始出来るロックは、1つのDBグループにつき1つだけとなる。
現在 mysql でのみ使用可能.
mysql ではロック中にテーブルのエイリアスを使用する場合、エイリアスに対してもロックを指定する必要がある。これを行うには、テーブル名の文字列の替わりにハッシュのリファレンス {'テーブル名' => 'エイリアス'} を指定する。次に、テーブル sample とそのエイリアス A, B をロックする例を示す。
$DB->lock(read => ['sample', {'sample' => 'A'}, {'sample' => 'B'}]); $DB->execute(q{ SELECT sample.nval, A.nval as A, B.nval as B FROM sample, sample AS A, sample AS B WHERE sample.nval + 1 = A.nval AND A.nval + 1 = B.nval }); $DB->unlock;
unlock
$DB->unlock
UNLOCK TABLES を実行する。 ロックがかかっていない場合は例外を発生させる。
UNLOCK TABLES
setBufferSize
$DB->setBufferSize($kbytes)
バッファサイズをKB単位でセットする。行を1行読み込んだ結果 このサイズを上回る場合、dieする。 0 または undef をセットすると、制限が解除される。
die
0
quote
$DB->quote($literal)
文字列をリテラルとしてクォートする。
通常 'a b c' のようにシングルクオートで文字列が囲まれる。
'a b c'
symquote
$DB->symquote($sym)
文字列を識別子としてクォートする。
mysql の場合は `a b c` となり、それ以外の場合は "a b c" となる。
`a b c`
"a b c"
escapeLike
$DB->escapeLike($pattern)
与えられた文字列を LIKE 演算子のパターンと見倣して、そのワイルドカード記号をエスケープする。 エスケープの方法は各データベースエンジンによって異なるが、多くの場合は次の式が成立する。
$DB->escapeLike('foo') eq 'foo' $DB->escapeLike('f_o') eq 'f\\_o' $DB->escapeLike('f%o') eq 'f\\%o' $DB->escapeLike('f\\') eq 'f\\\\'
getType
$DB->getType;
DBのタイプを返す。(mysql, pgsql, ...)
(mysql, pgsql, ...)
getDbh
$dbh = $DB->getDbh $dbh = $DB->getDbh('SET_W_Trans')
DBセット内のDBハンドルを返す。 返されるオブジェクトは DBI ネイティブのdbhである。
dbh
ネイティブのDBハンドルを使用してクエリを発行した場合、デバッグ機能(プロファイリング等)の機能は使用できません。 また、トランザクションやロック状態の管理もフレームワークで行えなくなるため、注意して使用する必要があります。
getLastInsertId
$id = $DB->getLastInsertId()
セッション内の最後の自動採番の値を取得.
fetchHash
$sth->fetchHash
ハッシュへのリファレンスで1行取り出す。
fetchArray
$sth->fetchArray
配列へのリファレンスで1行取り出す。
ret
最後に実行した execute の戻り値を返す。
rows
$sth->rows
DBI と同様。
$sth->finish
nameArray
$sth->nameArray
$sth->{NAME_lc} を返す。
$sth->{NAME_lc}
nameHash
$sth->nameHash
$sth->{NAME_lc_hash} を返す。
$sth->{NAME_lc_hash}
DBグループのパラメータのうち、半角小文字英数字のみで構成された パラメータは予約済みで、DBグループの動作設定に使用する。 DBセットは、予約済みではない名前であれば任意の名称が使用でき、 値としてDBコネクションのINIグループ名をカンマ区切りで指定する。
[DB] namequery=1 autoretry=1 type=mysql defaultset=SET_R_Trans SET_W_Trans=CON_DBW1 SET_R_Trans=CON_DBR1,CON_DBR2 [CON_DBW1] dbname=test user=daemon host=192.168.0.100 [CON_DBR1] dbname=test user=daemon host=192.168.0.110 [CON_DBR2] dbname=test user=daemon host=192.168.0.111
以下は特別なパラメータ:
namequery
namequery = 1
これを1にすると、実行しようとしたクエリのコマンド名の直後に /* foo.pl:111 [DB.R_Transaction1.DBR1] */ のようなコメントを挿入する。 デフォルトは0。
/* foo.pl:111 [DB.R_Transaction1.DBR1] */
autoretry
autoretry = 1
これを 1 にすると "tx" を用いて実行されたトランザクションがデッドロックにより失敗した場合に自動的にトランザクションを再実行する。 データベースがデッドロック検出機構を持っていない場合には再実行は行われない。デフォルトは 0。
なお単体の "execute" がデッドロックを起こした場合には、この設定とは無関係に必ず再実行される。
type
type = mysql
DBの種類を選択する。 mysql, pgsql, oracle, interbase, sqlite, mssql が使用可能。 必須項目。
defaultset
defaultset = SET_W_Trans
デフォルトのDBセットを設定する。 ここで設定されたDBセットは、引数無しのbegin()や、beginせずに行ったexecuteの際に使われる。
dbname
dbname = test
DB名を設定する。
host
host = localhost
DBのアドレスを設定する。 デフォルトはlocalhost。
localhost
user
user = www
DBに接続する際のユーザー名を設定する。
password
password = PASS
DBに接続する際のパスワードを設定する。 省略可能。
mysql_read_default_file
mysql_read_default_file = .../tl_mysql.cnf
mysql クライアントライブラリが使用する設定ファイル my.cnf のパスを指定する。 パスの指定を .../ で始めることで、 ini ファイルからの相対パスとして指定する事も可能。 設定ファイルを使用する事で、 default-character-set 等の Tripletail::DB や DBD::mysql からは設定できない項目が設定できる。 また、設定ファイルで user, password, host 等の値を指定する場合は、 Ini パラメータ のDBコネクションの値を省略する事ができる。(dbname だけは省略できない)
mysql_read_default_group
mysql_read_default_group = tripletail
mysql_read_default_file 指定時に、設定ファイル中のどのグループを使用するかを指定する。 グループを指定した場合は、 [client] グループの設定と指定したグループの設定の両方が有効になる。 グループを指定しない場合、 [client] グループの設定のみが有効となる。
試験的に SQL Server との接続が実装されています. DBD::ODBC と, Linux であれば unixODBC + freetds で, Windows であれば 組み込みの ODBC マネージャで動作します.
設定例:
# <tl.ini> [DB] type=mssql defaultset=SET_W_Trans SET_W_Trans=CON_RW [CON_RW] # dbname に ODBC-dsn を設定. dbname=test user=test password=test # freetds経由の時は, そちらのServernameも指定. tdsname=tds_test
freetds での接続文字コードの設定は freetds.conf で 設定します.
;; <freetds.conf> [tds_test] host = 10.0.0.1 ;;port = 1433 tds version = 7.0 client charset = UTF-8
Tripletail
Copyright 2006 YMIRLINK Inc.
This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself
このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。
Address bug reports and comments to: tl@tripletail.jp
HP : http://tripletail.jp/
To install Tripletail, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Tripletail
CPAN shell
perl -MCPAN -e shell install Tripletail
For more information on module installation, please visit the detailed CPAN module installation guide.