ourの正確な定義

社内でperlのmy,our,localについて盛り上がったのでそのあたりに関するポスト。
今回は、ourについて。

いろんなサイトでいろんな書き方されてて、よーわからん、、ということで ... perldoc
きちっと追ってくと、ああ納得。
少しでも役に立つかなっと、記事としてまとめてみました。
少しでも役立ったときには、コメントいただけるとものすごく喜びますw

ではまず、perldocから。 ここね Perldoc - our

our associates a simple name with a package variable in the current package for use 
within the current scope. When use strict 'vars'  is in effect, our lets you use declared 
global variables without qualifying them with package names, 
within the lexical scope of the our declaration. 
In this way our differs from use vars , which is package scoped.

([意]訳)
"our"は、宣言された現在のスコープの中で、現在のパッケージ変数を
簡単な名前で呼べるようにしてくれます。
つまり、"our"で宣言されたレキシカルスコープ内では、パッケージ名で修飾することなく、
その識別子名でパッケージ変数が呼び出せます。
これは、パッケージスコープのuse varsと異なり、レキシカルスコープであるという所が要チェック。

なるほど、、 Package Foo の中で、our $a としたら、
そのパッケージ変数 $Foo::a をレキシカルスコープ(主に、'{' から '}' まで)では、
$a だけでアクセスできるようになるってことね。

Unlike my, which both allocates storage for a variable and associates a simple name 
with that storage for use within the current scope, our associates a simple name with 
a package variable in the current package, for use within the current scope. 
In other words, our has the same scoping rules as my, but does not necessarily create
 a variable.

([意]訳)
"my"は、変数( $a )のためにメモリに領域を確保し、
変数( $a )がその領域を指し示すよう割り当てておくが、
"our"は、現在のパッケージのパッケージ変数( $Foo::a )を、
変数( $a )が指し示すように割り当てる。

つまり、 "our"はレキスカルスコープであるという点は"my"と同じであるが、
かならずしも、変数を新たに作るわけではない。

ここで言いたいのは、レキシカル空間に変数名は作るけど、それはパッケージ変数へのエイリアスなんだよ、ってことだと思う。
すでにパッケージ変数 $Foo::a が存在していれば、その $Foo::a へのエイリアスとなる。
これが分かると、以下のような挙動も理解できるようになる。

package Foo;

my $a = 1;
print $a;        # 1
print $Foo::a;  #undef;

our $a = 2;
print $a;       # 2
print $Foo::a; # 2

#おもしろいことに...
our $b = 2;
print $b;       # 2
print $Foo::b; # 2
my $b = 1;    # (※)
print $b;       # 1 
print $Foo::b; # 2

簡単にいうなら、 "our"はレキシカルな変数"を作成し(既に存在していたら新しく作成する!)、
同名のパッケージ変数へのエイリアスを割り当てる。

ちなみ、(※)の部分では、use warningsを指定しておくと、

"my" variable $b masks earlier declaration in same scope at /tmp/test.pl line **

という警告が出力される。
myという宣言は一回しかしてないのにもかかわらず、である。
warningsは、 変数名がレキシカルに既に存在しているか、をチェックすることになっている(ような)ので、
このような警告文がでることになる。(と思われる)

An our declaration declares a global variable that will be visible across
 its entire lexical scope, even across package boundaries. 
The package in which the variable is entered is determined at the point
 of the declaration, not at the point of use. 

This means the following behavior holds:

([意]訳)
"our"によって宣言されたグローバル変数(パッケージ変数)は、まさにレキシカルスコープである。
従って、パッケージスコープではないので、パッケージスコープの違いを乗り越えられる。
(ファイルスコープは乗り越えられないが...)
この"our"が有効になるのは、 our $a と宣言したときであって、
その宣言文がかかれたPackageをuseしたときではない。

だから、次のような挙動となる。

レキシカルスコープですよ、ってこと。
"my"とかもpackageの壁を乗り越えられるしね、一緒だよと。
その、「次のような挙動」を見てみると。

package Foo;
our $bar;       	# ここからレキシカルスコープの終わりまで、$barは$Foo::barを指し示す
$bar = 20;

package Bar;
print $bar;             # こいつも $Foo::bar => 20 だよ!

ちゃんとpackageの壁を超えてくれます。

Multiple our declarations with the same name in the same lexical scope are allowed
 if they are in different packages. 
If they happen to be in the same package, 
Perl will emit warnings if you have asked for them, just like multiple my declarations. 
Unlike a second my declaration, which will bind the name to a fresh variable, 
a second our declaration in the same package, in the same scope, is merely redundant.

([意]訳)
同じ変数名に対する、複数回の"our"宣言は、パッケージが違えば許可されています。
もし、同じパッケージ内でやってしまったら、 my 宣言を複数回したときのように警告を発します
但し、新規の値を割り当てる二回目の my 宣言とは異なり、
二回目の our 宣言は、ほとんど変化はない。

同じ変数に対してのour宣言も、パッケージが違うと、その変数が指しているパッケージ変数は変わっちゃうよってことです。
次のスクリプトを見ると、その挙動が一目瞭然です。

package Foo;

my $tmp = 20;
my $tmp;             # warning => 'my' variable $tmp masks earlier declaration....
print $tmp;           # undef

our $bar = 20;
our $bar;             # warning => 'our' variable $bar masks earlier declaration....
print $bar;           # 20 ( not undef )

package Baz;

$Baz::bar = 30;
print $bar;           # 20 ( $Foo::bar )

our $bar;
print $bar;           # 30 ( $Baz::bar )

perldocでは、続いて attribute の話になっていくのですが、
今回は our の挙動を知るということで、そちらはまた別の機会に。

って、perldoc読まなきゃなってほんと思いますね。きっちり書いてある。うん。
最近、英語がボトルネックになってきてるぞ、うーん、苦手っていってられないぞ、、うーん :-(

Net::RTM( RTM::APIかも )を作ろうかな

最近、Remember The Milk: Online to-do list and task managementでタスク管理することが多いのですが、
いろいろAPIが用意されてておもしろいなぁっと。
TimeLineを使ってトランザクションも実装されているようですし。

で、Perlから使えたらさらにおもしろそーだなぁっと。
で、Net::RTM.

だれか作ってるかなっと思ったけどCPANになかったので。

rubyはあるみたいhttp://blog.onestepcloser.jp/articles/2007/02/05

Catalystでフォームの値が、ある条件化において正確に取れない件

問題

ご存じのように、
$c->req->param('fieldname')としてフォームの値を参照できますが、
今構築中のアプリで、BodyParameterログをみてみると、
(※リクエスト値があるときにデバッグログに表示されるやつ)

.... 入力されていないフィールドの名前と値('')が取れていない ....

つまり、

$c->req->param('empty_field_name') => undef,
exists $c->req->params->{'empty_field_name'} => False

っておおっ??


普通は困らないのかもですが、
今はHTML::FillInFormを使う事前提の組み方なので、
空でもなんでも、値(というか、そのフィールド名)が検知できないと
色々××なことになるのです。

これはアカンということで調査。

結果

Catalyst自身の問題かなっと思ったけど.....
問題の箇所は別のところでした。

HTTPリクエスト BodyをパースするHTTP::Body::MultiPartモジュールが、
空の値をもったフィールドが取り除いてしまっていた、

というのが原因でした。。

詳細は以下に。お時間あれば一読していただければと。

Catalystのリクエスト処理を追う

CatalystはHTTPリクエストがくるとまずはprepareフェーズに入ります。
最初にcontext( あの$c )を準備して、次に、リクエストを解析するprepare_*メソッドを順次実行します。

$c->prepare_request(@argument);   # $ENVを受け取る
$c->prepare_connection;            # HTTPかHTTPSか判定してごにょ
$c->prepare_query_parameters;     # $ENV{QUERY_STRING}をパースしてparamsにセット
$c->prepare_headers;               # HTTPリクエストヘッダを解析してセット
$c->prepare_cookies;               # 受け取ったヘッダからCookieを取り出してセット
$c->prepare_path;                   # BaseURIなどをセット(PROXYで動かしても動くような処理も)
                                      #基本的には「GET」ではここまで
# On-demand parsing
$c->prepare_body unless $c->config->{parse_on_demand}; #HTTPリクエストBODYを解析してparamsにセット


Catalyst::Engine(::*)系で上のような処理を行います。
上記の問題は、POSTでしか(しかも特定のフォーム)起きていないこともあり、prepare_bodyメソッドに注目。


Catalyst::Engine

sub prepare_body {
    my ( $self, $c ) = @_;
    my $type = $c->request->header('Content-Type');

    $c->request->{_body} = HTTP::Body->new( $type, $self->read_length );

    while ( my $buffer = $self->read($c) ) {
        $c->prepare_body_chunk($buffer);
    }
}

prepare_body_chunk($buffer)

sub prepare_body_chunk {
    my ( $self, $c, $chunk ) = @_;
    $c->request->{_body}->add($chunk);
}

端折ってるけど、重要な部分を取り出すとこんな感じ。


HTTP::BODYモジュールを使用して、リクエストBodyのパースをし、
読み込んだHTTPリクエストBodyを
そのモジュールのaddメソッドに渡してパラメータを取得しています。


HTTP::Bodyをnewして、addする。ここに問題が潜んでいました。

HTTP::Bodyに潜む問題

HTTP::Bodyでは、newメソッド内で、エンコードタイプ($type)に合わせて読み込むモジュールを変えています。

読み込むモジュールエンコードタイプ
HTTP::Body::UrlEncodedapplication/x-www-form-urlencoded(標準)|
HTTP::Body::MultiPart multipart/form-data
HTTP::Body::OctetStream上のどちらでもなかったら
基本的なPOSTでは、エンコードタイプは標準と記されたものになるため、 $c->body->{_body}には、HTTP::Body::UrlEncodedが、newされます。 また、ファイルをアップロードするときなど、 form にenctype="multipart/form-data"を指定しているときには、 HTTP::Body::MultiPartがnewされることになります。 そして、addメソッドでは、それぞれのモジュールのspinメソッドが呼ばれます。 HTTP::Body::UrlEncodedは特に問題ないのですが、 (つまり、普通のPOSTでは$c->req->paramsは正常) 一方、HTTP::Body::MultiPartにありました、問題。

sub spin {
    my $self = shift;
    while (1) {
        if ( $self->{state} =~ /^(preamble|boundary|header|body)$/ ) {
            my $method = "parse_$1";
            return unless $self->$method;
        }
    }
}


MultiPartでのspinは、MultiPartとして渡ってくるデータ(*1)をパースし、
その中の実データ部分(body)を取得しています。

取得したものを $self->parse_bodyすることによって、
次のデータをセットします。


$self->{part}{done}: 読み込み完了フラグ
$self->{part}{size}: bodyのデータサイズ
$self->{part}{data}: 読み込んだデータ部分

そして、最後に handlerを呼ぶのですが、ここ注目!

sub handler {
    my ( $self, $part ) = @_;

    # skip parts without content
    if ( $part->{done} && $part->{size} == 0 ) {
        return 0;
    }else{
         $self->param( $part->{name}, $part->{data} );
    }
}

(かなり省略)


おっと、、、
$part->{size} == 0のときには、
paramにセットしないで、return 0しちゃってます。

ここだ。

結論

というわけで、

formで method="POST" enctype="multipart/form-data"
を指定してるときには、
HTTPリクエストのパーサー HTTP::Body::MultiPartの中で
sizeが0のパラメータは除去されている


というオチでした。
実際に、このreturn 0を除去したらきちっと取得できるようになりました。
うーん、、、なんでここでreturn 0することにしたのか...



(*1)備考 [POST時のHTTPリクエストのBodyの様相]

(i) application/x-www-form-urlencoded(通常)の場合

Content-Type: application/x-www-form-urlencoded

Content-Length: 58

user_name=yupug&user_sex=man

(ii) multipart/form-dataを指定したときのリクエスト(Content部分)

Content-Type: multipart/form-data; boundary=---------------------------132067928117778102532114132971
Content-Length: 1421
-----------------------------132067928117778102532114132971
Content-Disposition: form-data; name="user_name"

yupug
-----------------------------132067928117778102532114132971
Content-Disposition: form-data; name="user_sex"

man
-----------------------------132067928117778102532114132971
Content-Disposition: form-data; name="user_image"; filename="user.jpg"

Content-Type: image/jpeg

(binary)


-----------------------------132067928117778102532114132971

Perlのハッシュのバケットってなんだ?

ハッシュのバケットサイズはハッシュをスカラーコンテキストで評価してあげると取れるんだけど、
なんか規則がよくわかりません。

Perlがハッシュを、どのように物理設計に落としてるのかが知れる気がするんだけど。。

  my %test = ( a => 1, b => 1 , c => 1, d => 1 , e => 1, f => 1, g => 1
                h => 8, j => 9, k => 10 , l => 11 );
  print scalar %test;
  keys %test = 200;
  print scalar %test;

結果

  9/16
  11/256

keysでアロケートしてあげると正しい値が取れる(取れている気がする?)けど、
何もしてないと、よくわからない数値がでてきたよ。。

しかも、keyの個数を変えると、アロケートしてやったほうはきっちと追従してくれるけど、
してないほうは、どうも規則性がわからない。。

(keyの個数, バケット)
(7, 6/8)
(8, 7/16)
(9. 8/16)
(10, 8/16)
(11, 9/16)
(12, 10/16)
(13, 10/16)
...
<||

変数の局所化、local宣言の挙動を掴む

local宣言。

変数を局所化する宣言、と説明されているが、どうもピンとこなかった。。そこで、ちょっとlocalについてまとめてみました。
Catalystのsetupメソッド内でもlocalが使われています。local宣言がなぜ必要だったのか。また、そのときの内部の挙動はどうなるのか。
これを理解することで、localの持つ意味も掴んでしまいましょう!

※って間違ってたらごめんさい。

それでは、注目すべきCatalyst.pmのsetupメソッドを見てみましょう。
メソッドの構造は以下のようになっています。

setup { 
    my ( $class, @arguments ) = @_;
    $class->log->warn("Running setup twice is not a good idea.")
      if ( $class->setup_finished );
....
    $class->setup_plugins( delete $flags->{plugins} );
....
    {
    no warnings qw/redefine/;
    local *setup = sub { };
    $class->setup;        
    }
....
    $class->setup_finished(1);
}

setupメソッドの中で、*setupをごにょごにょして、$class->setupメソッドを呼び出しています。
$class(MyApp)はCatalystパッケージを継承しているので、つまりは....

そう、、なんか無限ループに陥ってしまいそうですよね。
setupがsetupを呼び出して、またそのsetupで....

結論をいうと、setupをlocal宣言することによってそのループを止めているのです。
なぜ止まるのか。この挙動を詳細に追っていきましょう。

localによって何が変わるのか

まずはメソッド宣言から順次追っていきましょう。
sub setup の宣言によって、パッケージCatalystのシンボルテーブルには、"setup"というシンボルテーブルエントリが作成されます。

{main}{Catalyst}{setup} => undef

このsetupシンボルテーブルエントリには、setupという名前を持つ型グロブ*setupが紐付きます。型グロブsetupは、以下のような構造をもっています。

*setup {
  SCALAR => undef,
  ARRAY => undef,
  HASH => undef,
  CODE => { sub setup{}に記述された処理 },
  FileHandle => undef,
  Format => undef
}

従って、{main}{Catalyst}{setup} = *setup という状態が確立します。

一方、何気なくかかれていた↓の処理。

$class->setup_plugins( delete $flags->{plugins} )

ここでは、読み込むべきプラグインを$class(MyApp)の継承チェーン(@ISA)に含めることを行っています。
(今回、詳細は割愛)
この処理によって、
$class(MyApp)の継承チェーンは、

@ISA = ( 'Plugin1', 'Plugin2', .... , ''Catalyst', 'Catalyst::Base'')

と表現される状態になります。
また、それぞれのPluginにsetupメソッドがある場合、

sub setup {
  ....
  $c->NEXT::setup(@_);
}

として、NEXTによって選択される次のパッケージに処理を移行させる記述がされているはずです。

これで前置きは終わりました。いよいよ、本題の部分にはいっていきましょう。
注目しているのはこの部分です。

{
    no warnings qw/redefine/;
    local *setup = sub { };
    $class->setup;        
}

ここで重要なのは以下の3点。

  1. {}ブロックで囲まれている
  2. *setupに処理を何も行わないコードリファレンスを代入している
  3. no warnings qw/redefine/として、redefineプラグマが無効化されている

まず、{}ブロックで囲むことで、$class->setupを実行する空間を、Catalystパッケージ内に新しく作成します。
このブロックが終了すれば、処理は元のCatalystパッケージに戻ります。

local *setup = sub {};

上の式は、下の式と同値です。

local *setup; -----(1)
*setup = sub {}; -----(2)

localは、変数を局所化する宣言文です。(1)の内部処理は、以下の手順で説明されます。

[避難すべき変数値を保持しておくもの] = {main}{Catalyst}{setup}
   (現在のsetupシンボルテーブルに含まれる値を格納しておく)
{main}{Catalyst}{setup} = undef

※このときに、redefineプラグマがonになっていると、
すでに存在しているパッケージ変数に対して初期化が行われたということでエラーが発生します。


この宣言によって、このブロック内から、Catalyst::setupを参照しても、何も存在しない状態になります。
したがって、(2)の処理によって、新たな関数リファレンス(何も処理を行わない)が代入されることによって、

$class->setup が呼び出されることで、@ISA内をNEXTによってsetupメソッド実行の連鎖が発生するのですが、
Catalyst->setupまで来たところで、処理が終了するようになります。

この状態はロックを抜ける元の状態に戻ります。

他の例

test.txt

arg1 : 1
arg2 : 2
arg3 : 3
open my $IN, 'test.txt'
my $count = 1;
{
  local $/;
    while(<$IN>){
        print $count++ . "\n";
    }
}

結果

1

わかりますよね。$/はある種グローバルな変数ですが、
local $/; ( $/にundefを代入)によって、行区切り文字がundefとなり、
ファイルを一気読みすることができるのですが、
ブロックを抜ければ、その性質は元の状態 $\ = "\n"に戻ります。