2014-04-24 Otogiri::Plugin::TableInfo ってのを書いてみた


ちょいちょいこの blog に書いてますが、最近 Otogiri::Plugin::TableInfoっていう物体を 書きました。

こんな感じで使える物体です。

use Otogiri::Plugin::TableInfo;
my $db = Otogiri->new( connect_info => [ ... ] );
$db->load_plugin('TableInfo');
my @table_names = $db->show_tables(); # => DB 上にあるテーブル名一覧が返る
my @filtered_table_name = $db->show_tables(qr/^user/); # => 「user」で始まるテーブル名一覧が返る
my $create_table_ddl = $db->desc('user'); # => user テーブルの CREATE TABLE 文が返る

とまあ大体こんな感じになっています。

DBMS は PostgreSQL, MySQL, SQLite に対応しています。MySQL と SQLite は DBI というか、DBIx::Inspector から CREATE TABLE 文が拾えるので割と楽なのですが、PostgreSQL はそういうの無いので、カラム情報や FK の情報などを組み合わせて 気合いで構築しています。(僕はお仕事で PostgreSQL を使っているので泣きながら実装したのです)

ここが地獄の一丁目です。 興味がある方は見てみると良いです。(見ないで済む生き方を選べるなら、きっとその方が幸せです)

最近いくつか紹介してる、Reply と組み合わせると便利シリーズの一環ですね。まーそこそこ便利だと思うので、興味がある方は使ってみると良いです。

「Otogiri 以外でも使ってみたい」って人がいたら、ここのコメントとか @tsucchi とか github issue とかに リクエストしてもらえれば対応するかもしれないし、気が向いたら対応するかもです。



2014-04-19 TravisCI で pg_dump が使えないっぽい


表記の通りなんですが、、最近 Otogiri::Plugin::TableInfo というモジュールを作ってます。 んで、これで PostgreSQL のテーブル定義を出すために異常な努力をしているのですが、正解の CREATE TABLE 文を pg_dump で出したやつ(を元に要らない行を消したやつ)としています。

なので、pg_dump したやつと、僕が書いてるモジュールが出してるテーブル定義が一致するかをテストするのを書いてたのですが、

なんかこんな感じの悲しい感じになってます。

286 t/03_pg.t ....... 1/? pg_dump: server version: 9.3.2; pg_dump version: 9.1.11
287 pg_dump: aborting because of server version mismatch

うーん、コレ何とかする方法あるんでしょうか?

ってか、なんでこんな良くわからん事になってるんだろ...



2014-04-12 hachioji.pm #39 に行ってきた


表記のイベントに参加してきましたよ、、、ってことで。

発表資料はこちら

と、いうわけで、最近色々工夫している Reply 環境をまとめた感じの話をしてみました。

Reply 使ってみると便利ですし、ORM と組み合わせると本当に便利なので、皆様是非是非お試しください。

あと、「冒険☆ミルキィロード」は本当に最高におすすめなので、是非是非購入すると良いと思います!



2014-04-10 Reply::Plugin::StandardPrompt ってのを書いてみた


こんばんは、Reply おじさんです。

多分このへん(↓)の続きみたいなお話です。

表記の通り、Reply::Plugin::StandardPrompt という Reply のプラグインを書きました。

シェルみたいな、カスタマイズ可能なプロンプトを提供するプラグインです。

Reply に標準添付されているプラグインで、Reply::Plugin::FancyPromptというのがあるのですが、これは名前に反して(?)あまりファンシーではありません。bash の PS1 で表現すると

export PS1="\!> "

と同等で、カスタマイズも出来ません。。。

まー普通に使う分にはそんなに困らないかも、なのですが、上記に挙げてる記事にも書いているのですが、最近は Otogiri のプラグインとかと組み合わせて、DB のデータメンテとかするのに Reply を使っているので、 せめて DB 名とか出したいわけです。

と、言うわけで書いたのが表記のモジュール。

cpanm とかでインストールしてから、FancyPrompt や他のプロンプト系のプラグインの設定を消して、代わりに

[StandardPrompt]
prompt='reply@' . Show_dbname() . "[$history_count]> "

みたいな感じで書きます。prompt には Perl の文が普通に書けて、main パッケージにエクスポートされている関数や変数が使えます。Show_dbname は Reply::Plugin::Otogiriが提供する現在使っているDB の設定名を返す関数です。$history_count は StandardPrompt が提供する変数で、bash でいうところの ! です。(コマンドの実行回数)

なので、この例ですと、

reply@myservice[10]> 

みたいな感じのプロンプトになります。(DB の設定名が myservice でコマンド実行回数が10回目とした場合)

ちょっと名前が強気すぎるかなぁ、とか他のプロンプトと共存できない(つーかそもそも出来るのか?実装上できるようになってるのだが、現実的に無理な気がする)とか、 諸々の問題があるので、とりあえず github 止まりにしてます。気が向いたら CPAN に上げるかも、です。



2014-04-04 Reply::Plugin::DataDumperAutoEncode ってのを書いてみた


こんにちは、最近 Reply 芸人と化している気がするミルキアンのおじさんです。

上記の続きみたいなお話なので、まだ読んでない方は読むと良いかもです。

Reply は ORM と組み合わせるとめっちゃ便利だし、組み合わせなくても普通に便利なのですが、一つ困った事がありました。

たとえば Otogiri と組み合わせて、DB からデータを引こうとすると、

% reply-service1
1> Select('detective', { id => 1 });
$res[1] = {
    id => 1,
    name => 'シャーロック・シェリンフォード',
    age => 15,
    toys => 'サイコキネシス',
    birthday => '3/31',
}

こういうデータが返って欲しいのに、実際に返ってくるのは

% reply-service1
1> Select('detective', { id => 1 });
$res[1] = {
    id => 1,
    name => "\x{30b7}\x{30e3}\x{30fc}\x{30ed}\x{30c3}\x{30af}\x{30fb}\x{30b7}\x{30a7}\x{30ea}\x{30f3}\x{30d5}\x{30a9}\x{30fc}\x{30c9}",
    age => 15,
    toys => "\x{30b5}\x{30a4}\x{30b3}\x{30ad}\x{30cd}\x{30b7}\x{30b9}",
    birthday => '3/31',
}

こんな感じの decode されたデータです。バイナリアンやスーパーハッカーの皆様ならこれを読む事も可能なのかもしれませんが、 いくら僕がシャロの事が大好きでも decode された文字列まではさすがに覚えていないし、シャロじゃなかったら覚える気にはなれないので結局読めません。

もちろん、

4> encode_utf8("\x{30b5}\x{30a4}\x{30b3}\x{30ad}\x{30cd}\x{30b7}\x{30b9}")
$res[3] = 'サイコキネシス'

こんな感じで、encode してあげれば読めますが、DBから返ってきた複数行の値とかだったりすると、つらいわけです。

そこで登場するのが、今回書いたプラグイン、Reply::Plugin::DataDumperAutoEncodeです。 標準プラグインの DataDumper の代わりに使います。ですので、コイツを CPAN からインストールして、.replyrc

[DataDumper]

と書いてある行を

[DataDumperAutoEncode]

と書き換えれば OK です。カジュアル!

使い方はこんな感じ。

tsucchi@surreal[590]$ reply
0> use Encode
1> decode_utf8('ほげ')
$res[0] = 'ほげ'

2> disable_auto_encode 
$res[1] = 0

3> decode_utf8('ほげ')
$res[2] = "\x{307b}\x{3052}"

4> enable_auto_encode 
$res[3] = '1'

5> decode_utf8('ほげ')
$res[4] = 'ほげ'

さっき ship した 0.02 からは disable_auto_encode/enable_auto_encode ってメソッドをつけたので、どうしても decode された文字列じゃないと満足できない あなたにもばっちりです。(謎)

めっちゃ簡単に説明すると、最初に提示した

% reply-service1
1> Select('detective', { id => 1 });
$res[1] = {
    id => 1,
    name => 'シャーロック・シェリンフォード',
    age => 15,
    toys => 'サイコキネシス',
    birthday => '3/31',
}

こういうデータを引っ張れるようになって、最高便利! ってことです。お試しあれ!



2014-04-01 最近の Reply-Otogiri 環境の話


おはよーおはよー。今日はエイプリルフールですが、これは真面目な(?)話です。

REPLでORMを使えるようにすると、めっちゃ便利だ、という話の続き。

とりあえず僕がほしかった環境が大体できたので、公開してみようと思う。

やりたかった事は

  • Reply の設定ファイルをコピペするのが良く無いので、1つにしたい(プラグインを追加するたびに全部書き換えとか嫌)
  • せっかく Reply::Plugin::Otogiri あるし使ってみるか

の2点です。

Reply::Plugin::Otogiri について

前回紹介した、

ですが、R::P::Otogiri はバージョンアップしてちょっと中身が変わったので内容が古くなっています。SYNOPSIS とか見ると分かるのですが、 新しいバージョンでは、設定ファイルを指定できてそこに DB の接続情報書いたりできます。んで、環境変数でどの設定を使うかを切り替える感じになっています。

.replyrc_otogiri

Reply の設定ファイルです。こんな感じ。DBの設定を書くファイルと、ロードしたいプラグインを指定します。TableInfo ってのは、最近僕が書いてるプラグインで、MySQL で言う所の、SHOW TABLESSHOW CREATE TABLE相当の処理をやってくれる物体です。

[Otogiri]
config=.otogiri_connect_info.pl
plugins = DeleteCascade,TableInfo

.bashrc (alias)

alias をきってサービス毎にコマンドを作っています。こんな感じ。あたらしい Reply::Plugin::Otogiri は環境変数 PERL_REPLY_PLUGIN_OTOGIRI でどのDB接続を使うかを指定できるので、ここで切り替えます。

alias reply-service1="env PERL_REPLY_PLUGIN_OTOGIRI=service1 reply --cfg $HOME/.replyrc_otogiri"
alias reply-service2="env PERL_REPLY_PLUGIN_OTOGIRI=service2 reply --cfg $HOME/.replyrc_otogiri"

.otogiri_connect_info.pl

.replyrc_otogiri で指定した DB 毎の設定ファイルです。

+{
    service1 => {
        connect_info => [
            'dbi:Pg:dbname=service1;host=db.example.com;port=5432',
            'tsucchi',
            'passdayo',
            { AutoCommit => 1, RaiseError => 1,PrintError => 0, pg_enable_utf8 => 1,},
        ]
    },
},

使い方

サービス毎に alias でコマンドを作ってあるので、普通にそれを呼ぶだけです。

% reply-service1
1> Select('detective', { id => 1 });
$res[1] = {
    id => 1,
    name => 'Sherlock Shellingford',
    age => 15,
    toys => 'psychokinesis'
    birthday => '3/31',
}

こんな感じで、シャーロック・シェリンフォードちゃん(昨日は誕生日だったね。おめでとう)の情報がゲットできて、 Otogiri のメソッドが呼べて、最高便利です。 新しい Reply::Plugin::Otogiri では、Otogiri のメソッドは大文字で始まるようになっています。($db とかの変数を経由せずに直接呼べます)。 また、タブを押すと Otogiri のメソッド名に対して補完が効きます。便利。



2014-03-22 Perl Beginners #12 に行ってきました


日付的には一昨日(3/20)ですが、Perl Beginners #12に行ってきました。LTしました。

資料はこちら

世の中には「異常な努力」によって成り立っている凄いモジュールがあること、でも簡単な処理でついうっかり「異常な努力」しちゃう事ってあるよね?気をつけよう、というお話です。以前話した「正解はひとつ!じゃない!!」のコードありバージョンみたいな感じですね。僕の前の papix さんのやつがコードない系だったのでちょうと良かったんじゃないかなー、と勝手に思っています。なお、今回はミルキィホームズ成分は控えめでお送りしました。

Perl Beginners はビギナー向けとはいいますが、色んなレベルの色んなお話が出てきて、しかも気軽な感じですごくいいなぁ、と思います。予定が合えば、また参加したいなー。



2014-03-18 Otogiri-0.09 がリリースされました


表記のものをリリースしました。

ポスグレ使っている人以外は全然関係ないのですが、PostgreSQL って last_insert_id を取得する際に、 シーケンスの名前を指定しないと取れないんですよ。

で、一方で、MySQL の LAST_INSERT_ID() みたいな振る舞いをする、LASTVAL() って関数もあります。 なので、これ使って便利にしたいなー、と思って改修してみました。

前回書いたように、最近 Otogiri + REPL の環境で DB を触っているのですが、

$db->last_insert_id(undef, undef, undef, { sequence => 'some_table_id_seq' });

とか書かないと SERIAL の ID が取れなくて、めっちゃダルかったのが、

$db->last_insert_id();

と一発で取れるようになって、大変カジュアルで便利だなー、と思う次第です。

もちろん、シーケンス名を指定した場合も従来通り普通にとれます。



2014-03-14 REPLでORMを使えるようにすると、めっちゃ便利だ、という話


この記事は何?

rails console の劣化版みたいなやつの紹介です

それじゃ分かんないよ?

端末からインタラクティブに ORM のメソッド投げたりできます。便利

ってか REPL って何?

端末でインタラクティブにプログラムを実行する物体です。irb とかが有名ですね。Perl では Reply ってのがおすすめです。

良くわからない人は以下の記事読んで、実際に使ってみるのが良いと思う。

で、どうやるの?

まずは必要な物体をインストールしましょう。Reply と使いたい ORM を入れます

% cpanm Reply
% cpanm Otogiri
% cpanm Otogiri::Plugin::DeleteCascade

ORM はお好みのやつでいいですが、DeleteCascade が使えるとめっちゃ捗るので、Otogiri がおすすめ。

で、一度実行してみましょう。

% reply
0> my $aa = 1 + 1;
$res[0] = 2

1> exit

うん、便利ですね。

一度実行すると、.replyrcというファイルが出来るので、こいつをコピーして、ORM 呼ぶ用のコンフィグを作ります

cp .replyrc .replyrc_myservice

名前は適宜好きなのつけてください。その DB を使うサービス名が良いと思います。

で、.replyrc_myservice を書き換えます。こんな感じ。

line 4〜8が追加した分です。

で、

% reply --cfg ~/.replyrc_myservice

のように、cfg オプションでコンフィグを指定できるので、そんな感じで実行します。すると...

% reply --cfg ~/.replyrc_myservice
1> $db->select('detective', { id => 1 });
$res[1] = {
    id => 1,
    name => 'Sherlock Shellingford',
    age => 15,
    toys => 'psychokinesis'
}

こんな感じで、シャーロック・シェリンフォードちゃんの情報がゲットできて$db 変数から Otogiri のメソッドが呼べて、最高便利です。

これだけだと、いちいちコンフィグを指定しなくてはいけないのがめんどくさいので、alias とかに登録しとくのが良いと思います。

% vi .bashrc
...
alias otogiri-myservice='reply --cfg $HOME/.replyrc_myservice'
...

補足

Perl入学式の校長としても有名な、papix 氏が Reply のプラグイン書いてるみたいです。

ちょっと試したんだけど、うまく動かなかったので、うまくいったらコレの紹介も書こうと思います。

2014/03/22 追記

作者さんの紹介記事も出たのでリンクしときます。

なるほど。メソッドが大文字でエクスポートされてたのね...(小文字で呼んでて、エラーになって、アレ?ってなってた。。。)

(追記おわり)

FAQ?

便利?

便利ですよ。めっちゃ捗るので是非試して欲しい。

普通に SQL のコマンドラインで良くね?

みんなプログラマなんだから、 SQL より Perl の方が得意でしょ?あとお前それ Oracle 使ってても同じ事言えるの?

...ってのは半分本気で半分ジョークです。Otogiri::Plugin::DeleteCascade を有効にしているので、FK あるテーブルも上から消せるのが最高に気持ちいいです。

Teng で同じ事やりたいんだけど...

.replyrc_myservice の use Otogiri してる所の周辺を書き換えてください。僕最近 Teng 使ってないし。あとは初期化の時に Row Object を無効にしといた方がデータが見やすくて良いかも。

DBIC で同じ事やりたいんだけど...

.replyrc_myservice の(ry

CDBI で同じ事やりたいんだけど...

.replyrc_m(ry



2014-02-27 Otogiri::Plugin::DeleteCascade ってのを書いてみた


多分下記の続きみたいな話。

FK 貼っているテーブルにテストデータをぶっ込んだり、データを何度か入れ直しするのってめんどくさいですよね?

前職では、(Fixture ではなく、)ファクトリ使ってテストデータをぶっ込む仕組みを作っていたので、FK のあるテーブルの データを入れるのは大変でした。(ゴミデータをちゃんと消さないと、外部キーの不整合でエラーになる)。今はモックでテストしてるので、 テストコードでは困ってませんが、普通にデータ入れ直しするときとか、やっぱメンドクサイんですよ。

で、「SQL で親から順番に消してくれる特殊構文とかあればいいのに」と思ったんですが、そういうの無いんですよね。。。(FK に CASCADE オプション つければ出来るけどつけてないから出来ない)

なので、DB のメタデータを引っ張って、FK 検出して、親データを DELETE すると子のデータも一緒に消えるような物体を作ってみた。

例によってSYNOPSIS 丸写しですが、こんな感じ。

use strict;
use warnings;
use Otogiri;
use Otogiri::Plugin;

Otogiri->load_plugin('DeleteCascade');

my $db = Otogiri->new( connect_info => $connect_info );
$db->insert('parent_table', { id => 123, value => 'aaa' });
$db->insert('child_table',  { parent_id => 123, value => 'bbb'}); # child.parent_id referes parent_table.id(FK)

$db->delete_cascade('parent_table', { id => 123 }); # both parent_table and child_table are deleted.

child_table.parent_id -> parent_table.id という FK があるとして、parent_table を消すと、一緒に child_table のデータも 消えてくれる君です。

DBIx::Inspector で FK の情報が引っ張っているので、対応してる DBMS(MySQL, PostgreSQL, SQLiteあたり)なら動くと思います。

アプリで使うってよりも、開発環境でちょっとデータメンテしたい、とかテストデータのロードする前にクリーンアップしたい、とかそういう 用途を想定してる感じです。(というか、多分この物体はアプリで使ってはいけない)

気が向いたら CPAN にアップするかもです。

2014-03-12 追記

気が向いたので、CPAN にアップしました。