Catalystのhome取れなくて急にハマった

久しぶりにCatalystを触ってハマったのでメモ。

ある環境では動くのに別の環境に移したら急にmyapp.ymlが読み込めなくてエラーになったんだけど、どうやらhomeの値がヘンな値になってるっぽい感じだったので急いでたのもあってとりあえずMYAPP_HOMEという環境変数にhomeのパスを直指定してなんとか動くようになったわけです。

元の環境ではMYAPP_HOMEなんて使わなくてもちゃんとhomeの自動取得がうまくいってたのにおかしーなーなんでかなーと思ってたわけですよ。

で、まぁ次の日Catalystのソースを見てびっくり。

homeの取得はCatalyst.pmのsetup_homeメソッドで行われています。

# Catalyst-v5.7011

sub setup_home {
    my ( $class, $home ) = @_;

    if ( my $env = Catalyst::Utils::env_value( $class, 'HOME' ) ) {
        $home = $env;
    }

    unless ($home) {
        $home = Catalyst::Utils::home($class);
    }

    if ($home) {
        $class->config->{home} ||= $home;
        $class->config->{root} ||= Path::Class::Dir->new($home)->subdir('root');
    }
}

まず最初にCatalyst::Utils::env_value関数使ってMYAPP_HOMEかCATALYST_HOMEが設定されてたらそれをhomeに設定するという処理を行ってます。

この処理は以前から知ってました。

問題は次のCatalyst::Utils::home関数です。

このhome関数は現在の環境から自動でhomeのパスを返すための関数なのですが、なかなかヘビーな処理を行ってます。

sub home {
    my $class = shift;

    # make an $INC{ $key } style string from the class name
    (my $file = "$class.pm") =~ s{::}{/}g;

    if ( my $inc_entry = $INC{$file} ) {
        {
            # look for an uninstalled Catalyst app

            # find the @INC entry in which $file was found
            (my $path = $inc_entry) =~ s/$file$//;
            my $home = dir($path)->absolute->cleanup;

            # pop off /lib and /blib if they're there
            $home = $home->parent while $home =~ /b?lib$/;

            # only return the dir if it has a Makefile.PL or Build.PL
            if (-f $home->file("Makefile.PL") or -f $home->file("Build.PL")) {

                # clean up relative path:
                # MyApp/script/.. -> MyApp

                my ($lastdir) = $home->dir_list( -1, 1 );
                if ( $lastdir eq '..' ) {
                    $home = dir($home)->parent->parent;
                }

                return $home->stringify;
            }
        }

        {
            # look for an installed Catalyst app

            # trim the .pm off the thing ( Foo/Bar.pm -> Foo/Bar/ )
            ( my $path = $inc_entry) =~ s/\.pm$//;
            my $home = dir($path)->absolute->cleanup;

            # return if if it's a valid directory
            return $home->stringify if -d $home;
        }
    }

    # we found nothing
    return 0;
}

処理の流れとしてはMyApp.pmをロードしている場所からlibよりひとつ下がったところにMakefile.PLかBuild.PLが存在してたらそこをhomeとする。って感じでしょうか。

オーノー。シラナカッタデス。

別の環境に移す時にMakefile.PLなんか使わねーから別にいらねーか、と思って移さなかったのデス。なんということでしょう。

結果、Makefile.PLが存在しないがためにhomeの取得に失敗しておかしな値になっちゃってたということです。

あうち。こんなゴリ押しな処理だったとは・・・。


教訓

ソース読め。ってこと。