2007-01-07 Sun

 DBICのリレーションシップ

やっぱり、DBIx::Class::Relationshipを読み直すことにした。
こういうのをちゃんと理解しておかないと足をすくわれるし。
ちょっとづつ書いたら、何日もかかった。ションボリ。

まずは前提となるデータ

こんな感じで、Authorテーブルと、Bookテーブルがあるとする。
ID | Name | Age
 ------------------
   1 | Fred | 30
   2 | Joe | 32

  ID | Author | Name
 --------------------
   1 | 1 | Rulers of the universe
   2 | 1 | Rulers of the galaxy

リレーションシップなしの場合

もしもリレーションシップを使わないときに、Fredの書いた全ての本を取
り出すとしたら、以下のようになる。
my $fred = $schema->resultset('Author')->find({ Name => 'Fred' });
my $fredsbooks = $schema->resultset('Book')->search({ Author => $fred->ID });

2行書かなきゃいけなくて、理解はしやすいかも知れないけど面倒すぎ。
リレーションシップありの場合

リレーションシップを使えば1行ですむ。
今回の場合は「Author上でbooksという名前のhas_manyなリレーションシッ
プを宣言」しておけば、以下のようにできる。(細かいことは後)
my $fredsbooks = $schema->resultset('Author')->find({ Name => 'Fred' })->books;

確かに手間が半分以下になる。すごい。
リレーションシップって何??

各リレーションシップは、 テーブルのアイテムたちを構成する
DBIx::Class::Manual::Glossaryオブジェクト
の"Row"がもつ、アクセサメソッドをセットアップする。
RowオブジェクトはResultSetオブジェクトから返ってきた実際のデータ。
DBIx::Class::Manual::Glossaryオブジェクトの"ResultSet" より、
search_relatedを使って、リレーションシップたちは検索されることが可能になる。
ResultSetオブジェクトはテーブルオブジェクトのこと。データそのもの。
リレーションシップの利点

リレーションシップは
リストコンテキストでは、リレーションはRowオブジェクトたち
のリストを関連するクラスに返す。
スカラーコンテキストでは、JOINされたテーブルを表現する新し
いResultSetを返す。

だから、クエリ圧縮のためのリレーションの連鎖呼び出しができる。
従って、実際にデータベースにクエリが投げられるのは、
実際のアイテムのためのデータを検索する必要に迫られたときで、
それまでは一切無駄な時間を使わない。

例えば、以下のようにsearch_relatedメソッドを使うと、
以下のような1行のSQLを発効する。
 my $cheapfredbooks = $schema->resultset('Author')->find({
   Name => 'Fred',
 })->books->search_related('prices', {
   Price => { '<=' => '5.00' },
 });

 SELECT * FROM Author me
 LEFT JOIN Books books ON books.author = me.id
 LEFT JOIN Prices prices ON prices.book = books.id
 WHERE prices.Price <= 5.00

複数回のfetcheなしでいける。
つか、リレーションシップを記述する利点は豊富なヘルパーメソッド

このとき、どこかでpriceという名前で、Bookテーブルと値段の書いたテー
ブル(多分Priceテーブル)との間のリレーションを記述しておく必要がある。

リレーションシップを記述すると、search_relatedなどのヘルパーメソッドが使
えるようになる。
search_relatedメソッド以外にも、頻繁に使う基本的なヘルパーなメソッドが用意されている。

search_relatedメソッド以外の、頻繁に使う基本的なヘルパーメソッドは
DBIx::Class::Relationship::Baseにあるから、そっちを見る。
(add,regist,search,count,create,find,update,set,delete,add,remove・・・)
まー、何か大体あるなー。

で、実際のリレーションシップに使うメソッドを見てね。

ヘルパーメソッドどもの使い方について


すべてのヘルパーメソッドは以下のような引数を取得する。
_PACKAGE__>$method_name('relname', 'Foreign::Class', $cond, $attrs);

$condと$attrsはオプションだから、何も書かなくてもいい。
どうしても何かを書きたくて、かつ無記入と同じデフォルトの値を渡したいときは、
undefを渡せばいいよ。

ヘルパーメソッドはさておき、リレーションの記述方法


実際にリレーションを記述するのは、テーブルスキーマを記述するクラス。

例えば、DB用のスキーマと、テーブルのスキーマを用意する。
package My::DBIC::Schema;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class::Schema/;
__PACKAGE__->connection('dbi:mysql:testapp', 'id', 'pass');
__PACKAGE__->load_classes(qw/Books Authors ISBN/);
1;

MySQLにtestappという名前のDBをcreateした状態。
idとpassには接続に必要なものを書く。
つか、いい加減だからコピペしても動かないと思う。

さらに、テーブルのスキーマ。
package My::DBIC::Schema::Book;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('book');
__PACKAGE__->add_columns(qw/id author name/);
__PACKAGE__->set_primary_key('id');
1;
__END__

package My::DBIC::Schema::Author;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('author');
__PACKAGE__->add_columns(qw/id name age/);
__PACKAGE__->set_primary_key('id');
1;
__END__


まー、多分こんな感じ。
説明とスキーマ内のテーブルとかアイテムの名前が違うけど、そこはそれ。
何書いているのか良く分からない人は、WEB-DBマガジンVol.36のnaoyaさ
んの連載を見ると、へえ、と分かると思います。

これらのものが少なくともある状態で、以下につづく。
has_one

Arguments: $accessor_name, $related_class,$foreign_key_column|$cond?, $attr?
引数:アクセサ名、関連付け対象のクラス名が必須。必要ならその他の引数も。

どこかにBookテーブルのidカラム(PK)と対応付けられている、
ISBNテーブルがあって、確実にBookとISBNが1対1対応すると分かっている
ときの例は以下のような感じ。My::DBIC::Schema::ISBNは省略。
package My::DBIC::Schema::Book;
・・・
__PACKAGE__->has_one(isbn => 'My::DBIC::Schema::ISBN');

My::DBIC::Schema::Bookに追記するってことですよ。

こういうリレーションシップを書いておくと、
isbnを呼び出してResultSetを得たり、ヘルパーメソッドを使えたりする。
あるクラスと他のクラスの1対1のリレーションシップを作ったことになる。
my $schema = My::DBIC::Schema->connect;
my $obj = $schema->resultset('Book')->find(1);
my $isbn_obj = $obj->isbn; # to get the ISBN object

この場合、スカラーコンテキストなのでResultSet(データ)が返ってくる。
返ってきたResultSetは、BookテーブルとISBNテーブルがINNER JOINされ
たもの。

JOINする際に「対応するオブジェクトが必ずある」という暗黙の了解がな
い場合は、might_haveを使うと良い。
might_haveの場合には、LEFT JOINされたResultSetが返ってくる。
has_oneとmight_haveの違いは、それだけ。
INNER JOINの場合は、対応関係が取れないデータは無視するし、
LEFT JOINの場合には対応関係が取れない場合に、対応先として、
とりあえずnullを挿入してJOINしてくれる。

JOINするときに使われるキーについては、might_haveの説明中に書く。
ちなみに、上のリレーションシップの場合は、
My::DBIC::Schema::ISBNのisbnカラムか、My::DBIC::Schema::ISBNのPKが、
My::DBIC::Schema::BookのPKと対応していると考えられる。
might_have

あー、えーと、Pseudonymテーブルが出てきますけど記述は省略。

Arguments: $accessor_name, $related_class,$foreign_key_column|$cond?, $attr?
引数:アクセサ名、関連付け対象のクラス名が必須。必要ならその他の引数も。
package My::DBIC::Schema::Author;
・・・
__PACKAGE__->might_have(pseudonym => 'My::DBIC::Schema::Pseudonym');

このようなリレーションを記述することで、以下のようにできる。
my $schema = My::DBIC::Schema->connect;
my $obj = $schema->resultset('Author')->find(1);
my $pname = $obj->pseudonym; # to get the Pseudonym object

で、JOINするときのキーが一体何なのか。
これが知りたくて英語ドキュメントを読み直したんだけど、
ちゃんと書いてありましたよ。
JOINするときのキーは何なのか

My::DBIC::Schema::Author->might_have(pseudonym =>'My::DBIC::Schema::Pseudonym')


上の例のMy::DBIC::Schema::Authorへの記述は、
クラスに対する任意の1対1のリレーションシップを作る。
これはhas_oneとmight_haveで共通のこと。

「Authorはmight_haveだ。Pseudonymを。」という関係。
つまりAuthorがPseudonymを所有している。
PseudonymがAuthorを所有するかなー?と思うので、
この場合、AuthorはPseudonymのPKを知らないけれど、
PseudonymはAuthorのPKを知っている状態ということになる。

まー、もちろん、AuthorがPseudonymのPKを知っていて、
互いに所有しあっていても問題はない。
ポイントは所有される側が、所有する側のPKを知っているか、
ということだろう。
これって、何か哲学的だなー。

例えば主人と犬がいて、犬は主人のPKを知らなきゃいけないけれど、
主人は犬のPKを知っていても知らなくてもいいってことだよなー。
ふかいー。子供が泣くから親は子を育てるんですねー。

あ、脱線した。

リレーションシップは標準ではアクセサ名(例、pseudonym)を、
関連付けされたクラス(例、My::DBIC::Schema::Pseudonym)中の外部キー
として、JOINを解決するために使う。
リレーションの記述の3つめの引数は、$foreign_key_columnか$condだが
例外は、関連付けされたクラスに$foreign_key_columnに明記されたカラムが
ある場合や、$condがJOIN条件を示すハッシュへの参照を示す場合だ。

リレーションシップは
- 関連付けされたクラスに$foreign_key_column(外部キーのカラム)と明記されたカラムがあるか
- JOIN条件を示すハッシュ$condがあるか
を調べて、JOINの対応を解決しようとする。
そもそも$foreign_key_columnか$condがなければ、
- 関連付けされたクラスに、アクセサ名と同じ名前のカラムはあるか
を調べてくれる。もしカラムがあればJOIN時につかわれる。
それでも駄目なら、2つのテーブルはPKを共有していると見なすようだ。

なるほどねー。あとで試してみよう。やっと分かったよー。

つまり、上述のAuthorとPseudonymのリレーションシップでは、
AuthorにPseudonym関連の記述がないことは分かっているので、
PseudonymのpseudonymカラムかPKが、AuthorのPKと一致していないと、
期待通りの結果を得られないことがわかる。

PseudonymのauthorというカラムにAuthorのPKが入っているときには、
以下のように書いてあげれば、期待通りのJOINの解決がおこなわれる。
これが、$foreign_key_columnの設定。
この場合、PseudonymのauthorカラムをJOINに使う。

My::DBIC::Schema::Author->might_have( pseudonym =>
					 'My::DBIC::Schema::Pseudonym',
					 'author' );


さもなくば、こう書く。これがJOIN条件を示すハッシュ$cond。

  My::DBIC::Schema::Author->might_have( pseudonym =>
					 'My::DBIC::Schema::Pseudonym',
					 { 'foreign.author' => 'self.author' } );


この場合、foreign(My::DBIC::Schema::Pseudonym)のauthorカラムと
self(My::DBIC::Schema::Author)のauthorカラムをJOINの解決に使ってくれる。
JOINの解決に呼び出し側のPKを使わない場合はこれだね。

結局has_oneとmight_haveとbelongs_toは、それぞれ理解すると理解が深
まるので、上を見たり下をみたりするといいかも。
belongs_to

では、belongs_to。基本はhas_oneやmight_haveと同じ。

Arguments: $accessor_name, $related_class,$foreign_key_column|$cond?, $attr?
引数:アクセサ名、関連付け対象のクラス名が必須。必要ならその他の引数も。

  # in a Book class (where Author has many Books)
  My::DBIC::Schema::Book->belongs_to( author => 'My::DBIC::Schema::Author' );
  my $author_obj = $obj->author; # get author object
  $obj->author( $new_author_obj ); # set author object


「Bookは所有されている。Authorに。」という関係。
AuthorがBookの親であるという関係ですね。

AuthorがBookのPKをどのカラムにも持っていないときに使う。
つまり呼び出される側が呼び出す側のPKを持っていない場合が、
belongs_toとなる。

呼ぶ側のクラスが呼ぶ側のクラス自身のカラムの一つ(または複数)に、
呼ばれる側である外部クラスのプライマリキー(PK)を保存している、とい
うリレーションシップを作る。

上の例の場合、
呼ぶ側クラスから見た外部クラスは「My::DBIC::Schema::Author」で、
呼ぶクラス「My::DBIC::Schema::Book」は、コラムのどこかにAuthorのPK
をもっているということだ。

ふむふむ。

上述の例だと、$foreign_key_columnや$condが無いので、
Bookのauthorカラムを使おうとするわけだ。

ちなみにbelongs_toは1対1、1対多、多対多の関係のどれでも使われる。
has_oneやmight_haveは必ず1対1ね。

$foreign_key_columnや$condの使い方は同じ。

  My::DBIC::Schema::Book->belongs_to( author=> 'My::DBIC::Schema::Author',
				       { 'foreign.author' => 'self.author' } );

4つめの引数$attって何?

$attの説明をすっ飛ばしてきた。じゃあ、理解してみよう。

has_oneやmight_haveは、そのようなリレーションシップにおけるオブジェ
クト(JOINされたデータ)を作るためにあるんだけれど、
そのオブジェクトにdeleteしたりupdateしたりしたときに、
標準では関連付けされた基のオブジェクトもdeleteされたりupdateされ
たりする。これが嫌なときは$attを使う。

関連づけされた基のオブジェクトへの作用を切るには以下のようにする。
My::DBIC::Schema::Author->might_have( pseudonym =>
					 'My::DBIC::Schema::Pseudonym',
					 'author', {cascade_delete => 0} );

こうすることで、DBレベルのdaleteやupdateの制約を無効化できる。へえ。

belongs_toの$attではJOINの方法を指定できる。
どうもbelongs_toの標準はINNER_JOINのようだ。
そうすると、$obj->authorして、Bookのauthorカラムを見たときに、
Authorのauthorカラムと対応するものがないときに、
その対応するものがないauthorカラムの要素はJOIN時に無視される。
そうではなくて、Bookのauthorカラムに対応する要素がないなら、
対応要素の代わりにNULLをいれて欲しい場合もある。
そんな時は、以下のようにしてleft_joinだよ、と思えてあげればいい。
  # in a Book class (where Author has_many Books)
  __PACKAGE__->belongs_to(author => 'My::DBIC::Schema::Author',
			   'author', {join_type => 'left'});

また、belongs_toでは標準で「cascade_delete => 0」になっている。
なので、DBレベルのdaleteやupdateの制約を有効にしたければ、
「cascade_delete => 1」と指定してあげればいい。
has_many

はぁ、疲れてきた。has_manyか。

Arguments: $accessor_name, $related_class,$foreign_key_column|$cond?, $attr?
引数:アクセサ名、関連付け対象のクラス名が必須。必要ならその他の引数も。

has_oneじゃなく、1対多のhas_many。
「こいつはもってる。たくさんのあれを。」な関係。
今回は著者と本の関係。一人の著者が沢山の著書をもってる場合ですね。

記述はhas_oneとかの時と、そんなに変わっていない。
  # in an Author class (where Author has_many Books)
  My::DBIC::Schema::Author->has_many(books => 'My::DBIC::Schema::Book', 'author');

Authorから見て、複数のbookを持っているんだから、
直感的にbooksという名前のアクセサ名にしたい。
Author - has_many - booksね。

でも、そうするとbooksカラムなんて無いから、
ちゃんと$foreign_key_columnを指定する必要がある。
上述の場合は、BookのauthorカラムとAuthorのPKが対応してる。
もちろん、$foreign_key_columnや$condが無いときには、
アクセサ名を使って呼ばれたクラス(例だとBook)のカラムを探す。
なるほどね。

has_manyなリレーションシップを作ると、
3つのメソッドが作られて、それらを使えるようになる。
上述の例だと、以下の3つが作られる。

- books()
- books_rs()
- add_to_books()

要するに、

- $accessor_name()
- $accessor_name + _rs()
- add_to_ + $accessor_name()

の3つが作られる。

まず、例文を示す。

  # in an Author class (where Author has_many Books)
  My::DBIC::Schema::Author->has_many(books =>
  'My::DBIC::Schema::Book', 'author');

  my $booklist = $obj->books;
  my $booklist = $obj->books({
    name => { LIKE => '%macaroni%' },
    { prefetch => [qw/book/],
  });
  my @book_objs = $obj->books;
  my $books_rs = $obj->books;
  ( $books_rs ) = $obj->books_rs;

  $obj->add_to_books(\%col_data);


3つのメソッドを使っている。
リレーションシップで返ってくるオブジェクトは、
スカラーコンテキストの場合ResultSetだと分かっているので、
books()の使い方については割愛。
# だって、今はリレーションシップが分からないんだもん。

さらに、books_rs()は、単純にResultSetを返してくれる。
ただ、ポイントなのはスカラーコンテキストでも、
リストコンテキストでも、お構い無しにResultSetを返す。

add_to_books()については、詳しくはDBIx::Class::Relationship::Base
を参照してくれと書いてある。
新しいRowアイテムを追加するためにあるメソッドのようだ。
そのため、has_manyは標準ではDBレベルのdaleteやupdateの制約が
有効になっている。無効にしたければ$attに「cascade_delete => 0」
を入れておけばいい。

ちなみに、add_to_の例はこんな感じ。
  my $role = $schema->resultset('Role')->find(1);
  $actor->add_to_roles($role);
      # creates a My::DBIC::Schema::ActorRoles linking table row object
  $actor->add_to_roles({ name => 'lead' }, { salary => 15_000_000 });
      # creates a new My::DBIC::Schema::Role row object and the linking table
      # object with an extra column in the link

あー、なるほどねー。だ。
many_to_many

最後だ最後。many_to_manyいってみよう。

Arguments: $accessor_name, $link_rel_name, $foreign_rel_name, $attr?
引数:アクセサ名、リンクリレーションシップ名、外部リレーションシッ
プ名が必須。必要ならその他の引数も。

どんなときがmany_to_manyなのか。
それはマルチユーザなブックマークとかを考えるといいかも。
複数のユーザと複数のエントリがあって
あるユーザの行に複数のエントリの行が対応していて、
また、複数のユーザの行に、あるエントリの行が対応している。
そんなのをイメージすると、many_to_manyって必要だなと分かる。

実際のところ、このmany_to_manyな関係は例えばBookとAuthorのような、
2つのテーブルがあれば使えるわけではなく、
もう1つ中間テーブルのようなものが必要になる。

UserテーブルとEntryテーブルのmany_to_manyを表現するには、
Bookmarkテーブルも必要ってことですね。へえ。

丁度良いので、WEB-DBマガジンVol,36のBookmarkアプリの
スキーマを使って考えてみようかな。
create table user(
    id int unsigned auto_increment primary key,
    name varchar(32),
    key (name)
) TYPE = MyISAM DEFAULT CHARSET=utf8;

create table entry(
    id int unsigned auto_increment primary key,
    url varchar(255) binary unique,
    title varchar(255),
    created_on datetime,
    key (url)
) TYPE = MyISAM DEFAULT CHARSET=utf8;

create table bookmark(
    user_id int unsigned,
    entry_id int unsigned,
    comment text,
    created_on datetime,
    primary key(user_id, entry_id)
) TYPE = MyISAM DEFAULT CHARSET=utf8;

あー、ふーん。なるほど。

で、many_to_manyを使うまえにhas_manyな関係を記述する。

・userとbookmarkの関係
package Bookmark::Schema::User;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('user');
__PACKAGE__->add_columns(qw/id name/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(
     bookmarks=>'Bookmark::Schema::Bookmark',{
	  'foreign.user_id'=>'self.id'
     }
);
1;

・entryとbookmarkの関係
package Bookmark::Schema::Entry;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('entry');
__PACKAGE__->add_columns(qw/id url title created_on/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(
     bookmarks=>'Bookmark::Schema::Bookmark',{
	  'foreign.entry_id'=>'self.id'
     }
);
1;

・bookmarkとentryの関係、と、bookmarkとuserの関係
package Bookmark::Schema::Bookmark;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('bookmark');
__PACKAGE__->add_columns(qw/user_id entry_id comment/);
__PACKAGE__->set_primary_key('user_id', 'entry_id');
__PACKAGE__->belongs_to(
     entry=>'Bookmark::Schema::Entry',{
	  'foreign.id'=>'self.entry_id'
     }
);
__PACKAGE__->belongs_to(
     user=>'Bookmark::Schema::User',{
	  'foreign.id'=>'self.user_id'
     }
);
1;

で、ここからmany_to_manyにしてみる。
あんまり意味がありませんが・・。

いじくるのは、Bookmark::Schema::EntryとBookmark::Schema::User。
・userとbookmarkの関係にmany_to_manyを追記
package Bookmark::Schema::User;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('user');
__PACKAGE__->add_columns(qw/id name/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(
     bookmarks=>'Bookmark::Schema::Bookmark',{
	  'foreign.user_id'=>'self.id'
     }
);
__PACKAGE__->many_to_many(
	  entries=>'bookmarks', 'entry'
);
1;

・entryとbookmarkの関係にmany_to_manyを追記
package Bookmark::Schema::Entry;
use strict;
our $VERSION = '0.01';
use warnings;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('entry');
__PACKAGE__->add_columns(qw/id url title created_on/);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(
     bookmarks=>'Bookmark::Schema::Bookmark',{
	  'foreign.entry_id'=>'self.id'
     }
);
__PACKAGE__->many_to_many(
	  'users'=>'bookmarks', 'user'
);
1;


Bookmark::Schema::Bookmarkが、Bookmark::Schema::Entryと
Bookmark::Schema::Userの間を取り持ってくれている。

Bookmark::Schema::Userではentriesというリレーションシップを作って、
Bookmark::Schema::Userのhas_manyで記述したbookmarks経由で、
Bookmark::Schema::Bookmarkのuserリレーションシップを呼ぶ。

Bookmark::Schema::Entryではusersというリレーションシップを作って、
Bookmark::Schema::Entryのhas_manyで記述したbookmarks経由で、
Bookmark::Schema::Bookmarkのentryリレーションシップを呼ぶ。

こうすることで、「B::S::Entryから、あるエントリに紐づく複数のユーザ」や
「B::S::Userから、あるユーザに紐づく複数のエントリ」を取得できる。
ああ、まー使えるかも。

many_to_manyでは、アクセサは2つのリレーションシップのようなものをつくり
ともかくアクセサはhas_manyのように、ResultSetやオブジェクトのリス
トを返す。

上の例のBookmarkクラスのように、many_to_manyな関係を作りたい
2つのテーブルの間を取り持つクラスをリンクテーブルクラスと言う。

Bookmark::Schema::Entryの例に着目して、many_to_manyの記述を見る。
package Bookmark::Schema::Entry;
・・・
__PACKAGE__->has_many(
     bookmarks=>'Bookmark::Schema::Bookmark',{
	  'foreign.entry_id'=>'self.id'
     }
);
__PACKAGE__->many_to_many(
	  'users'=>'bookmarks', 'user'
);

package Bookmark::Schema::Bookmark;
・・・・
__PACKAGE__->belongs_to(
     user=>'Bookmark::Schema::User',{
	  'foreign.id'=>'self.user_id'
     }
);

Bookmark::Schema::Entryを見る。Bookmark::Schema::Entryからの視点で
は、Bookmark::Schema::Bookmarkがリンクテーブルクラスと呼ばれ
Bookmark::Schema::Bookmark::Schema::Userは外部クラスと呼ばれる。

many_to_manyだけ、他のリレーションシップと引数が違って
引数 = ($accessor_name, $link_rel_name, $foreign_rel_name, $attr?)
だった。

アクセサ名は今までと同じ。

$link_rel_nameパラメタは、many_to_manyを記述しているクラスのテーブ
ル(例ではBookmark::Schema::Entry)からリンクテーブル(例では、
Bookmark::Schema::Bookmark)へのhas_manyなリレーションシップのアク
セサ名を記述する。

$foreign_rel_nameパラメタには、リンクテーブルから外部テーブルへの
belongs_toなリレーションシップのアクセサ名を記述する。

many_to_manyを使うためには、データを格納している外部テーブル(オリ
ジナルテーブルとも呼ぶ)からリンクテーブルへのリレーションシップと、
リンクテーブルからmany_to_manyを記述したテーブル(エンドテーブルと
も呼ぶ) へのリレーションシップが必要となる。
many_to_manyリレーションシップを呼ぶと合計3つのリレーションシップ
を使うことになる。

many_to_manyリレーションシップでも、
has_manyと同じような感じで3つのメソッドが作られる。

上の例では以下の3つのメソッドがつくられる。

- users();
- add_to_users();
- set_users();

つまり、

- $accessor_name();
- add_to_ + $accessor_name();
- set_ + $accessor_name();

が作られる。使い方は

・add_to_
Arguments: ($foreign_vals | $obj), $link_vals?
  $actor->add_to_roles({ name => 'lead' }, { salary => 15_000_000});
      # creates a new My::DBIC::Schema::Role row object and the linking table
      # object with an extra column in the link

・set_
Arguments: (\@hashrefs | \@objs)
  my $actor = $schema->resultset('Actor')->find(1);
  my @roles = $schema->resultset('Role')->search({ role => 
     { '-in' -> ['Fred', 'Barney'] } } );
  $actor->set_roles(\@roles);
     # Replaces all of $actor's previous roles with the two named


add_to_は追加したオブジェクトからリンクテーブルオブジェクトが作ら
れる。追加したリンクテーブルのカラムは$link_valsで指定できる。

set_は、既存のリンクテーブルオブジェクトをset_で与えたオブジェクト
を入れ替える。既存のリンク関係は全部破棄されて、新しいリンクが作ら
れる。

詳しくはDBIx::Class::Relationship::Base




はー、長い。一応自分が使ったお試しスクリプトも貼っておく。

#!/usr/bin/perl
use strict;
use warnings;
use lib qw(./lib);
use Bookmark::Schema;

my $schema = Bookmark::Schema->connect;
$schema->storage->debug(1);

# 完全に削除
$schema->resultset('User')->search(     {} )->delete;
$schema->resultset('Bookmark')->search( {} )->delete;
$schema->resultset('Entry')->search(    {} )->delete;

my $u_rs = $schema->resultset('User');
my $b_rs = $schema->resultset('Bookmark');
my $e_rs = $schema->resultset('Entry');

$u_rs->create( { name => 'bw4' } );
$u_rs->create( { name => 'satou' } );

$e_rs->create(
    {   url =>
	 'http://gigazine.net/index.php?/news/comments/20070106_firefox_revenue/',
	 title => 'firefox',
    }
);
$e_rs->create(
    {   url   => 'http://www.popxpop.com/archives/2007/01/5firefox.html',
	 title => 'firefox',
    }
);

my $users_rs = $u_rs->search( {} );    # get ResultSet
while ( my $user = $users_rs->next ) { # use iterator(search once)
    print $user->name, ": search\n";
}

my $bw_users_rs = $u_rs->search_like( { name => 'bw%' } );    #LIKE get ResultSet
while ( my $user = $bw_users_rs->next ) {    # use iterator(search once)
    print $user->name, ": search like bw\n";
}

my $tmp_user;

$tmp_user = $u_rs->find( { name => 'satou' } );    # get Bookmark::Schema::User
# $bookmarks will be Bookmark::Schema::Bookmark
my $bookmarks = $tmp_user->bookmarks();    # has_many
while ( my $bm = $bookmarks->next ) {      # use iterator(search once)
    print $bm->comment, ": entry->bookmarks()\n";
}

$bookmarks = $tmp_user->bookmarks_rs();
while ( my $bm = $bookmarks->next ) {      # use iterator(search once)
    print $bm->comment, ": entry->bookmarks_rs()\n";
}

# has_many
$bookmarks = $tmp_user->add_to_bookmarks(
    {   entry_id => 1,
	 comment  => 'hahaha',
    }
);

# has_many
$bookmarks = $tmp_user->add_to_bookmarks(
    {   entry_id => 2,
	 comment  => 'fufufu',
    }
);

# get Bookmark::Schema::Bookmark
my $bookmark = $tmp_user->search_related( 'bookmarks', { entry_id => 2 }, )->first;
my $entry = $bookmark->entry;    # get Bookmark::Schema::Entry

# get ResultSet
my $bookmark_rs = $entry->search_related( 'bookmarks', { entry_id => 2 }, );
while ( my $bm = $bookmark_rs->next ) {    # use iterator(search once)
    print $bm->comment, ": search related\n";
    }

my $entries_rs = $tmp_user->entries();
while ( my $ent = $entries_rs->next ) {    # use iterator(search once)
    print $ent->url, ": entries()\n";
}

$tmp_user->add_to_entries(
    {   url   => 'http://www.popxpop.com/archives/2007/01/5firefox.xml',
	 title => 'firefox!!!',
    }
);
$entries_rs = $tmp_user->entries();
while ( my $ent = $entries_rs->next ) {    # use iterator(search once)
    print $ent->url, ": entries()\n";
}

my @ent_arr = $e_rs->search_like( { title => '%!%' } );
$tmp_user->set_entries( \@ent_arr );
$tmp_user->add_to_entries(
    { url => 'http://overlasting.dyndns.org/', title => 'my blog', } );
$entries_rs = $tmp_user->entries();
while ( my $ent = $entries_rs->next ) {    # use iterator(search once)
    print $ent->url, ": entries()\n";
}


書き始めたときは、あとで読み返すかな?と思ったけど、
記事にしちゃうと理解がおわっていて、後で見返すか不安。ま、いいか。

あと、書き忘れたけど、has(have)とbelongsは表裏一体の関係なので、
片方向からだけでなく、双方向になるように両方とも記述しておくと、
あとで楽ですよっていうか、双方向にするような慣習があるみたい。

はっきりしないところはDBICよりもRuby on Railsの資料を読んだほうが
わかったりするかもしれませんね。

関連エントリ

[2007-01-03-4] DBICのリレーションが良くわからないのでメモ - 1対多
[2007-01-03-3] DBICのリレーションが良くわからないのでメモ - 1対1
[-] 3
投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2007-01-03 Wed

DBICのリレーションが良くわからないのでメモ - 1対多

[2007-01-08]:追記
[2007-01-07-0]で、DBICのリレーションシップについてのエントリを
書きました。以下は非常にいい加減なので[2007-01-07-1]をどうぞ。




よく分からない1対多のhas_manyと、belongs_toの使い方を理解したい。
引き続き、以下の文献をグルグル読みながら理解してみる。。

101号室より愛をこめて: [DBIx::Class][DBIC][perl] DBIx::Classのリレーション
Perl/DBIC - Nekokak's core dump
DBIx::Class::Manual::Cookbook
DBIx::Class::Relationship
カエルチュウイホウ - DBICで多対多の設定を
カエルチュウイホウ - DBICで多対多

以下に出てくるコードとかは参照先を書いてませんが、
上の参考記事のどこかから持ってきました。ありがとうございます。

Userテーブルと本テーブルがあって、Userが本を各自多数持っている状態を考えると、いい感じかな。

基本的なことは、1対1と同じ。
CREATE TABLE User (
  user_id text primary key,
  password text,
  ....
);
CREATE TABLE Book (
  book_id integer primary key,
  user_id text,
  name text,
  author text,
  ....
);

こんなときに、
My::DBIC::Schema::User->has_many( books =>'My::DBIC::Schema::Book', "user_id" );


My::DBIC::Schema::Book->belongs_to( user_id =>'My::DBIC::Schema::User' );

のように記述できる。

前者は$user->booksで、ユーザが持っている本のリストを得られて、
後者は$book->user_idで、着目している本を持っているユーザのリストを得られるのだ。

うーん、なんか不十分な気がするので、もうちょっと例を探そうかな。
いや、ドキュメント中心にちゃんと読もう。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2007-01-03 Wed

DBICのリレーションが良くわからないのでメモ - 1対1

[2007-01-08]:追記
[2007-01-07-1]で、DBICのリレーションシップについてのエントリを
書きました。以下は非常にいい加減なので[2007-01-07-1]をどうぞ。




よく分からないというか、has_oneとmight_haveとbelong_toの明確な違い
が分からなくて、どうすれば一生間違いなく理解できるかなと、

これらを見て、理解することから始めた。
101号室より愛をこめて: [DBIx::Class][DBIC][perl] DBIx::Classのリレーション
Perl/DBIC - Nekokak's core dump
DBIx::Class::Manual::Cookbook
DBIx::Class::Relationship
カエルチュウイホウ - DBICで多対多の設定を
カエルチュウイホウ - DBICで多対多

以下に出てくるコードとかは参照先を書いてませんが、
上の参考記事のどこかから持ってきました。ありがとうございます。

リレーションはテーブル間にリファレンスを張るようなもの。
JOINでテーブルをくっつけてくれる。

has_oneとmight_hasとbelongs_toはテーブルAの行とBの行の間に1:1対応の関係

has_one : INNER JOIN(一致する行のみを持ってくる)
might_have : LEFT JOIN(一致しない行には、片側にNULLを付けて持ってくる)
CREATE TABLE User (
  user_id text primary key,
  password text,
  ....
);
CREATE TABLE Profile (
  user_id text primary key,
  name text,
  mail text,
  ....
);

こういう風に互いに相手のprimary keyがどこかに入っているときには、
has oneでリレーションを記述できる。
My::DBIC::Schema::User->has_one(profile=>'My::DBIC::Schema::Profile' );




My::DBIC::Schema::Profile->has_one( user=>'My::DBIC::Schema::User' );


のようにできる。

こんな風にリレーションを記述しておくと、$user->profileみたいにアクセスできる。
この場合Primary Keyを使ってJoinしてる。

もちろん、相手のPrimary Keyが自分のPrimary Keyじゃなくても良い。

以下のように、Primary Keyが共有じゃないけど、普通のカラムには持っているときには、
CREATE TABLE User (
  user_id text primary key,
  profile_id text,
  password text,
  ....
);
CREATE TABLE Profile (
  profile_id text primary key,
  user_id text,
  name text,
  mail text,
  ....
);


My::DBIC::Schema::User->has_one(profile=>'My::DBIC::Schema::Profile', 'user_id' );


My::DBIC::Schema::Profile->has_one( user=>'My::DBIC::Schema::User', 'profile_id' );

のようにできる。

つまり、has_oneはINNE RJOINする相手のテーブルが、自分のPrimary Keyを
Primary Keyか普通のカラムのどれかに持っていることをあらわしている。
might_haveはLEFT JOINする以外はhas_oneと同じ。

でも、都合よく相手が自分のPrimary Keyを持っていないときもある。
CREATE TABLE User (
  user_id text primary key,
  password text,
  ....
);
CREATE TABLE Profile (
  profile_id text primary key,
  user_id text,
  name text,
  mail text,
  ....
);

このときUserから見ると、ProfileはUserのPrimary Keyをもっている。
けど、Profileから見ると、UserはProfileのPrimary Keyをもっていない。

なので、
My::DBIC::Schema::User->has_one( profile =>'My::DBIC::Schema::Profile', "user_id" );

これはできそうだけど、逆は???って感じ。

こういうときにbelongs_toを使う。

使い方はこんな感じ。
My::DBIC::Schema::Profile->belongs_to( user_id =>'My::DBIC::Schema::User' );

Profileは、Userのアクセサ名テーブルか、
UserのPrimary Keyに従ってJOINするよ、という解釈ができる。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-12-30 Sat

SVK-2.0

SVK-2.0だー。

Chia-liang Kao (高嘉良) / SVK-v2.0.0 - search.cpan.org

いろいろ落ち着いたら入れてみよ。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-12-27 Wed

mod_perl2をコンパイルできない症候群

まー、なんというか、どんなに頑張ってもmod_perl2のコンパイルができない。
多分、何かがいけないのだろう。2ヶ月くらい前も駄目だったな。
エラーメッセージで検索しても、解決につながる情報が引っかからないので
ちょっとお手上げ。近いうちに違う環境でコンパイルを試みてみる。
それでコンパイルできたら今の環境が悪いということだよな。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-12-18 Mon

CGIの%ENVから得られる環境変数

とあるCGIプログラムにアクセスがあったとき、
そのCGIプログラム中の%ENVから得られる環境変数についてのメモ。

環境変数の研究 'PERL-LABO'

環境変数名通称データの意味
HTTP_ACCEPT_LANGUAGE言語設定
HTTP_REFERERリファラリンク元ページのURL
HTTP_USER_AGENTユーザエージェントOSやブラウザの情報
REMOTE_ADDRリモートアドレスIPアドレス
REMOTE_PORTポート番号クライアントマシンが通信に使っているポート

$ENV{'HTTP_REFERER'}は頻繁に使いますよね。
Apacheのログを見ることができない場合には、
こういう情報だけでも活かせないか考えて、いろいろ試したいものです。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-12-18 Mon

アクセスされたら画像をブラウザに出力するCGI

アクセスがあったときに、指定した画像をブラウザに出力するCGIについてのメモ。

以下のaccess_img.cgiの場合は、access_img.cgiにアクセスすると
access_imgcgiと同じディレクトリ内のtest.pngをブラウザに表示します。

access_img.cgi


#!/usr/bin/perl

use strict;
use warnings;

my $file= './test.png';
my $type = 'png';

#オープン
open(IMG, "$file") or die;

#表示

binmode IMG;
binmode STDOUT;
print "Content-type: image/$type\n\n";
print while (<IMG>);

#クローズ
close(IMG);
exit(0);


超簡単ですね。

こういうCGIプログラムをちょっと発展させて画像生成を組み合わせると、
応用範囲が一気に広がりますね。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-12-10 Sun

Perlの戻り値におけるショボいはまり方

昨日、2時間位発見できなかったションボリなミス。

一部のサブルーチン内の処理の末端のひとつにreturnを
表記していなかったら、そのサブルーチンからの戻り値が
文字だったり、0だったり、1だったりして泣きそうになった。
テストも原因不明で通ったり通らなかったりするので死ねる。

結論としては、全ての処理の末端で返す値が無くても、
「return 0」か「return ''」は書こうよ。ということで。

そういえば、これ系の話はPerlベストプラクティスに書いてありましたね。


投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-12-08 Fri

Perlの代入におけるショボいはまり方

今日、30分位発見できなかったションボリなミス。

if ($num =~ m|^(.+>).*?$|) {
    $num =~ $1
}


$numに$1の内容が全然代入されなくて、
正規表現を、あーでもない、こーでもないといじって、
駄目だーと諦めてトイレに行って帰ってきたら気がついた。

代入されるわけないじゃん。ショボーン。何故こんなことに。
# =~ じゃなくて = にしないと!!

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-12-06 Wed

Perlデータマンジング

Perlに関する本は結構買っているのですが、
Perlデータマンジングは忘れたころに再読すると良い感じです。



今日も久々に読んでみたのですが、これは使える、と思うことが
2、3見つかりました。正確には今日、理解しました。遅すぎ。

同じことは、他のPerl本にも書いてあると思うのですが、
Effective Perlのように盛り沢山ではなく、
Perlクックブックのように分厚くなく、
テーマをしぼってコンパクトにまとまっているので、
ちょっとの空き時間にザッと読めるのが良いですね。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-11-29 Wed

TestTest

Test::Base最高。というかTest最高。

理想の出力が最初は分かっていないような処理でも、
Testがあれば気軽に実装をはじめられる。

pmsetupでモジュールとテストの雛形を作って、
モジュールに単なる0/1を送るサブルーチンを実装して、
Test側からサブルーチンを呼び出してnot_okをだして、
Test側のInputデータを本番のものに入れ替えて、
サブルーチンを入力して出力を理想に近づけて・・・

目の前の目標が見えなくても、目の前に気になる失敗(not_ok)があると
それを乗り越えようとして頑張れる。そういうとこが好き。

Testなしで実装したほうが実装は早いと思ってたものも、
行き詰ると結局テストを書いたりする場合があるので、
そこらへんを上手に予測できると良いなー。
まだ、慣れが足りないのかも。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-11-23 Thu

SubversionのDBが壊れたときの修復スクリプト

SubversionのDBが壊れたときに、svnadmin recoverでは回復しなかったので
スクリプトを書いて解決しました。

以前[2006-10-03-1]にSubversionのDBが壊れてしまったときに、DBを再構築したのですが
それはSubversionを使い始めたばかりだからです。
今ではリポジトリのサイズが300Mbyteを超えていて、
再構築するのが非常に面倒そうです。

なので、真剣に修復する方法を探したら、方法が見つかりました。
以下のページに書いてあった方法を試したのです。

Subversion Users: Re: DB Corruption

一回全てのdbファイルとlogファイルを消して、
bdbなファイルを全てダンプしてロードするのです。
そうすると直ってしまうのです。

以下が、SubversionのDBを修復するスクリプトです。自己責任でどうぞ。

recover.sh


#!/bin/sh

mv log.* /tmp
mv __db.* /tmp

for bdb in changes copies nodes representations revisions strings transactions uuids lock-tokens locks
do
  mv $bdb $bdb.org
  db4.2_dump $bdb.org | db4.2_load $bdb
done

svnadmin recover /path/to/svnrepos/
svnadmin verify /path/to/svnrepos/


このスクリプトを/path/to/svnrepos/db/へ設置して実行してひたすら待つのです。
「db4.2_*」は自分の環境で使えるBerkeley DB関連のコマンドに
置き換えてご利用ください。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-11-10 Fri

GD::Graphでy軸の値を昇順じゃなくて、任意の降順にできないか

GD::Graphでy軸の値を昇順じゃなくて、任意の降順にできないかと、
思ってGD::Graphをいじりまくった。

結果、さまざまなオプションに対する理解が深まり、
グラフを混合できるようになったりしたものの、
タイトルにあるようなことはできなかった。

何がしたいかって、前提としてxとyは正の整数。
で、y軸とx軸がクロスするところのyの値を80000とか1000000にして、
y軸に関する値が値が増えると0に近づくようにしたい。
でもx軸がグラフを描画する領域の下にあってほしい。
なんじゃそりゃー。でも必要なんです。

これはy軸に関する値に-1をかけて負の値にすれば近いことはできるけど、
y軸に表示されるラベルにさらに-1をかけなきゃいけないし、
x軸がグラフを描画する領域の上部に出るようになってしまう。

ということで悩みまくりんぐでしたが、一旦あきらめた。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-11-07 Tue

perltidy で Perl Best Practiceなコーディングスタイルにコードの整形

Perl Best Practice(Perlベストプラクティス)の洋書版を買って、
.perltidyrcを即記述したのだけれど、ChangeLogに転記し忘れていた。

perltidyの整形ルールを書く$HOME/.perltidyrcに以下のような記述をすると、
perltidyがPerl Best Practiceなコーディングスタイルにコードを
整形してくれちゃう。すばらしすぎ。

-l=78  # Max line witdh is 78 cols
-i=4   # Indent level is 4 cols
-ci=4  # Continuation indent is 4 cols
-st    # Output to STDOUT
-se    # Errors to STDERR
-vt=2  # Maximal vertical tightness
-cti=0 # No extra indentation for closing brackets
-pt=1  # Medium parenthesis tightness
-bt=1  # Medium brace tightness
-sbt=1 # Medium square bracket tightness
-bbt=1 # Medium block brace tightness
-nsfs  # No space before semicolons
-nolq  # Don't outdent long quoted strings
-wbb="% + - * / x != == >= <= =~ !~ < > | & >= < = **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x="
	# Break before all operators


これで安心。


投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-11-06 Mon

素数を求めるアルゴリズム

ちょっと用事で任意の整数までの素数を求めるPerlプログラムを作った。

戦略は1つ。
・nが素数かどうか素数と総当りするときは√n以下の素数までで終了。

#!/usr/bin/perl

use strict;
use warnings;

my $limit  = $ARGV[0];
my $count  = 0;
my $layout = 0;
my @sosuus;
for ( my $i = 1; $i <= $limit; $i++ ) {
    my $flag = 0;
    if ( 1 == $i ) { $flag = 0; }    # 1はnot素数
    else {
	 unless (@sosuus) { $flag = 1; }
	 foreach my $sosuu (@sosuus) {

	  # チェックすべきは素数の二乗(√$i)以下の素数まで
	     if ( ( $sosuu * $sosuu ) > $i ) { last; }
	     my $r = $i % $sosuu;
	     unless ($r) {
		 $flag = 0;
		 last;
	     }
	     $flag = 1;    # 過去の素数のn倍じゃなければ素数
	 }
    }
    if ($flag) {
	 print("$i ");
	 push @sosuus, $i;
	 $count++;
	 $layout++;
	 if ( 10 == $layout ) {

	     print("\n");
	     $layout = 0;
	 }
    }
}
print("\n素数は$count個\n");


実行例は以下のとおり。

% ./sosuu.pl 1000
2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173
179 181 191 193 197 199 211 223 227 229
233 239 241 251 257 263 269 271 277 281
283 293 307 311 313 317 331 337 347 349
353 359 367 373 379 383 389 397 401 409
419 421 431 433 439 443 449 457 461 463
467 479 487 491 499 503 509 521 523 541
547 557 563 569 571 577 587 593 599 601
607 613 617 619 631 641 643 647 653 659
661 673 677 683 691 701 709 719 727 733
739 743 751 757 761 769 773 787 797 809
811 821 823 827 829 839 853 857 859 863
877 881 883 887 907 911 919 929 937 941
947 953 967 971 977 983 991 997
素数は168個


forが2重になっているので100000あたりから重くなる。
作っていて頭悪いなぁ、もっと勉強しないとなぁ、と思った。
週末にちょっと勉強して、もう少し頭よく書きたい。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-11-05 Sun

SpeedyCGIを使ってみた

ちょっと重たいスクリプトを連続して使うためにSpeedyCGIを使った。

SpeedyCGIを使うとSpeedy_baseというプロセスが、
mod_perlとApacheを連携したときのように一度実行したPerlスクリプトを
コンパイルした状態でメモリに保持してくれる。
なので、スクリプトに変更が無ければ繰り返し高速に実行できる。

使い方は極めて簡単。以下メモ。




1、SpeedyCGIをインストールする
CPANシェルで「install CGI::Speedy」とするだけ。

2、PerlスクリプトをSpeedyCGIを使うように変更する。
PerlスクリプトをSpeedy_baseに読み込むために、
Perlスクリプトの行頭で/usr/bin/speedyを使うように変更する。

#!/usr/bin/perl


から

#!/usr/bin/speedy -- -t20 -M1


のように変更する。

3、実行
普通のPerlスクリプトと同じように実行するだけ。




speedyはSpeedyCGIをインストールすると使えるようになる。
オプションも多いのでマニュアルをじっくり読む必要がありますが、
-tと-Mが一番重要。

-tオプションで指定した秒数以上Speedy_baseにリクエストしないと、
Speedy_baseがメモリを開放するし、-Mオプションで指定した数以上に
Speedyプロセス(Speedy_baseを利用するクライアント)は立ち上がらない。
らしい。正直使い慣れてはいないので今後要勉強。

SpeedyCGIの実行速度は使ってみて分かったけど激早い。
今後も使うべきときに使おうと思う。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-10-26 Thu

続・はじめてPerl 改訂版

続・はじめてPerl 改訂版を書店で購入し、軽く目を通した。



全体的に説明がやさしくなったと思う。素直な文が多い。
改定前と同じ部分の日本語が全然違って感動した。

頭に入りやすい気がする。
OOについては、50ページくらい割かれていて、分かりやすさアップ。
テストに関してはTest::Moreを使って、ちょっと詳しく解説してる。
# 軽すぎですね。

続・はじめてのPerlには、まだ記憶してないトピックが多いので
しばらく卒業できそうもないなぁ。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-10-25 Wed

Test::Baseを使い始めた

Test::Baseを使い始めた。

まず、miyagawaさんのTest::Baseの資料、use Test::Base;と、
Ingy dot Net氏の資料、Building Data Driven Test Frameworksを見た。
Shibuya.pm行きたかったなぁ、と思いながら資料を見ていたら
Test::Baseってスゴイ便利そうだと思って、いじってみて感動。
Test::Baseのマニュアル翻訳版も良かった。

miyagawaさんの資料によると、
Test::Baseを使ってなめらかに開発できるかを考えることが、
早い作業で良いAPIをもったモジュールを作ることになるようだ。
実装するものの性質によるけど、データを加工するためのモジュールや、
こういう感じだよなぁ、という入力と出力がイメージできる実装に
積極的に使いたいなと思った。

もっと前に、Test::Base 最高!Ingy と結婚したい!とか読んだときに使い始めるべきだった。

Test::Baseはやれることが多い。多すぎて使わないと忘れる。
しかも、これで良いのかな?と使っていて不安になる。
そんなときにはPerl Testingが良いのかぁ、と思って発注した。


投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-10-09 Mon

P.pm便利

デバッグ時の出力っていろいろ面倒だけど、
どうしてるって話が盛り上がって収束したようだ。

naoyaグループ - naoyaの日記 - P

perl の組み込みの warn が対象がデータ構造とかオブジェクトだったら
それシリアライズして dump してくれりゃいい


"print Dumper"と"warn"は8文字しか違わないけれど、
デバッグがとても楽になった。
早速重宝してます。

P.pmの圧縮されたものをダウンロードして、
解凍して、perl Makefile.pl、make、make installする。
.zshrcなどでPERL5OPT=-MPする。

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |

2006-10-08 Sun

perlプログラミング救命病棟

Perlプログラミング救命病棟を再読してみた。

身についていることと身についていないことがあるな。

これを読んでいて思ったのは、Perlに限らず
プログラミング言語はノートを書きながら勉強したほうが
効率が良いということ。

ノートを書かないと次から次に忘れてしまう。

Perlプログラミング救命病棟
powered by amagami on 2006.10.17
ピーター・J・スコット, トップスタジオ, 伊藤 直也
翔泳社

投稿者:としのり  日時:23:59:59 | コメント | トラックバック |