DATAトークンと継承

yusukebe氏のListPod::App::Lite::Viewを見ていて良さげだったので真似してみた。

1. Baseとなるクラスを用意しておいて、こいつをuseする

package CMS::Lite::View::Base;
use strict;
use warnings;
use utf8;
use Text::MicroTemplate qw(build_mt);

sub import {
    my $pkg = shift;
    my $caller = caller;

    {
        no strict 'refs';
        *{"$caller\::pkg"} = sub { $caller };

        my @functions = qw/new edit_args template render/;

        for my $func (@functions) {
            *{"$caller\::$func"} = \&$func;
        }
 
    }
}

sub new { bless { __CACHE => undef }, $_[0] } 

sub edit_args { 
    my $self = shift;
    my $args = @_ == 1 ? shift : {@_};
} # I want to override this

sub template { 
    my $DATA_TOKEN = $_[0]->pkg . "::DATA";
    $_[0]->{__CACHE} = do { local $/; <$DATA_TOKEN>; } unless $_[0]->{__CACHE}; }

sub render { 
    my $self = shift;
    my $args = $self->edit_args(@_);
    build_mt( $self->template )->($args)->as_string;
}

1;

2. 上記をuseしたクラスは次のように書ける

package CMS::Lite::View::Head;
use strict;
use warnings;
use utf8;
use CMS::Lite::View::Base;

1;

__DATA__
? my $args = shift;
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf8" />
    <title><?= $args->{title} ?> | <?= $args->{site_name} ?></title>
    <meta name="keywords" content="<?= join ', ', @{ $args->{keywords} } ?>" />
    <meta name="description" content="<?= $args->{description} ?>" />
    <meta name="author" content="<?= $args->{author} ?>" />
    <meta name="copyright" content="<?= $args->{copyright} ?>" />
    <meta name="robots" content="<?= $args->{robots} ?>" />
    <link rel="stylesheet" type="text/css" media="screen" href="<?= $args->{css_file} ?>"/>
</head>

3. 次のように実行する

#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use Test::Differences;
use CMS::Lite::View::Head;

subtest 'basic test' => sub {

    my $head = CMS::Lite::View::Head->new;
    isa_ok $head, 'CMS::Lite::View::Head';

    my %args = (
        title => 'title',
        site_name => 'site_name',
        keywords => [qw/some keywords here/],
        description => 'site description',
        author => 'okamura',
        copyright => 'Copyright (C) site name',
        robots => 'all',
        css_file => '../common/css/import.css',
    );

	$head->render(%args); # 一回読んどく

    eq_or_diff $head->render(%args), <<"_EOF_";
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf8" />
    <title>title | site_name</title>
    <meta name="keywords" content="some, keywords, here" />
    <meta name="description" content="site description" />
    <meta name="author" content="okamura" />
    <meta name="copyright" content="Copyright (C) site name" />
    <meta name="robots" content="all" />
    <link rel="stylesheet" type="text/css" media="screen" href="../common/css/import.css"/>
</head>
_EOF_

    done_testing;
};

done_testing;

困った事

本当はuse baseしたいのですが、このように書くと意図した結果になりません。

sub template { 
	$_[0]->{__CACHE} = do { local $/; <__PACKAGE__::DATA>; } unless $_[0]->{__CACHE}; 
}

とか

sub template { 
	$_[0]->{__CACHE} = do { local $/; <$_[0]::DATA>; } unless $_[0]->{__CACHE}; 
}

1つ目はを見たいのにを見てしまう。
2つ目は←なんかこうなる

で、結局use baseあきらめてimport書いて回避したんですが、この理由だけでこう書くのはやだなー。

あと__PACKAGE__に文字列を連結するときどうやるんだっけな。

もう右眼が眼精疲労で苦しいので明日誰かに聞こう…