Hatena::Groupdann

dann's blog このページをアンテナに追加 RSSフィード

Fork me on GitHub

2008-07-21

PerlとJavaでのテストの違い

PerlとJavaでのテストの違い - dann's blog を含むブックマーク はてなブックマーク - PerlとJavaでのテストの違い - dann's blog PerlとJavaでのテストの違い - dann's blog のブックマークコメント

結論からいうと大きな差はないのですが、少しだけ違いがあります。

データドリブンのテストとしてのTest::Baseの存在とxUnit styleのテストが主流ではない事、この2点です。

Test::Base

Javaの世界でのテストと似たものがないのは、Test::BaseとTest::Pod::Coverageかなぁという気がしました。Test::Baseは、データドリブンなテストがとても書きやすく、これは素晴らしいテストモジュールだなぁと思いました。

XUnit style

後、違いがあるのは、テストのスタイルです。CPANモジュールのレベルだと、Test::Moreが割とよく使われていて、xUnit styleはあまり一般的じゃないっていう違いがあります。

個人的な感覚では、アプリケーションというレベルだと、

  • xUnit的なsetup, teardownとか、starup時、shutdown時などの処理が簡単にかけるテストの機構がほしい
  • テストケースで共通な部分を抽象化するのをクラスベースで書きたい
  • テストケースを意味ある単位でグループ化したい

と思ってしまうので、アプリケーションレベルでのテストの機構としては、Test::Moreはちょっと不足してるのかなぁという気がしました。

逆にCPANのモジュールというサイズだと、逆にクラスベースの抽象化機構やsetup, teardownなどの必要性もさほどなく、xUnit styleは大げさなケースも結構ありそうだなぁという気もするので、これは適材適所なのかなという気もします。

Perlでのテストについて

Perlでのテストについては、Mobile-Factoryの方々が書かれた記事もあるので、テストについては、さほど悩まなくてもいいですね。Happy Testing !

http://gihyo.jp/dev/feature/01/test-perl/

Cloud Computingでの多段キャッシュ

Cloud Computingでの多段キャッシュ - dann's blog を含むブックマーク はてなブックマーク - Cloud Computingでの多段キャッシュ - dann's blog Cloud Computingでの多段キャッシュ - dann's blog のブックマークコメント

ふと思ったのだけれど、

http://eucalyptus.cs.ucsb.edu/

はS3互換のAPIを出してるわけで、S3=>Eucalyptus とか多段でクラウドを使うってすればいんじゃないかと思った。信頼性/性能の高い順にクラウドが多段で並べて使うというイメージ。

APIはデファクトを真似られてしまって、複数のクラウドを選択して、多段で使うようになるなんて時代もやってくるかもしれないなぁ。S3が8時間も落ちちゃう今の時代ですぐに使えるレベルにはならないとは思うけど、10年スパンで考えると普通にありえそうな世界。

特にAPIが切りやすそうなストレージの層は、5年くらいで抽象化が進んでしまうかもしれないなぁ。感覚的にはS3のようなものは、どこでも同じような形で使えるようになっていくような気がする。日本でそうなるかはさておき、既に上記のベンチャーも出てきているわけだし、予想されうる未来ではある気がする。

そうなると、レンタルクラウドコンピューティング屋さんってのが10年後あたりにはもっとPopularな分野になっているかもしれないなぁ。今のレンタルサーバー屋さんくらいのの感覚で。どういうクラウドを提供するのかっていうのも、アプリケーションの特性毎にかわるはずで、このクラウドの層もさらに細分化が進んでいくとは思うのだけれど。

脱線しはじめたので、この辺で。

# Net::Amazon::S3のEndpointをEucalyptusに変えるHackをworemacxさんがしてたような。あのパッチとりこんでもらって、psh3llもendpoint切り替えるようにするかなぁ。そうすると、CHI::Driver::S3で、EucalyptusとS3への多段キャッシュがお手軽に。といっても、今みたいに同期でキャッシュするのはさすがに使い方としてはないかもなぁ... けど、コンセプト的には多段クラウドのキャッシュっていうのは、そういうイメージなんだなぁ。

# woremacxさんのは、これかな。Eucalyptusじゃないけれど、修正としては同じ修正でよさそう。

http://blog.woremacx.com/2008/06/use-net-amazon-s3-for-park-place-host.html

次は、DOD or Moco ?

次は、DOD or Moco ? - dann's blog を含むブックマーク はてなブックマーク - 次は、DOD or Moco ? - dann's blog 次は、DOD or Moco ? - dann's blog のブックマークコメント

CatalystとMooseは大体わからないことがあっても、ある程度は中まで手をいれられるようになったかなぁ。使ってるだけなのは、DBIC。

ORマッパは、DBIC、DOD、Mocoなんだけれど、DBICは頑張ってよむのはしんどいなぁというところで、DODかMocoに突っ込んで何かやってみようかなぁという今日この頃。DOD、Mocoは全体的にDBICに比べると大分シンプルで、DBICに比べれば大分手をいれられそうという印象。

ただ、DODもMocoも、どちらも全然情報が無いので、もう少し情報があると始めやすいんだけどなぁ。

サービス開発者の中の分業

サービス開発者の中の分業 - dann's blog を含むブックマーク はてなブックマーク - サービス開発者の中の分業 - dann's blog サービス開発者の中の分業 - dann's blog のブックマークコメント

サービス開発者というのは、もう少し細分化されるようになって、一つはデータに対して意味付けを与える開発者と、それを利用してWebサービスを作る開発者。

どちらの層にも重要になってくるのは、クラウドの層に対する知識があるかないかということ。特にデータに意味付けを与える側は、データ量に対する計算の速度(オーダー)を意識できる必要があって、さらに分散コンピューティングに対する知識が求められるという形になっていく。

今は、機会学習、音声言語、画像解析などのデータに対して意味付けをする分野は研究所にいくかGoogleに行くかという選択肢しかないけれど、大規模データを触れるインフラが整いだすと、少し状況が変わるかもしれない。

日本でもはてながPFIと連携して、データの意味にまで踏み込もうとしていることを考えると、Webアプリケーションの開発という分野も少しずつ変わってきているなぁという気がする。これがどれだけ凄いのかということが、一般の層にはおそらくあまり伝わらないとは思うのだけれど、明らかに半歩先の領域に踏み込んでいると思う。

サービス開発者の中でも少しずつ分業が進んでいく中で、どこの層に対して強みをもって、複数の層を知っている事に強みを出すのか、よりニッチな層に対して攻めるべきかは悩ましいけれど、全体を知りながらサービスをデザインできるようになりたいなぁとは思う。

クラウドコンピューティングで進む分業化

クラウドコンピューティングで進む分業化 - dann's blog を含むブックマーク はてなブックマーク - クラウドコンピューティングで進む分業化 - dann's blog クラウドコンピューティングで進む分業化 - dann's blog のブックマークコメント

EC2とS3の値段がさらに下がると、少し違った未来が見えるかもしれない。クラウドの世界は、今はAmazon、Google, Salesforceの3強だけれど、それにしたって安い。これが5年もたてばもっと下がる可能性は高い。

現在のWebサービスの難しさは、アプリケーションのロジックうんぬんではなくて、バックエンドをいかにスケールさせるかっていうところにかかっていて、コストもそれに大きなコストがかかる。そのため、インフラ側を作るコストとそれによって得られるサービスの利益のバランスがペイしないから、個人でサービスを作る人が数多くでてきにくい状態なのではないかと思う。それゆえに、特にWebの世界では小銭を稼げるモデルが成り立ちにくくなってしまっている。

また、iPhoneをみればわかるように、常時インターネットに接続するモバイルデバイスが、サービスをAPIとして使って、UIはクライアント側で作るという形もより増えるだろう。

こうなっていくと、

  • クラウドを作る側
  • サービスを作る側
  • サービスのUIを作る側

という分業する形になっていくかもしれない。

このような分業化された世界の中で、インフラ側の多くの大変さをクラウド提供側が吸収して、クラウドを提供する世界になると、個人がより世界に影響を与えられる世界がよりやってくるかもしれない。

分業化が進む世界の中で、どこに力をいれて活動すべきなのかというのは悩ましい問題なのだけれど、もっと個人が世の中に何かを問える時代になったらWebはもっと個人にとって面白くなるよなぁと思うのでした。

CHIのDriverでAmazon S3にキャッシュ - CHI::Driver::S3

CHIのDriverでAmazon S3にキャッシュ - CHI::Driver::S3 - dann's blog を含むブックマーク はてなブックマーク - CHIのDriverでAmazon S3にキャッシュ - CHI::Driver::S3 - dann's blog CHIのDriverでAmazon S3にキャッシュ - CHI::Driver::S3 - dann's blog のブックマークコメント

割とさくっといけそうだったので、S3用のDriverを書いてみました。

http://dann.g.hatena.ne.jp/dann/20080721/p6

expire周りの実装がまるで抜けてるのとエラー処理がないのでリリースするにはまだ早いですが、set, get, get_multi系はとりあえず動作します。

package CHI::Driver::S3;
use Carp;
use Net::Amazon::S3;
use Moose;
use strict;
use warnings;

extends 'CHI::Driver';

has 'aws_access_key_id'     => ( is => 'ro', isa => 'Str', reququired => 1 );
has 'aws_secret_access_key' => ( is => 'ro', isa => 'Str', required   => 1 );
has 'bucket_name'           => ( is => 'ro', isa => 'Str', required   => 1 );
has 'retry'                 => ( is => 'ro', isa => 'Int', default    => 1 );
has 'bucket'                => ( is => 'rw' );

__PACKAGE__->meta->make_immutable();

sub BUILD {
    my ( $self, $params ) = @_;

    my $api = Net::Amazon::S3->new(
        {   aws_access_key_id     => $self->{aws_access_key_id},
            aws_secret_access_key => $self->{aws_secret_access_key},
        }
    );
    $self->bucket( $api->bucket( $self->{bucket_name} ) );
}

sub get {
    my ( $self, $key, $options ) = @_;
    croak "must specify key" unless defined($key);
    my $response = $self->bucket->get_key($key);
    return $response->{value};
}

sub fetch {
    my ( $self, $key ) = @_;
    return $self->get($key);
}

sub set {
    my ( $self, $key, $value ) = @_;
    croak "must specify key" unless defined($key);
    return unless defined($value);

    $self->bucket->add_key( $key, $value );
}

sub store {
    my ( $self, $key, $data ) = @_;
    $self->set( $key, $data );
}

sub remove {
    my ( $self, $key ) = @_;
    $self->bucket->delete_key($key);
}

sub get_keys {
    my ($self)   = @_;
    my $bucket   = $self->bucket;
    my $response = $bucket->list_all
        or die $bucket->err . ": " . $bucket->errstr;
    my $keys = [];
    foreach my $key ( @{ $response->{keys} } ) {
        push @{$keys}, $key->{key};
    }
    return $keys;
}

sub expire {
    my ( $self, $key ) = @_;
}

sub expire_if {
    my ( $self, $key, $code ) = @_;
    return 1;
}

sub exists_and_is_expired {
    my ( $self, $key ) = @_;
    croak "must specify key" unless defined($key);

    if ( my $obj = $self->get($key) ) {
        return 1;
    }
    else {
        return;
    }
}

1;

__END__

次はCHI::Driver::TokyoCabinet?

S3問題のあれ

S3問題のあれ - dann's blog を含むブックマーク はてなブックマーク - S3問題のあれ - dann's blog S3問題のあれ - dann's blog のブックマークコメント

CHI::Driver::S3を作ればいいんじゃないかと思った。とはいっても、容量気にする必要がある場合には、画像とかそこそこでかいデータをキャッシングするには、分散ファイルシステムが必要になっちゃうなぁ。

(Local?)->分散ファイルシステム->S3

そこまでいっちゃうと、S3使う意味が殆どないような気もしたりする。

Devel::Coverでカバレッジテスト

| Devel::Coverでカバレッジテスト - dann's blog を含むブックマーク はてなブックマーク - Devel::Coverでカバレッジテスト - dann's blog Devel::Coverでカバレッジテスト - dann's blog のブックマークコメント

機能テストで機能仕様を満たしているかを確認するのにテストケースを書く場合、それらのテストをしていてもカバレッジが100%になっていなければ、何らかの漏れがあるか、使われていない古いコードが残っているかの可能性が高くなります。どちらのケースも後々になって問題が大きくなってきます。

特に製品規模が大きくなればなるほど、使われないコードが残存するというのは、割とよくあるケースで、使われないコードが増えだすと、どこに手をいれてよいのかがわかりにくくなってきます。こうなってくると、段々とコードを変更する事が大変になってきます。

テストケースがあったとしても、カバレッジが低ければ、どこを触ってもデグレードしてしまう危険があるからです。

特に企業で開発する場合、ずっと一人でメンテをするということはなく、開発メンバは変わっていきます。このような場合に、テストカバレッジが低いと大変です。

2-3人で開発をしていて、全体を全ての人が把握しきれているという状態が未来永劫続く環境があれば、カバレッジが低くても何とかなりますが、現実的にはそのような状況は存在しえません。長い目でみると、カバレッジを高く保つ事は、保守性を高め、結果プロダクト開発の生産性を高めることになります。

Perlでは、このカバレッジテストをするためのモジュールとして、Devel::Coverというモジュールが存在します。http-engineの例を見ると、以下のようなスクリプトでcoverageを、Devel::Coverを使って測定しています。

#!/bin/zsh
rm -rf cover_db
make realclean
perl Makefile.PL
HARNESS_PERL_SWITCHES=-MDevel::Cover=+ignore,inc,-coverage,statement,branch,condition,path,subroutine make test
cover
open cover_db/coverage.html

上記のスクリプトに少し手を加えて、cronで日付単位で結果を生成するようにして、indexを生成するスクリプトを作っておけば、日常的にカバレッジを確認できるようになりますね。

特に、開発の中盤から後半、またサービスのリリース後においては、テストカバレッジが高い状況を作り出しておくと、いざ開発メンバが変わったときでも安心して開発ができるのではないかと思います。

開発時のvimの使い方

開発時のvimの使い方  - dann's blog を含むブックマーク はてなブックマーク - 開発時のvimの使い方  - dann's blog 開発時のvimの使い方  - dann's blog のブックマークコメント

今までは、

  • 必要なファイルを必要なときに開く
  • Ctrl-zでsusppendして、なんかしてfgで戻る

最近は、

  • vim **/*.pm で全部バッファに開く
  • fuzzyfinderでバッファ移動 + autocomplpopでbufferから補完
  • screenで移動して、なんかして、vim用screenのwindowに移動

とても富豪的な使い方なんだけれど、最近はこちらのほうが快適。fuzzy finderとautocomplpopがでてから、使い方ががらりと変わった。

色んな流儀があると思うんだけど、他の人はどんな風に使ってるのかなぁ。

Coroでmuxtapeの情報取得をparalell化

Coroでmuxtapeの情報取得をparalell化 - dann's blog を含むブックマーク はてなブックマーク - Coroでmuxtapeの情報取得をparalell化 - dann's blog Coroでmuxtapeの情報取得をparalell化 - dann's blog のブックマークコメント

dmakiさんの記事を参考に、つい先日書いたmuxtapeの情報取得スクリプトをCoro化してみました。

http://mt.endeworks.jp/d-6/2008/04/coroflickr.html

Coroは、Javaだとconcurrent.utilに相当するものですね。お手軽に処理をパラレル化できるので、色々と使える場面がありそうです。

#!/usr/bin/env perl
use strict;
use warnings;
use Benchmark qw(:all);
use FindBin::libs;

use Coro;
use Coro::AnyEvent;
use Coro::LWP; 

use WWW::Muxtape::Scraper;
use URI;
use Perl6::Say;
use YAML::Syck;
$YAML::Syck::ImplicitUnicode = 1;

my $t = timeit(1, ¥&main);
print "main code took:",timestr($t),"¥n";

sub main {
    say "collecting tapes ...";
    my $tapes = collecting_tape_pages();
    generate_tape_lists($tapes);
    say "Done! See muxtape.yaml ;)";
}

sub collecting_tape_pages {
    my $muxtape    = WWW::Muxtape::Scraper->new;
    my $tape_lists = $muxtape->top_page->scrape();
    my $tapes      = [];
    my @coros;
    foreach my $tape ( @{$tape_lists} ) {
        push @coros, async {
            say 'collecting tape info: ' . $tape->{tapename};
            push @{$tapes},
                $muxtape->tape_page->scrape( tapename => $tape->{tapename} );
            say 'collected: ' . $tape->{tapename};
        }
    }
    $_->join for @coros;
    $tapes;
}

sub generate_tape_lists {
    my $tapes = shift;
    DumpFile( "muxtape.yaml", $tapes );
}

__END__

dmakiさんの記事にも書いてありますが、今回のCoroの使い方のポイントは、以下の2点。

use Coro;
use Coro::AnyEvent;
use Coro::LWP; 
use LPW系モジュール;
    my @coros;
    foreach my $tape ( @array ) {
        push @coros, async {
            'do something'
        }
    }
    $_->join for @coros; #処理待ち

Test::Classを使ったCPANモジュール

Test::Classを使ったCPANモジュール - dann's blog を含むブックマーク はてなブックマーク - Test::Classを使ったCPANモジュール - dann's blog Test::Classを使ったCPANモジュール - dann's blog のブックマークコメント

CHIはTest::Class使ってるなぁ。xUnit styleで書かれていて、個人的にはこのスタイルの方が好きかなぁ。テストケース毎の共通化もベースクラスでできるし。

ただ、テストケースをlib/CHI/t/libってとこに置いてるけれど、tディレクトリの下に置いたほうが個人的には好きかなぁ。ソースとテストケースのディレクトリは分けるべきだと思っているから。そうすると、t/lib/CHI/t/libに置くのがいいのかなぁ。

テストケースをrunするスクリプトだけが、tディレクトリの下においているみたい。個人的には、これは一つにまとめたほうが好みかな。

それでなくても参考にはなるかなぁ。

CHIのMultilevel driverで多段cache

CHIのMultilevel driverで多段cache - dann's blog を含むブックマーク はてなブックマーク - CHIのMultilevel driverで多段cache - dann's blog CHIのMultilevel driverで多段cache - dann's blog のブックマークコメント

MemoryになければMemcachedからcache取得、のようなコードを以下のように書けるというのがCHIの肝で、ここがData::ObjectDriverととても似ているんですね。

subcachesのところに、driverを複数書くと、書いた順にcacheを探しにいきます。

#!/usr/bin/env perl
use strict;
use warnings;
use CHI;

my $cache = CHI->new(
    driver    => 'Multilevel',
    subcaches => [
        { driver => 'Memory' },
        {   driver  => 'MemcachedFast',
            servers => ["127.0.0.1:11211"]
        }
    ],
);

$cache->set( 'hoge', 'hige' );
print $cache->get('hoge');

# 後もう一つの肝は、get_multiを使える事。