$c->stashの書き方を効率的にしよう

catalystアプリのソースで、よくこういうのを見る。

$c->stash->{hoge} = $hoge;
$c->stash->{huga} = $huga;
$c->stash->{fooo} = $fooo;


これはこう書ける。

$c->stash(
    hoge => $hoge,
    huga => $huga,
    fooo => $fooo,
);


ずっと前に神から教えてもらった。
というわけでソース嫁俺。

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%     --

だよね。んー?
助けて神。