Hatena::Groupcatalyst

masaki@catalyst RSSフィード

Fork me on GitHub
 | 

2008-06-25

Catalyst::View::Templated の実装

| 02:24 | Catalyst::View::Templated の実装 - masaki@catalyst を含むブックマーク はてなブックマーク - Catalyst::View::Templated の実装 - masaki@catalyst Catalyst::View::Templated の実装 - masaki@catalyst のブックマークコメント

Catalyst::View::Templated を使う前に - masaki@catalyst - Catalystグループ の続き.new(), process(), render() を実際に見てみる.template() は基本前のエントリで書いた通りなので割愛.

new

ここで引数にデフォルト値をセットしてる.

sub new {
    my $self = shift;
    my ($c, $args) = @_;
    
    $args->{CONTENT_TYPE} ||= 'text/html';
    
    # default INCLUDE_PATH
    if (!$args->{INCLUDE_PATH}){
        $args->{INCLUDE_PATH} = [$c->path_to('root')];
    }
    
    # fixup INCLUDE_PATH if the user passes a scalar
    if (ref $args->{INCLUDE_PATH} ne 'ARRAY') {
        $args->{INCLUDE_PATH} = [$args->{INCLUDE_PATH}];
    }

    $args->{CATALYST_VAR} ||= 'c';
    
    return $self->next::method($c, $args);
}
  1. $args->{CONTENT_TYPE} が無ければ 'text/html' を入れて
  2. $args->{INCLUDE_PATH} が無ければ [ $c->path_to('root') ] を入れつつ ArrayRef かどうかチェックして
  3. $args->{CATALYST_VAR} が無ければ 'c' を入れて
  4. next::method() 呼んでインスタンス返す

process

テンプレートを実際にレンダリングして $c->res->body$c->res->content_type を設定する.

sub process {
    my $self = shift;
    # c is also passed, but we don't care anymore

    my $template = $self->template;
    $self->context->log->debug(qq/Processing template "$template"/) 
      if $self->context->debug;
    my $output = $self->_do_render($template);
    
    unless ( $self->context->response->content_type ) {
        my $ct      = $self->{CONTENT_TYPE};
        my $charset = 'iso-8859-1';
        $charset = 'utf-8' if utf8::is_utf8($output);
        
        $self->context->response->content_type("$ct; charset=$charset");
    }
    
    $self->context->response->body($output);
    
    return 1; # backcompat, ick
}
  1. まずテンプレートファイルを取得して
  2. _do_render() を呼んでレンダリングして
  3. $c->res->content_type が空ならセットして
  4. $c->res->body にレンダリング結果をセットする

render

レンダリングだけ行う.

sub render {
    my $self = shift;

    my ($c, $template, $args);
    if (ref $_[0]) {
        ($c, $template, $args) = @_;
    }
    else {
        ($template, $args) = @_;
    }

    $self->context->log->debug(qq/Rendering template "$template"/) 
      if $self->context->debug;
    
    return $self->_do_render($template, $args);
}
  1. $view->($c, $template) 形式の古い呼び出し方でも大丈夫なようにチェックして
  2. _do_render() した結果を返す

_do_render とか

_do_render() からレンダリングしている部分を抜粋.

    my $stash    = {%{$self->context->stash}};
    my $catalyst = $self->{CATALYST_VAR};
    
    $stash->{$catalyst} = $self->context;
    $stash->{base} ||= $self->context->request->base;
    $stash->{name} ||= $self->context->config->{name};
    
    my $output = eval {
        $self->_render($template, $stash, $args);
    };

ここでやっているのは,$c->stash のコピーに c, base, name の 3 つをセットして,_render() メソッドで結果を生成するという処理になる.stash にいくつか値をセットしているのは例によって Catalyst::View::TT と同様の処理.

さて,実は最後に呼んでいる _render() は Catalyst::View::Templated には実装されていないメソッドで,これがどういうことかというと,要するに Catalyst::View::Templated を継承する View それぞれで固有のテンプレートエンジンを使った実装をしてね,ということ.なので Catalyst::View::Templated を使うときには最低でも _render() は実装する必要があって,このことは POD にも書いてある.

=head1 IMPLEMENTING A SUBCLASS

All you need to do is implement a new method (for setup) and a _render method that accepts a template name, a hashref
of paramaters, and a hashref of arguments (optional, passed to render by the user), and returns the rendered template.

ちなみに POD には _render() だけじゃなく new() も実装してね,と書いてあるが,その理由は,固有のテンプレートエンジンの設定をしたりインスタンス作っておいたり,必要に応じて TEMPLATE_EXTENSION などの設定値のデフォルト値が必要だよね,ということだと思う.

 |