Text::PSTemplateをrequireできるようにした

 

Text::PSTemplateをrequireで使えるようにしました。

Twitter上でPerlのAttribute::Handlerが過去の遺物だということを知ってしまった。大好きだったのに。。いや今でも好き。動けば問題ないのでAttributeは使っていく。

Text::PSTemplateではプラグインクラスでTplExportアトリビュートを指定したサブルーチンを検出してテンプレートオブジェクトに設定するため、CHECKフェーズでキューにシンボル名を登録し、実行フェーズで実際のテンプレートオブジェクトに登録する手順にしてました。CHECKフェーズを利用しているため、必然的にプラグインはuseしなければなりませんでした。ただ、流行りのフレームワークなど色々見てまわってみると、どうやらテンプレートエンジンはrequireで使えないと話しにならないっぽい。なのでできるようにした。

何をしたかというと、Attribute::Handlerの呼び出しフェーズをBEGINに変えました。BEGINフェーズではシンボルテーブルが未完成のため、ハンドラの引数でサブルーチンのシンボル名を受け取ろうと思っても中途半端な結果にしかならないっぽい。なので、BEGINフェーズで確実に取得できるコードリファレンスをキューに登録しておき、プラグイン機構の決まりで最初に確実に実行されるnewコンストラクタ内でプラグインパッケージのシンボルテーブルとリファレンスを照合し、シンボル名を得るという手順にしてみた。

汎用的に書き直すと、こんなことをした。attr1アトリビュートを定義する例。

package SomeClassBase;
use strict;
use warnings;
use Attribute::Handlers;

    my %_attr1_cue;
    my %_attr_fixed;

    ### ---
    ### Constractor
    ### ---
    sub new {

        my ($class, @args) = (@_);
        my $self = bless {}, $class;

        if (! $_attr_fixed{$class}) {
            $class->_fix_attr1_cue;
            $_attr_fixed{$class} = 1;
        }
        $class->_do_attr1_specific_process();

        return $self;
    }

    sub ATTR1 : ATTR(BEGIN) {

        my($pkg, undef, $ref, undef, $data, undef) = @_;
        push(@{$_attr1_cue{$pkg}}, [$ref, $data ? {@$data} : {}]);
    }

    sub _fix_attr1_cue {

        my $class = shift;
        if (my $cue = $_attr1_cue{$class}) {
            my $tbl = _get_sub_syms($class);
            for my $entry (@$cue) {
                $entry->[2] = $tbl->{$entry->[0]};
            }
        }
    }

    sub _get_sub_syms {

        my ($pkg) = @_;
        no strict 'refs';
        my $out = {};
        my $sym_tbl = %{"$pkg::"};
        for my $key (keys %$sym_tbl) {
            if (exists &{$sym_tbl->{$key}}) {
                $out->{&{$sym_tbl->{$key}}} = $key;
            }
        }
        return $out;
    }

    sub _do_attr1_specific_process {
        my $class = shift;
        for my $cue (@{$_attr1_cue{$class}}) {
            my ($ref, $data, $sym) = @{$cue};
            # do something
        }
    }

これで例外なく動くのかどうかまだ自信ないけど、今のところは問題なさげ。このやり方はnewコンストラクタが必ず、かつ自動的に呼ばれるようなケースでしか使えそうもない。それ以外の場合はアトリビュート自体使うべきでないのかもしれない。

Text::PSTemplateをrequireできるようにした」への1件のフィードバック

  1. ピンバック: lテ・na utan fast inkomst

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です