このメソッドはどのモジュールから呼ばれたんだろう?

というときは
UNIVERSAL::whichです。
http://search.cpan.org/dist/UNIVERSAL-which/lib/UNIVERSAL/which.pm

作成の経緯は以下みたいです。
http://blog.livedoor.jp/nipotan/archives/50506829.html

http://blog.livedoor.jp/dankogai/archives/50493208.html

http://blog.livedoor.jp/dankogai/archives/50831844.html

便利です。

CPAN

前々から疑問を持っていたCPANについての疑問を解消した。

一般ユーザ権限でのCPANの使用

要はroot権限以下では/usr/lib/perl5あたりにインストールされるのを、自分のホームディレクトリ以下に置けばいい。
というわけで、/usr/lib/perl5に相当するディレクトリを掘る。

mkdir ~/perl-lib

次にcpan用の設定ファイルをあらかじめ、必要な部分だけ作っておく。

mkdir -p .cpan/CPAN
vi .cpan/CPAN/MyConfig.pm

MyConfig.pmの中身は以下の通り。

$CPAN::Config->{cpan_home}  = undef;
$CPAN::Config->{makepl_arg} = 'PREFIX=~/perl-lib';
$CPAN::Config->{histfile}   = "$ENV{HOME}/.cpan/histfile";
1;

あとは通常通りの使用方法でOK。

今後書くスクリプトには

use lib qw(/home/crazycamel/perl-lib/lib/perl5/site_perl/5.8.8);

みたいなことをしておかなくてはいけない。

参考文献
http://iandeth.dyndns.org/mt/ian/archives/000623.html

CPANのインストールでこけたら試してみること。

1.言語設定を変えてやってみる。

LANG=C perl -MCPAN -e shell

2.Makeしてみる。

cpan> install HTML::Entities
Running install for module HTML::Entities
Running make for G/GA/GAAS/HTML-Parser-3.56.tar.gz
  Is already unwrapped into directory /home/urapico/.cpan/build/HTML-Parser-3.56
  Has already been processed within this session
Running make test
  Can't test without successful make
Running make install
  make had returned bad status, install seems impossible

cpan> look HTML::Entities

$ make install

3.force installしてみる。

cpan> force install モジュール名

参考文献
http://chain.main.jp/weblog/archives/2005/06/cpaninstall.html
http://d.hatena.ne.jp/urapico/20071224/1198513404

ハッシュのリファレンスに持ったファイルハンドルを処理するとき

そういうときは<>演算子じゃなくて、readlineだよってお話。

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

our $test = './test.txt';

my $hashref = fh_open($test);
while(<$hashref->{handle}>){
        print;
}
exit();

sub fh_open{
        my $filename = shift;
        open my $handle, '<', $filename or die $!;
        return { file => $filename, handle => $handle };
}

実行結果

syntax error at ./handle.pl line 8, near "<$hashref->{handle"
Execution of ./handle.pl aborted due to compilation errors.
#!/usr/bin/perl
use strict;
use warnings;

our $test = './test.txt';

my $hashref = fh_open($test);
while(readline($hashref->{handle})){
        print;
}
exit();

sub fh_open{
        my $filename = shift;
        open my $handle, '<', $filename or die $!;
        return { file => $filename, handle => $handle };
}

実行結果

I am Test File
haha

Gearmanによるキューイング

公式:http://gearman.org/

キューイングを使うメリット

ウェブアプリケーションを対象として考えています。

処理を分別できる。

今すぐ必要な処理、後回しにして良い処理、負荷が高い処理、負荷が低い処理を分別することができます。
ユーザがすぐに確認しなければならない処理、負荷が低くシステムの負担にならない処理は自分自身で処理し、後回しにして良い処理、負荷が高い処理はサーバにお願いして、違う人にやってもらえば、自分の仕事は極端に減ります。(ユーザにとって快適なウェブアプリケーションとなる。)

サーバプロセスがロードするライブラリが減る。

ウェブアプリケーションのプロセスないで重い処理を簡潔させてしまうと、待機プロセスすべてが、その重い処理に必要なモジュールやライブラリを読み込ませているので、その分メモリを占有してしまうらしいです。
これを別のプロセスで行えば、HTTPサーバ待機プロセスは軽くなります。

キューイングシステムの基本的な構成

+--------+              +--------+                    +--------+
| Client | 仕事の依頼-> | Server | <-仕事を引き受ける | Worker |
+--------+              +--------+                    +--------+

ウェブアプリケーションで考えます。
ClientはCGIでしょう。Serverは今回はGearmanを使います。Workerは処理によって定義されます。

とりえあずインストール

FreeBSD
#cd /usr/ports/devel/p5-Gearman-Server
#make install clean
起動
# gearmand --daemonize --pidfile=/var/run/gearmand.pid --port=7003

エラーでた。。。
/usr/local/bin/gearmandの先頭行が#!/usr/bin/local/perlになってた。
#!/usr/bin/perlに直して再度試すと、無事起動しました。

起動時のパラメータについて
daemonize
オプションを付けると個プロセスがforkされてデーモンとして稼働します。
pidfile
プロセスIDをファイルに記録することことができます
port
gearmandはデフォルトでポート7003番で稼働します。ひとつのサーバで複数のgearmandを立ち上げるにはポート番号を変更する必要があります。
gearmandサーバ停止方法
kill `cat /var/run/gearmand.pid`

テストWorker

#!/usr/bin/perl

#gearmanテスト用ワーカー:)

use strict;
use warnings;
use Gearman::Worker;
use Storable qw(freeze thaw);
use LWP::UserAgent;

#ワーカーのインスタンス生成
my $worker = Gearman::Worker->new;
# gearmandが稼働するサーバを指定
$worker->job_servers(qw/localhost:7003/);

my $ua = LWP::UserAgent->new(timeout => 1);

# get_site_statusジョブをサーバに登録
$worker->register_function(
        get_site_code=> sub{
                my $job = shift;
                my %result = ();
                my @args = @{thaw($job->arg)};
                for my $url (@args){
                        my $res = $ua->get($url);
                        next unless $res->is_success;
                        $result{$url} = $res->code;
                }
                return freeze(\%result);
        }
);

# 処理待ちループ
$worker->work while 1;

テストClient

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use Storable qw(freeze thaw);
use Gearman::Client;

my $client = Gearman::Client->new;
$client->job_servers(qw/localhost:7003/);

my @args = (
        'http://www.perl.org',
        'http://cpan.org',
        'http://c2.com/cgi/wiki',
);

my $args = freeze(\@args);

my %option = ();
#ジョブを登録してその結果を受け取る
my $result = $client->do_task(get_site_code => $args, \%option);
my $deserialized = thaw($$result);
print Dumper $deserialized;

コーディングの際の注意事項

引数はスカラー値しか渡せません。

よって複雑な引数を渡したいときは、スカラー値にシリアライズして渡してやらなければなりません。
上記の例ではStorableを使用しています。

イントロスペクション

Perlはグローバルな(myで宣言していない)変数のルックアップにはシンボルテーブルが使用されます。
シンボルテーブルとはハッシュであり、キーと値の対の集合です。
シンボルテーブルのキーとは変数名であり、値は変数名のグロブです。

例えば以下のようなコードを実行した場合

#!/usr/bin/perl
use strict;
use warnings;
our $a;

シンボルテーブルは以下のようになります。

キー 値へのリファレンス
a グロブa

なぜ、グロブが出てくるのか?
それはPerlにとってaという単語は様々な意味を持つからです。

Perlが$aの値を見つけるには2ステップ必要です。

  1. シンボリックテーブルからaを探し、グロブaを見つける。
  2. グロブaから$aを見つける。

ということはグロブを書き換えてやれば、変数の別名(エイリアス)を作ることができます。

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

our $a = 10;
*b = *a;
print $b . "\n";

実行結果は以下です。

10

これはあくまで、グローバル変数の世界の話しであって、my宣言されたものは動きません。

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

my $a = 10;
*b = *a;
print $b . "\n";

実行結果

Name "main::a" used only once: possible typo at ./introspection.pl line 6.
Use of uninitialized value in concatenation (.) or string at ./introspection.pl line 7.

上記のエラーメッセージはwarningsプラグマせいです。warningsプラグマをはずすと、なにも表示されません。
だって、$bは$aではないからね。

偽Exporterを作る。

Exporterの動作を確認するため、ふたつコードを書いてみた。

TestModule.pm

package TestModule;
use strict;
use warnings;
use base 'Exporter';
our @EXPORT = qw(camel);

sub camel($){
        my $msg = shift;
        print "Camel says $msg\n";
}
1;

import.pl

#!/usr/bin/perl
use strict;
use warnings;
use TestModule;

camel("I am Crazy.")

import.plの実行結果は以下になる。

Camel says I am Crazy.

上記の場合Expoterはimport.plにTestModule.pmのcamelメソッドをインポートする責任がある。
これっぽい動きをするのが以下。
TestModule.pm

package TestModule;
use strict;
use warnings;

sub camel($){
        my $msg = shift;
        print "Camel says $msg\n";
}

sub import{
        no strict 'refs';
        *{caller()."::camel"} = *camel;
}

BEGIN{
        &import;
}

1;

import.plの実行結果は以下になる。

Camel says I am Crazy.

TestModule.pmを呼び出したとたんに、importメソッドを呼ぶ。
importメソッドはcaller()を読んで、main::camelという文字列を作成する。
no strict 'refs'によってこの構文はglobとして解釈される。

main::camelグロブはcamelグロブによって上書きされる。
これによってimport.pl内でのcamelサーブルーチン呼び出しは、TestModule::camelメソッドを呼ぶことになる。

問題点

しかしこれでは、$camel,@camel,%camel,ファイルハンドルcamel,ディレクトリハンドルcamelもTestModule.pmのものを使ってしまいます。
TestModule.pm

package TestModule;
use strict;
use warnings;
our $camel = 'I live in TestModule.pm';

sub camel($){
        my $msg = shift;
        print "Camel says $msg\n";
}

sub import{
        no strict 'refs';
        *{caller()."::camel"} = *camel;
}

BEGIN{
        &import;
}

1;

import.pl

#!/usr/bin/perl
use strict;
use warnings;
use TestModule;

camel("I am Lazy.");
print $camel . "\n";

import.plの実行結果は以下です。

Camel says I am Lazy.
I live in TestModule.pm

僕はサブルーチンだけを書き換えたいのです。
そんな方法をPerlはもちろん持っています。

# サブルーチンだけ書き換える。
*glob = \&camel;
# スカラーだけ書き換える。
*glob = \$camel;
# 配列だけ書き換える。
*glob = \@camel;
# ハッシュだけ書き換える。
*glob = \%camel;
#!/usr/bin/perl
use strict;
use warnings;
our $camel;
our @camel;
our %camel;
*camel = \"Hello";
*camel = [1..10];
*camel = { 'a' => 'a', 'b' => 'b'};

print $camel . "\n";
print @camel . "\n";
print $camel{a} . "\n";

実行結果は以下です。

Hello
10
a

これで好きならくだにアイリアスする方法を見つけました。
よってTestModule.pmは以下のようにすればサブルーチンだけエイリアスできます。

package TestModule;
use strict;
use warnings;
our $camel = 'I live in TestModule.pm';

sub camel($){
        my $msg = shift;
        print "Camel says $msg\n";
}

sub import{
        no strict 'refs';
        *{caller()."::camel"} = \&camel;
}

BEGIN{
        &import;
}

1;

perlで電卓

コーディング中に電卓使いたくなることってありますよね。

% perl -de 1

Loading DB routines from perl5db.pl version 1.31
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):   1
  DB<1> p 10*10
100
  DB<2>