$c->stashの書き方を効率的にしよう
catalystアプリのソースで、よくこういうのを見る。
$c->stash->{hoge} = $hoge; $c->stash->{huga} = $huga; $c->stash->{fooo} = $fooo;
これはこう書ける。
$c->stash( hoge => $hoge, huga => $huga, fooo => $fooo, );
ずっと前に神から教えてもらった。
というわけでソース嫁俺。
- Catalyst.pm
sub stash { my $c = shift; if (@_) { my $stash = @_ > 1 ? {@_} : $_[0]; croak('stash takes a hash or hashref') unless ref $stash; foreach my $key ( keys %$stash ) { $c->{stash}->{$key} = $stash->{$key}; } } return $c->{stash}; }
前者の呼び方 - $c->stash->{hoge} = 'huga';
引数無しで呼んでるので(メソッド呼び出し時に自動で渡る自分自身、$cはshiftしてるし)@_が無いのでifをスルーして、インスタンスに生えてるハッシュキーstashのバリューを返す。
んで呼び出し元に戻る。$c->stash->{hoge}は即ち$c->{stash}なので、(前後の$cは別物、ややこしい)$c->{stash}->{hoge} = 'huga'となる。
$c->stashを呼んだら何かブツが返ってきて、そのブツに更にキーを生やした、と。
後者の呼び方 - $c->stash(hoge => 'huga');
ifに入る。引数は2つなので、$stash = {@_}となる。引数を丸ごとハッシュrefにしたもの。
もし$c->stash('hoge')とかすると、$stash = 'hoge'となり、ハッシュrefではなくなるので、unlessに引っかかって終了。
引数に入れたハッシュ(arghashとでもしよう)のペア全てにおいて、インスタンスのキーstashにarghashのキーを作成、値は当然arghashのバリュー。
インスタンスのキー、stashを返す。こいつは、ついさっきのarghashの値ペアを全て持つ。
以前に入れた値も当然保持。
こういうメソッドをcatalyst以外で普通のoopで使おうとすると、$c->stashの値にハッシュrefを作っていないうちに前者の呼び方をすると死ぬので注意。
catalystはどうしてるかってーと…調べてない。まー先に値を作ってるんじゃないかな!
あと神曰く、「後者の方が早い」とのことだけど、えーと。分からん。
ハッシュrefから値をコピーして生成する後者より、既に用意されてる値を使う前者のが早い気がするんだけどな。
というわけでベンチ取れ俺。
package Dummy; use strict; use warnings; use Data::Dumper; sub new { my $class = shift; $class = ref $class || $class; bless { @_ }, $class; } sub stash { my $c = shift; if (@_) { my $stash = @_ > 1 ? {@_} : $_[0]; croak('stash takes a hash or hashref') unless ref $stash; foreach my $key ( keys %$stash ) { $c->{stash}->{$key} = $stash->{$key}; } } return $c->{stash}; } package main; use strict; use warnings; use Benchmark qw/:all/; my $c = Dummy->new; $c->stash(hoge => 'huga'); sub normal { $c->stash->{hoge} = 'huga'; } sub hash { $c->stash(hoge => 'huga'); } cmpthese(500000, { normal => sub { normal() }, hash => sub { hash() }, });
Rate hash normal hash 113122/s -- -66% normal 333333/s 195% --
だよね。んー?
助けて神。