モダンなCatalystコンポーネントを書くときは

Catalystがモダンかどうかは置いておいてCatalystコンポーネントを書く時のお作法として

大昔は

...
use base qw(Catalyst::Action);
use NEXT;

sub execute {
    my $self = shift;
    $self->NEXT::execute( @_ );
...

ちょっと前は

...
use base qw(Catalyst::Action);
use Class::C3;

sub execute {
    my $self = shift;
    $self->next::method( @_ );
...

モダンなのは

...
use base qw(Catalyst::Action);
use MRO::Compat;

sub execute {
    my $self = shift;
    $self->next::method( @_ );
...

未来的には

...
extends qw(Catalyst::Action);

override 'execute' => sub {
    my $self = shift;
    super();
...

こんな感じ?

参考

モダパー開催のお知らせ

  • 追記:2009/02/19 場所変更になりました。
  • 追記:2009/02/19 場所仮決めしました。2/19 15時までに出欠をはてブコメントで表明してください。なお人数が多い場合には渋谷の他の店なる可能性があります。
  • 追記:2009/02/10 日程が変更になりました


id:lestrratさん著、モダンPerl入門の出版を祝って飲み会をしようと思います。
名づけてモダンPerl入門出版パーティー。略してモダパー。

参加予定者

  • lestrrat
  • hidek
  • typester
  • ka2u
  • mikihoshi
  • download_takeshi
  • charsbar
  • lopnor
  • yappo

明日にはお店に伝えなきゃならんので参加表明はお早めに。

モダンPerl入門 (CodeZine BOOKS)

モダンPerl入門 (CodeZine BOOKS)

mod_perliteを試してみた


PHPのようにファイルを置くだけで実行されてCGIより速くてmod_perlより簡単なapache moduleだそうです。去年からのCGI frameworkブームを補完する上でも試してみるっきゃないということで

まずはソースの取得

svn co http://code.sixapart.com/svn/mod_perlite/trunk modperlite
cd modperlite

ちなみにもうすぐgithubに移行されるようです。

コンパイルとインストールはapxsにパスを通して

./Build.PL
./Build
./Build install

httpd.confとかconf.d/modperlite.confとかに

LoadModule perlite_module    modules/mod_perlite.so
AddType application/x-httpd-perlite .pl

これで.plの拡張子のファイルがmod_perlite下で動くようになります。

最後にapacheリスタート

例によってこんなCGIでベンチ取って見た。

#!/usr/bin/perl

use strict;
use warnings;

use CGI;

my $q = CGI->new;
print $q->header;
print "HelloWorld $ENV{MOD_PERLITE}";

$ENV{MOD_PERLITE}でmod_perlite下で動いていればバージョン番号が取れる。
この時は0.08と表示されてた。

ab -n 1000 -c 10の結果

CGI

Server Software:        Apache
Server Hostname:        localhost
Server Port:            80

Document Path:          /~hide/test.cgi
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   49.72042 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      159120 bytes
HTML transferred:       11000 bytes
Requests per second:    20.38 [#/sec] (mean)
Time per request:       490.720 [ms] (mean)
Time per request:       49.072 [ms] (mean, across all concurrent requests)
Transfer rate:          3.16 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.6      0      13
Processing:   114  489 226.7    452    4286
Waiting:      114  485 224.6    447    4286
Total:        114  489 226.6    452    4286

Percentage of the requests served within a certain time (ms)
  50%    452
  66%    521
  75%    577
  80%    609
  90%    715
  95%    821
  98%    991
  99%   1118
 100%   4286 (longest request)

modperlite

Server Software:        Apache
Server Hostname:        localhost
Server Port:            80

Document Path:          /~hide/test.pl
Document Length:        15 bytes

Concurrency Level:      10
Time taken for tests:   35.646800 seconds
Complete requests:      1000
Failed requests:        500
   (Connect: 0, Length: 500, Exceptions: 0)
Write errors:           0
Total transferred:      85500 bytes
HTML transferred:       7500 bytes
Requests per second:    28.05 [#/sec] (mean)
Time per request:       356.468 [ms] (mean)
Time per request:       35.647 [ms] (mean, across all concurrent requests)
Transfer rate:          2.33 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     7  354 340.5    252    2025
Waiting:        0  250 355.2     93    2025
Total:          7  354 340.5    252    2025

Percentage of the requests served within a certain time (ms)
  50%    252
  66%    355
  75%    466
  80%    564
  90%    841
  95%   1120
  98%   1316
  99%   1659
 100%   2025 (longest request)

超劇的!ってわけでもないけど確かにパフォーマンスの向上が見られます。
しかもソース改変時に最初の呼び出しはコンパイルが発生して遅いけどapacheのリロードがいらないのが良いです。
これから楽しみなモジュールです。

mixi OpenIDのsreg.nickname

追記 (2009/01/14): id:asannouさんが直してくださったようで、当方でも正常に動作したのを確認いたしました。あざーっす!

初めにお断りさせていただきますが、OpenIDはよくわかってないので勘違いだったらごめんなさい><

Net::OpenID::Consumerを使ってmixi OpenIDで認証させてsregでnicknameを取得しようとしてるのですが、id_resでsreg.nicknameに空白が含まれている場合

openid.sreg.nickname=Hideo+Kimura

とかいうURLでリダイレクトされてURLパースに失敗してしまいopenid.sigが取れずにこけてしまいます。

本来

penid.sreg.nickname=Hideo+Kimura

であるべきで、これってmixiのURLエンコードが間違ってると思うんだけどどうなんだろか。
ちなみにOpenID.ne.jpでは後者で返してくれました。
教えてエロい人!

Web::ScraperでDellオーダーステータスを取得して凹む

最近これを叩くのが日課になってます。

use strict;
use warnings;

use Web::Scraper;
use WWW::Mechanize;
use URI;

my $type = 'ponum';
my $cnum = '******';
my $lnum = '******';

my $url = 'https://jpapp1.jp.dell.com/orderstatus/multiple.asp';
my %type_label = (
    'custnum' => '顧客番号',
    'ponum'   => '客様注文番号',
    'irn'     => 'ELLオンラインストアでご注文時のご注文No',
    'yahooordernum' => 'Yahooショッピングでご注文時のご注文No',
    'loancontractnum' =>
        'デルらくらく分割 ジャックス会員番号',
    'leasecontractnum' => 'リース契約番号',
);

my $scraper = scraper {
    process '//td[@width="108"][1]//a', 'order_num' => 'TEXT';
    process '//td[@width="108"][1]//a',
        'order_link' => ['@href', sub { URI->new_abs($_, $url)->as_string; }];
    process '//td[@width="108"][2]', 'customer_num'  => 'TEXT';
    process '//td[@width="108"][3]', 'delivery_date' => 'TEXT';
    process '//td[@width="108"][4]', 'status'        => 'TEXT';
};

my $mech = WWW::Mechanize->new;
$mech->post(
    $url,
    {
        stype           => $type,
        customer_number => $cnum,
        link_number     => $lnum,
    }
);
my $content = $mech->content;
my $res     = $scraper->scrape(\$content);
use YAML;
warn Dump $res;
---
customer_num: *****
delivery_date: 2009/02/02
order_link: https://jpapp1.jp.dell.com/orderstatus/single.asp?flg=1
order_num: *****
status: 製造工程

毎日凹んでる。

Text::Hatena::Escaped

というわけで、Text::Hatenaに渡したらHTMLエスケープした上で整形するように継承してみた。

#!/usr/bin/perl

use strict;
use warnings;

{
    package Text::Hatena::Escaped;
    use base qw(Text::Hatena);

    sub text_line {
        my $class = shift;
        my $text  = shift->{items}->[2];
        return $class->escape("$text\n");
    }

    sub cdata {
        my $class = shift;
        my $items = shift->{items};
        my $data  = $items->[1];
        $data = $class->escape($data);
        return "<$data>\n";
    }

    sub pre_line {
        my $class   = shift;
        my $items   = shift->{items};
        my $inlines = $class->expand($items->[2]);
        return $class->escape("$inlines\n");
    }

    sub inline {
        my $class = shift;
        my $items = shift->{items};
        my $item  = $items->[0] or return;
        $item = $class->escape($item);
        $item = Text::Hatena::AutoLink->parse($item);
        return $item;
    }

    sub escape {
        my ($self, $text) = @_;
        return $text unless $text;
        for ($text) {
            s/&/&amp;/g;
            s/</&lt;/g;
            s/>/&gt;/g;
            s/"/&quot;/g;
        }
        $text;
    }

}

warn Text::Hatena::Escaped->parse(<<EOL);
 >||
 sub test {
     <script>alert('hoge')</script>
 }
 ||<
 * <h1>test</h1>
 - <h3>foo</h3>
 - <h3>bar</h3>
 |*<h1>名前</h1>|*<h1>色</h1>|*<h1>個数</h1>|
 |<b>りんご</b>|<b>赤</b>|<b>1</b>|
 <script>alert('hoge')</script
 [http://www.hatena.ne.jp/:title=Hatena]
EOL

結果は

<div class="section">
<pre>
sub test {
    &lt;script&gt;alert('hoge')&lt;/script&gt;
}
</pre>
</div>
<div class="section">
<h3> &lt;h1&gt;test&lt;/h1&gt;</h3>
<ul>
<li> &lt;h3&gt;foo&lt;/h3&gt;</li>
<li> &lt;h3&gt;bar&lt;/h3&gt;</li>
</ul>
<table>
<tr>
<th>&lt;h1&gt;名前&lt;/h1&gt;</th>
<th>&lt;h1&gt;色&lt;/h1&gt;</th>
<th>&lt;h1&gt;個数&lt;/h1&gt;</th>
</tr>
<tr>
<td>&lt;b&gt;りんご&lt;/b&gt;</td>
<td>&lt;b&gt;赤&lt;/b&gt;</td>
<td>&lt;b&gt;1&lt;/b&gt;</td>
</tr>
</table>
<p>&lt;script&gt;alert('hoge')&lt;/script&gt;</p>
<p><a href="http://www.hatena.ne.jp/">Hatena</a></p>
</div>

こんな感じ。

Wiki SyntaxとHTML

NanoWikiを作ってて思ったんだけど、Wiki SyntaxとHTMLの関係って難しいすな。

そもそも

フォーム入力 → HTMLエスケープ → HTML変換→HTML出力

って感じに処理させるのが全うな考え方だと思うわけです。

Text::Markdownの場合、特殊文字(&とか<とか>とか)の実態参照変換はしてくれるけどMarkdown自体がHTMLを許容する記法なのでHTMLタグはエスケープしてくれない。
じゃあということで、HTMLエスケープしてからText::Markdownに渡すと&が二重エスケープされてしまう。

Text::Hatena(0.20)の場合、内部では一切HTMLエスケープしてくれてない。
のでHTMLエスケープしてからText::Hatenaに渡す必要があるんだけど、pre記法などで<>を使っているのでそこを避ける処理を書く必要がある。

ので、自前でParser書いたほうが楽な気がしてきたぬ。