summaryrefslogtreecommitdiff
path: root/lib/Template/View.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Template/View.pm')
-rw-r--r--lib/Template/View.pm752
1 files changed, 0 insertions, 752 deletions
diff --git a/lib/Template/View.pm b/lib/Template/View.pm
deleted file mode 100644
index 6de8d42..0000000
--- a/lib/Template/View.pm
+++ /dev/null
@@ -1,752 +0,0 @@
-#============================================================= -*-Perl-*-
-#
-# Template::View
-#
-# DESCRIPTION
-# A custom view of a template processing context. Can be used to
-# implement custom "skins".
-#
-# AUTHOR
-# Andy Wardley <abw@kfs.org>
-#
-# COPYRIGHT
-# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
-#
-# This module is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-#
-# TODO
-# * allowing print to have a hash ref as final args will cause problems
-# if you do this: [% view.print(hash1, hash2, hash3) %]. Current
-# work-around is to do [% view.print(hash1); view.print(hash2);
-# view.print(hash3) %] or [% view.print(hash1, hash2, hash3, { }) %]
-#
-# REVISION
-# $Id: View.pm,v 2.9 2004/01/30 18:36:11 abw Exp $
-#
-#============================================================================
-
-package Template::View;
-
-require 5.004;
-
-use strict;
-use vars qw( $VERSION $DEBUG $AUTOLOAD @BASEARGS $MAP );
-use base qw( Template::Base );
-
-$VERSION = sprintf("%d.%02d", q$Revision: 2.9 $ =~ /(\d+)\.(\d+)/);
-$DEBUG = 0 unless defined $DEBUG;
-@BASEARGS = qw( context );
-$MAP = {
- HASH => 'hash',
- ARRAY => 'list',
- TEXT => 'text',
- default => '',
-};
-
-$DEBUG = 0;
-
-#------------------------------------------------------------------------
-# _init(\%config)
-#
-# Initialisation method called by the Template::Base class new()
-# constructor. $self->{ context } has already been set, by virtue of
-# being named in @BASEARGS. Remaining config arguments are presented
-# as a hash reference.
-#------------------------------------------------------------------------
-
-sub _init {
- my ($self, $config) = @_;
-
- # move 'context' somewhere more private
- $self->{ _CONTEXT } = $self->{ context };
- delete $self->{ context };
-
- # generate table mapping object types to templates
- my $map = $config->{ map } || { };
- $map->{ default } = $config->{ default } unless defined $map->{ default };
- $self->{ map } = {
- %$MAP,
- %$map,
- };
-
- # local BLOCKs definition table
- $self->{ _BLOCKS } = $config->{ blocks } || { };
-
- # name of presentation method which printed objects might provide
- $self->{ method } = defined $config->{ method }
- ? $config->{ method } : 'present';
-
- # view is sealed by default preventing variable update after
- # definition, however we don't actually seal a view until the
- # END of the view definition
- my $sealed = $config->{ sealed };
- $sealed = 1 unless defined $sealed;
- $self->{ sealed } = $sealed ? 1 : 0;
-
- # copy remaining config items from $config or set defaults
- foreach my $arg (qw( base prefix suffix notfound silent )) {
- $self->{ $arg } = $config->{ $arg } || '';
- }
-
- # name of data item used by view()
- $self->{ item } = $config->{ item } || 'item';
-
- # map methods of form ${include_prefix}_foobar() to include('foobar')?
- $self->{ include_prefix } = $config->{ include_prefix } || 'include_';
- # what about mapping foobar() to include('foobar')?
- $self->{ include_naked } = defined $config->{ include_naked }
- ? $config->{ include_naked } : 1;
-
- # map methods of form ${view_prefix}_foobar() to include('foobar')?
- $self->{ view_prefix } = $config->{ view_prefix } || 'view_';
- # what about mapping foobar() to view('foobar')?
- $self->{ view_naked } = $config->{ view_naked } || 0;
-
- # the view is initially unsealed, allowing directives in the initial
- # view template to create data items via the AUTOLOAD; once sealed via
- # call to seal(), the AUTOLOAD will not update any internal items.
- delete @$config{ qw( base method map default prefix suffix notfound item
- include_prefix include_naked silent sealed
- view_prefix view_naked blocks ) };
- $config = { %{ $self->{ base }->{ data } }, %$config }
- if $self->{ base };
- $self->{ data } = $config;
- $self->{ SEALED } = 0;
-
- return $self;
-}
-
-
-#------------------------------------------------------------------------
-# seal()
-# unseal()
-#
-# Seal or unseal the view to allow/prevent new datat items from being
-# automatically created by the AUTOLOAD method.
-#------------------------------------------------------------------------
-
-sub seal {
- my $self = shift;
- $self->{ SEALED } = $self->{ sealed };
-}
-
-sub unseal {
- my $self = shift;
- $self->{ SEALED } = 0;
-}
-
-
-#------------------------------------------------------------------------
-# clone(\%config)
-#
-# Cloning method which takes a copy of $self and then applies to it any
-# modifications specified in the $config hash passed as an argument.
-# Configuration items may also be specified as a list of "name => $value"
-# arguments. Returns a reference to the cloned Template::View object.
-#
-# NOTE: may need to copy BLOCKS???
-#------------------------------------------------------------------------
-
-sub clone {
- my $self = shift;
- my $clone = bless { %$self }, ref $self;
- my $config = ref $_[0] eq 'HASH' ? shift : { @_ };
-
- # merge maps
- $clone->{ map } = {
- %{ $self->{ map } },
- %{ $config->{ map } || { } },
- };
-
- # "map => { default=>'xxx' }" can be specified as "default => 'xxx'"
- $clone->{ map }->{ default } = $config->{ default }
- if defined $config->{ default };
-
- # update any remaining config items
- my @args = qw( base prefix suffix notfound item method include_prefix
- include_naked view_prefix view_naked );
- foreach my $arg (@args) {
- $clone->{ $arg } = $config->{ $arg } if defined $config->{ $arg };
- }
- push(@args, qw( default map ));
- delete @$config{ @args };
-
- # anything left is data
- my $data = $clone->{ data } = { %{ $self->{ data } } };
- @$data{ keys %$config } = values %$config;
-
- return $clone;
-}
-
-
-#------------------------------------------------------------------------
-# print(@items, ..., \%config)
-#
-# Prints @items in turn by mapping each to an approriate template using
-# the internal 'map' hash. If an entry isn't found and the item is an
-# object that implements the method named in the internal 'method' item,
-# (default: 'present'), then the method will be called passing a reference
-# to $self, against which the presenter method may make callbacks (e.g.
-# to view_item()). If the presenter method isn't implemented, then the
-# 'default' map entry is consulted and used if defined. The final argument
-# may be a reference to a hash array providing local overrides to the internal
-# defaults for various items (prefix, suffix, etc). In the presence
-# of this parameter, a clone of the current object is first made, applying
-# any configuration updates, and control is then delegated to it.
-#------------------------------------------------------------------------
-
-sub print {
- my $self = shift;
-
- # if final config hash is specified then create a clone and delegate to it
- # NOTE: potential problem when called print(\%data_hash1, \%data_hash2);
- if ((scalar @_ > 1) && (ref $_[-1] eq 'HASH')) {
- my $cfg = pop @_;
- my $clone = $self->clone($cfg)
- || return;
- return $clone->print(@_)
- || $self->error($clone->error());
- }
- my ($item, $type, $template, $present);
- my $method = $self->{ method };
- my $map = $self->{ map };
- my $output = '';
-
- # print each argument
- foreach $item (@_) {
- my $newtype;
-
- if (! ($type = ref $item)) {
- # non-references are TEXT
- $type = 'TEXT';
- $template = $map->{ $type };
- }
- elsif (! defined ($template = $map->{ $type })) {
- # no specific map entry for object, maybe it implements a
- # 'present' (or other) method?
- if ( $method && UNIVERSAL::can($item, $method) ) {
- $present = $item->$method($self); ## call item method
- # undef returned indicates error, note that we expect
- # $item to have called error() on the view
- return unless defined $present;
- $output .= $present;
- next; ## NEXT
- }
- elsif ( UNIVERSAL::isa($item, 'HASH' )
- && defined($newtype = $item->{$method})
- && defined($template = $map->{"$method=>$newtype"})) {
- }
- elsif ( defined($newtype)
- && defined($template = $map->{"$method=>*"}) ) {
- $template =~ s/\*/$newtype/;
- }
- elsif (! ($template = $map->{ default }) ) {
- # default not defined, so construct template name from type
- ($template = $type) =~ s/\W+/_/g;
- }
- }
-# else {
-# $self->DEBUG("defined map type for $type: $template\n");
-# }
- $self->DEBUG("printing view '", $template || '', "', $item\n") if $DEBUG;
- $output .= $self->view($template, $item)
- if $template;
- }
- return $output;
-}
-
-
-#------------------------------------------------------------------------
-# view($template, $item, \%vars)
-#
-# Wrapper around include() which expects a template name, $template,
-# followed by a data item, $item, and optionally, a further hash array
-# of template variables. The $item is added as an entry to the $vars
-# hash (which is created empty if not passed as an argument) under the
-# name specified by the internal 'item' member, which is appropriately
-# 'item' by default. Thus an external object present() method can
-# callback against this object method, simply passing a data item to
-# be displayed. The external object doesn't have to know what the
-# view expects the item to be called in the $vars hash.
-#------------------------------------------------------------------------
-
-sub view {
- my ($self, $template, $item) = splice(@_, 0, 3);
- my $vars = ref $_[0] eq 'HASH' ? shift : { @_ };
- $vars->{ $self->{ item } } = $item if defined $item;
- $self->include($template, $vars);
-}
-
-
-#------------------------------------------------------------------------
-# include($template, \%vars)
-#
-# INCLUDE a template, $template, mapped according to the current prefix,
-# suffix, default, etc., where $vars is an optional hash reference
-# containing template variable definitions. If the template isn't found
-# then the method will default to any 'notfound' template, if defined
-# as an internal item.
-#------------------------------------------------------------------------
-
-sub include {
- my ($self, $template, $vars) = @_;
- my $context = $self->{ _CONTEXT };
-
- $template = $self->template($template);
-
- $vars = { } unless ref $vars eq 'HASH';
- $vars->{ view } ||= $self;
-
- $context->include( $template, $vars );
-
-# DEBUGGING
-# my $out = $context->include( $template, $vars );
-# print STDERR "VIEW return [$out]\n";
-# return $out;
-}
-
-
-#------------------------------------------------------------------------
-# template($template)
-#
-# Returns a compiled template for the specified template name, according
-# to the current configuration parameters.
-#------------------------------------------------------------------------
-
-sub template {
- my ($self, $name) = @_;
- my $context = $self->{ _CONTEXT };
- return $context->throw(Template::Constants::ERROR_VIEW,
- "no view template specified")
- unless $name;
-
- my $notfound = $self->{ notfound };
- my $base = $self->{ base };
- my ($template, $block, $error);
-
- return $block
- if ($block = $self->{ _BLOCKS }->{ $name });
-
- # try the named template
- $template = $self->template_name($name);
- $self->DEBUG("looking for $template\n") if $DEBUG;
- eval { $template = $context->template($template) };
-
- # try asking the base view if not found
- if (($error = $@) && $base) {
- $self->DEBUG("asking base for $name\n") if $DEBUG;
- eval { $template = $base->template($name) };
- }
-
- # try the 'notfound' template (if defined) if that failed
- if (($error = $@) && $notfound) {
- unless ($template = $self->{ _BLOCKS }->{ $notfound }) {
- $notfound = $self->template_name($notfound);
- $self->DEBUG("not found, looking for $notfound\n") if $DEBUG;
- eval { $template = $context->template($notfound) };
-
- return $context->throw(Template::Constants::ERROR_VIEW, $error)
- if $@; # return first error
- }
- }
- elsif ($error) {
- $self->DEBUG("no 'notfound'\n")
- if $DEBUG;
- return $context->throw(Template::Constants::ERROR_VIEW, $error);
- }
- return $template;
-}
-
-
-#------------------------------------------------------------------------
-# template_name($template)
-#
-# Returns the name of the specified template with any appropriate prefix
-# and/or suffix added.
-#------------------------------------------------------------------------
-
-sub template_name {
- my ($self, $template) = @_;
- $template = $self->{ prefix } . $template . $self->{ suffix }
- if $template;
-
- $self->DEBUG("template name: $template\n") if $DEBUG;
- return $template;
-}
-
-
-#------------------------------------------------------------------------
-# default($val)
-#
-# Special case accessor to retrieve/update 'default' as an alias for
-# '$map->{ default }'.
-#------------------------------------------------------------------------
-
-sub default {
- my $self = shift;
- return @_ ? ($self->{ map }->{ default } = shift)
- : $self->{ map }->{ default };
-}
-
-
-#------------------------------------------------------------------------
-# AUTOLOAD
-#
-
-# Returns/updates public internal data items (i.e. not prefixed '_' or
-# '.') or presents a view if the method matches the view_prefix item,
-# e.g. view_foo(...) => view('foo', ...). Similarly, the
-# include_prefix is used, if defined, to map include_foo(...) to
-# include('foo', ...). If that fails then the entire method name will
-# be used as the name of a template to include iff the include_named
-# parameter is set (default: 1). Last attempt is to match the entire
-# method name to a view() call, iff view_naked is set. Otherwise, a
-# 'view' exception is raised reporting the error "no such view member:
-# $method".
-#------------------------------------------------------------------------
-
-sub AUTOLOAD {
- my $self = shift;
- my $item = $AUTOLOAD;
- $item =~ s/.*:://;
- return if $item eq 'DESTROY';
-
- if ($item =~ /^[\._]/) {
- return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
- "attempt to view private member: $item");
- }
- elsif (exists $self->{ $item }) {
- # update existing config item (e.g. 'prefix') if unsealed
- return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
- "cannot update config item in sealed view: $item")
- if @_ && $self->{ SEALED };
- $self->DEBUG("accessing item: $item\n") if $DEBUG;
- return @_ ? ($self->{ $item } = shift) : $self->{ $item };
- }
- elsif (exists $self->{ data }->{ $item }) {
- # get/update existing data item (must be unsealed to update)
- if (@_ && $self->{ SEALED }) {
- return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
- "cannot update item in sealed view: $item")
- unless $self->{ silent };
- # ignore args if silent
- @_ = ();
- }
- $self->DEBUG(@_ ? "updating data item: $item <= $_[0]\n"
- : "returning data item: $item\n") if $DEBUG;
- return @_ ? ($self->{ data }->{ $item } = shift)
- : $self->{ data }->{ $item };
- }
- elsif (@_ && ! $self->{ SEALED }) {
- # set data item if unsealed
- $self->DEBUG("setting unsealed data: $item => @_\n") if $DEBUG;
- $self->{ data }->{ $item } = shift;
- }
- elsif ($item =~ s/^$self->{ view_prefix }//) {
- $self->DEBUG("returning view($item)\n") if $DEBUG;
- return $self->view($item, @_);
- }
- elsif ($item =~ s/^$self->{ include_prefix }//) {
- $self->DEBUG("returning include($item)\n") if $DEBUG;
- return $self->include($item, @_);
- }
- elsif ($self->{ include_naked }) {
- $self->DEBUG("returning naked include($item)\n") if $DEBUG;
- return $self->include($item, @_);
- }
- elsif ($self->{ view_naked }) {
- $self->DEBUG("returning naked view($item)\n") if $DEBUG;
- return $self->view($item, @_);
- }
- else {
- return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
- "no such view member: $item");
- }
-}
-
-
-1;
-
-
-__END__
-
-=head1 NAME
-
-Template::View - customised view of a template processing context
-
-=head1 SYNOPSIS
-
- # define a view
- [% VIEW view
- # some standard args
- prefix => 'my_',
- suffix => '.tt2',
- notfound => 'no_such_file'
- ...
-
- # any other data
- title => 'My View title'
- other_item => 'Joe Random Data'
- ...
- %]
- # add new data definitions, via 'my' self reference
- [% my.author = "$abw.name <$abw.email>" %]
- [% my.copy = "&copy; Copyright 2000 $my.author" %]
-
- # define a local block
- [% BLOCK header %]
- This is the header block, title: [% title or my.title %]
- [% END %]
-
- [% END %]
-
- # access data items for view
- [% view.title %]
- [% view.other_item %]
-
- # access blocks directly ('include_naked' option, set by default)
- [% view.header %]
- [% view.header(title => 'New Title') %]
-
- # non-local templates have prefix/suffix attached
- [% view.footer %] # => [% INCLUDE my_footer.tt2 %]
-
- # more verbose form of block access
- [% view.include( 'header', title => 'The Header Title' ) %]
- [% view.include_header( title => 'The Header Title' ) %]
-
- # very short form of above ('include_naked' option, set by default)
- [% view.header( title => 'The Header Title' ) %]
-
- # non-local templates have prefix/suffix attached
- [% view.footer %] # => [% INCLUDE my_footer.tt2 %]
-
- # fallback on the 'notfound' template ('my_no_such_file.tt2')
- # if template not found
- [% view.include('missing') %]
- [% view.include_missing %]
- [% view.missing %]
-
- # print() includes a template relevant to argument type
- [% view.print("some text") %] # type=TEXT, template='text'
-
- [% BLOCK my_text.tt2 %] # 'text' with prefix/suffix
- Text: [% item %]
- [% END %]
-
- # now print() a hash ref, mapped to 'hash' template
- [% view.print(some_hash_ref) %] # type=HASH, template='hash'
-
- [% BLOCK my_hash.tt2 %] # 'hash' with prefix/suffix
- hash keys: [% item.keys.sort.join(', ')
- [% END %]
-
- # now print() a list ref, mapped to 'list' template
- [% view.print(my_list_ref) %] # type=ARRAY, template='list'
-
- [% BLOCK my_list.tt2 %] # 'list' with prefix/suffix
- list: [% item.join(', ') %]
- [% END %]
-
- # print() maps 'My::Object' to 'My_Object'
- [% view.print(myobj) %]
-
- [% BLOCK my_My_Object.tt2 %]
- [% item.this %], [% item.that %]
- [% END %]
-
- # update mapping table
- [% view.map.ARRAY = 'my_list_template' %]
- [% view.map.TEXT = 'my_text_block' %]
-
-
- # change prefix, suffix, item name, etc.
- [% view.prefix = 'your_' %]
- [% view.default = 'anyobj' %]
- ...
-
-=head1 DESCRIPTION
-
-TODO
-
-=head1 METHODS
-
-=head2 new($context, \%config)
-
-Creates a new Template::View presenting a custom view of the specified
-$context object.
-
-A reference to a hash array of configuration options may be passed as the
-second argument.
-
-=over 4
-
-=item prefix
-
-Prefix added to all template names.
-
- [% USE view(prefix => 'my_') %]
- [% view.view('foo', a => 20) %] # => my_foo
-
-=item suffix
-
-Suffix added to all template names.
-
- [% USE view(suffix => '.tt2') %]
- [% view.view('foo', a => 20) %] # => foo.tt2
-
-=item map
-
-Hash array mapping reference types to template names. The print()
-method uses this to determine which template to use to present any
-particular item. The TEXT, HASH and ARRAY items default to 'test',
-'hash' and 'list' appropriately.
-
- [% USE view(map => { ARRAY => 'my_list',
- HASH => 'your_hash',
- My::Foo => 'my_foo', } ) %]
-
- [% view.print(some_text) %] # => text
- [% view.print(a_list) %] # => my_list
- [% view.print(a_hash) %] # => your_hash
- [% view.print(a_foo) %] # => my_foo
-
- [% BLOCK text %]
- Text: [% item %]
- [% END %]
-
- [% BLOCK my_list %]
- list: [% item.join(', ') %]
- [% END %]
-
- [% BLOCK your_hash %]
- hash keys: [% item.keys.sort.join(', ')
- [% END %]
-
- [% BLOCK my_foo %]
- Foo: [% item.this %], [% item.that %]
- [% END %]
-
-=item method
-
-Name of a method which objects passed to print() may provide for presenting
-themselves to the view. If a specific map entry can't be found for an
-object reference and it supports the method (default: 'present') then
-the method will be called, passing the view as an argument. The object
-can then make callbacks against the view to present itself.
-
- package Foo;
-
- sub present {
- my ($self, $view) = @_;
- return "a regular view of a Foo\n";
- }
-
- sub debug {
- my ($self, $view) = @_;
- return "a debug view of a Foo\n";
- }
-
-In a template:
-
- [% USE view %]
- [% view.print(my_foo_object) %] # a regular view of a Foo
-
- [% USE view(method => 'debug') %]
- [% view.print(my_foo_object) %] # a debug view of a Foo
-
-=item default
-
-Default template to use if no specific map entry is found for an item.
-
- [% USE view(default => 'my_object') %]
-
- [% view.print(objref) %] # => my_object
-
-If no map entry or default is provided then the view will attempt to
-construct a template name from the object class, substituting any
-sequence of non-word characters to single underscores, e.g.
-
- # 'fubar' is an object of class Foo::Bar
- [% view.print(fubar) %] # => Foo_Bar
-
-Any current prefix and suffix will be added to both the default template
-name and any name constructed from the object class.
-
-=item notfound
-
-Fallback template to use if any other isn't found.
-
-=item item
-
-Name of the template variable to which the print() method assigns the current
-item. Defaults to 'item'.
-
- [% USE view %]
- [% BLOCK list %]
- [% item.join(', ') %]
- [% END %]
- [% view.print(a_list) %]
-
- [% USE view(item => 'thing') %]
- [% BLOCK list %]
- [% thing.join(', ') %]
- [% END %]
- [% view.print(a_list) %]
-
-=item view_prefix
-
-Prefix of methods which should be mapped to view() by AUTOLOAD. Defaults
-to 'view_'.
-
- [% USE view %]
- [% view.view_header() %] # => view('header')
-
- [% USE view(view_prefix => 'show_me_the_' %]
- [% view.show_me_the_header() %] # => view('header')
-
-=item view_naked
-
-Flag to indcate if any attempt should be made to map method names to
-template names where they don't match the view_prefix. Defaults to 0.
-
- [% USE view(view_naked => 1) %]
-
- [% view.header() %] # => view('header')
-
-=back
-
-=head2 print( $obj1, $obj2, ... \%config)
-
-TODO
-
-=head2 view( $template, \%vars, \%config );
-
-TODO
-
-=head1 AUTHOR
-
-Andy Wardley E<lt>abw@kfs.orgE<gt>
-
-=head1 REVISION
-
-$Revision: 2.9 $
-
-=head1 COPYRIGHT
-
-Copyright (C) 2000 Andy Wardley. All Rights Reserved.
-
-This module is free software; you can redistribute it and/or
-modify it under the same terms as Perl itself.
-
-=head1 SEE ALSO
-
-L<Template::Plugin|Template::Plugin>,
-
-=cut
-
-
-
-
-