--- /dev/null
+blib*
+Makefile
+Makefile.old
+Build
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+Template-Tiny-*
+cover_db
--- /dev/null
+;; This buffer is for notes you don't want to save, and for Lisp evaluation.
+;; If you want to create a file, visit that file with C-x C-f,
+;; then enter the text in that file's own buffer.
+
+CVS
+*.gz
+blib
+*.tar
+old
+*~
+
--- /dev/null
+Revision history for Template-Simple
+
+0.02 Tue Oct 17 02:08:28 EDT 2006
+ - Fixed bug with nested hashes being rendered. Added nested.t to
+ the tests.
+ Thanks to Nigel Hamilton <nigel@turbo10.com>
+
+0.01 Sun Aug 27 00:07:13 EDT 2006
+ First release
+
+
--- /dev/null
+Changes
+MANIFEST
+META.yml # Will be created by "make dist"
+Makefile.PL
+README
+lib/Template/Simple.pm
+t/00-load.t
+t/boilerplate.t
+t/pod-coverage.t
+t/pod.t
+t/top.t
+t/scalar.t
+t/nested.t
+t/options.t
+t/include.t
+t/error.t
+t/common.pm
--- /dev/null
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
+name: Template-Simple
+version: 0.02
+version_from: lib/Template/Simple.pm
+installdirs: site
+requires:
+ File::Slurp: 0
+ Test::More: 0
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
--- /dev/null
+# This Makefile is for the Template::Simple extension to perl.
+#
+# It was generated automatically by MakeMaker version
+# 6.17 (Revision: 1.133) from the contents of
+# Makefile.PL. Don't edit this file, edit Makefile.PL instead.
+#
+# ANY CHANGES MADE HERE WILL BE LOST!
+#
+# MakeMaker ARGV: ()
+#
+# MakeMaker Parameters:
+
+# ABSTRACT_FROM => q[lib/Template/Simple.pm]
+# AUTHOR => q[Uri Guttman <uri@sysarch.com>]
+# NAME => q[Template::Simple]
+# PL_FILES => { }
+# PREREQ_PM => { Test::More=>q[0], File::Slurp=>q[0] }
+# VERSION_FROM => q[lib/Template/Simple.pm]
+# clean => { FILES=>q[Template-Simple-*] }
+# dist => { COMPRESS=>q[gzip -9f], SUFFIX=>q[gz] }
+
+# --- MakeMaker post_initialize section:
+
+
+# --- MakeMaker const_config section:
+
+# These definitions are from config.sh (via /usr/local/lib/perl5/5.8.6/sun4-solaris/Config.pm)
+
+# They may have been overridden via Makefile.PL or on the command line
+AR = ar
+CC = gcc
+CCCDLFLAGS = -fPIC
+CCDLFLAGS =
+DLEXT = so
+DLSRC = dl_dlopen.xs
+LD = gcc
+LDDLFLAGS = -G -L/usr/local/lib
+LDFLAGS = -L/usr/local/lib
+LIBC = /lib/libc.so
+LIB_EXT = .a
+OBJ_EXT = .o
+OSNAME = solaris
+OSVERS = 2.9
+RANLIB = :
+SITELIBEXP = /usr/local/lib/perl5/site_perl/5.8.6
+SITEARCHEXP = /usr/local/lib/perl5/site_perl/5.8.6/sun4-solaris
+SO = so
+EXE_EXT =
+FULL_AR = /usr/ccs/bin/ar
+VENDORARCHEXP =
+VENDORLIBEXP =
+
+
+# --- MakeMaker constants section:
+AR_STATIC_ARGS = cr
+DIRFILESEP = /
+NAME = Template::Simple
+NAME_SYM = Template_Simple
+VERSION = 0.02
+VERSION_MACRO = VERSION
+VERSION_SYM = 0_02
+DEFINE_VERSION = -D$(VERSION_MACRO)=\"$(VERSION)\"
+XS_VERSION = 0.02
+XS_VERSION_MACRO = XS_VERSION
+XS_DEFINE_VERSION = -D$(XS_VERSION_MACRO)=\"$(XS_VERSION)\"
+INST_ARCHLIB = blib/arch
+INST_SCRIPT = blib/script
+INST_BIN = blib/bin
+INST_LIB = blib/lib
+INST_MAN1DIR = blib/man1
+INST_MAN3DIR = blib/man3
+MAN1EXT = 1
+MAN3EXT = 3
+INSTALLDIRS = site
+DESTDIR =
+PREFIX =
+PERLPREFIX = /usr/local
+SITEPREFIX = /usr/local
+VENDORPREFIX =
+INSTALLPRIVLIB = $(PERLPREFIX)/lib/perl5/5.8.6
+DESTINSTALLPRIVLIB = $(DESTDIR)$(INSTALLPRIVLIB)
+INSTALLSITELIB = $(SITEPREFIX)/lib/perl5/site_perl/5.8.6
+DESTINSTALLSITELIB = $(DESTDIR)$(INSTALLSITELIB)
+INSTALLVENDORLIB =
+DESTINSTALLVENDORLIB = $(DESTDIR)$(INSTALLVENDORLIB)
+INSTALLARCHLIB = $(PERLPREFIX)/lib/perl5/5.8.6/sun4-solaris
+DESTINSTALLARCHLIB = $(DESTDIR)$(INSTALLARCHLIB)
+INSTALLSITEARCH = $(SITEPREFIX)/lib/perl5/site_perl/5.8.6/sun4-solaris
+DESTINSTALLSITEARCH = $(DESTDIR)$(INSTALLSITEARCH)
+INSTALLVENDORARCH =
+DESTINSTALLVENDORARCH = $(DESTDIR)$(INSTALLVENDORARCH)
+INSTALLBIN = $(PERLPREFIX)/bin
+DESTINSTALLBIN = $(DESTDIR)$(INSTALLBIN)
+INSTALLSITEBIN = $(SITEPREFIX)/bin
+DESTINSTALLSITEBIN = $(DESTDIR)$(INSTALLSITEBIN)
+INSTALLVENDORBIN =
+DESTINSTALLVENDORBIN = $(DESTDIR)$(INSTALLVENDORBIN)
+INSTALLSCRIPT = $(PERLPREFIX)/bin
+DESTINSTALLSCRIPT = $(DESTDIR)$(INSTALLSCRIPT)
+INSTALLMAN1DIR = $(PERLPREFIX)/man/man1
+DESTINSTALLMAN1DIR = $(DESTDIR)$(INSTALLMAN1DIR)
+INSTALLSITEMAN1DIR = $(SITEPREFIX)/man/man1
+DESTINSTALLSITEMAN1DIR = $(DESTDIR)$(INSTALLSITEMAN1DIR)
+INSTALLVENDORMAN1DIR =
+DESTINSTALLVENDORMAN1DIR = $(DESTDIR)$(INSTALLVENDORMAN1DIR)
+INSTALLMAN3DIR = $(PERLPREFIX)/man/man3
+DESTINSTALLMAN3DIR = $(DESTDIR)$(INSTALLMAN3DIR)
+INSTALLSITEMAN3DIR = $(SITEPREFIX)/man/man3
+DESTINSTALLSITEMAN3DIR = $(DESTDIR)$(INSTALLSITEMAN3DIR)
+INSTALLVENDORMAN3DIR =
+DESTINSTALLVENDORMAN3DIR = $(DESTDIR)$(INSTALLVENDORMAN3DIR)
+PERL_LIB = /usr/local/lib/perl5/5.8.6
+PERL_ARCHLIB = /usr/local/lib/perl5/5.8.6/sun4-solaris
+LIBPERL_A = libperl.a
+FIRST_MAKEFILE = Makefile
+MAKEFILE_OLD = $(FIRST_MAKEFILE).old
+MAKE_APERL_FILE = $(FIRST_MAKEFILE).aperl
+PERLMAINCC = $(CC)
+PERL_INC = /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE
+PERL = /usr/local/bin/perl
+FULLPERL = /usr/local/bin/perl
+ABSPERL = $(PERL)
+PERLRUN = $(PERL)
+FULLPERLRUN = $(FULLPERL)
+ABSPERLRUN = $(ABSPERL)
+PERLRUNINST = $(PERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
+FULLPERLRUNINST = $(FULLPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
+ABSPERLRUNINST = $(ABSPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
+PERL_CORE = 0
+PERM_RW = 644
+PERM_RWX = 755
+
+MAKEMAKER = /usr/local/lib/perl5/5.8.6/ExtUtils/MakeMaker.pm
+MM_VERSION = 6.17
+MM_REVISION = 1.133
+
+# FULLEXT = Pathname for extension directory (eg Foo/Bar/Oracle).
+# BASEEXT = Basename part of FULLEXT. May be just equal FULLEXT. (eg Oracle)
+# PARENT_NAME = NAME without BASEEXT and no trailing :: (eg Foo::Bar)
+# DLBASE = Basename part of dynamic library. May be just equal BASEEXT.
+FULLEXT = Template/Simple
+BASEEXT = Simple
+PARENT_NAME = Template
+DLBASE = $(BASEEXT)
+VERSION_FROM = lib/Template/Simple.pm
+OBJECT =
+LDFROM = $(OBJECT)
+LINKTYPE = dynamic
+
+# Handy lists of source code files:
+XS_FILES =
+C_FILES =
+O_FILES =
+H_FILES =
+MAN1PODS =
+MAN3PODS = lib/Template/Simple.pm
+
+# Where is the Config information that we are using/depend on
+CONFIGDEP = $(PERL_ARCHLIB)$(DIRFILESEP)Config.pm $(PERL_INC)$(DIRFILESEP)config.h
+
+# Where to build things
+INST_LIBDIR = $(INST_LIB)/Template
+INST_ARCHLIBDIR = $(INST_ARCHLIB)/Template
+
+INST_AUTODIR = $(INST_LIB)/auto/$(FULLEXT)
+INST_ARCHAUTODIR = $(INST_ARCHLIB)/auto/$(FULLEXT)
+
+INST_STATIC =
+INST_DYNAMIC =
+INST_BOOT =
+
+# Extra linker info
+EXPORT_LIST =
+PERL_ARCHIVE =
+PERL_ARCHIVE_AFTER =
+
+
+TO_INST_PM = bug.pl \
+ lib/Template/Simple.pm \
+ lib/Template/Simple.pm.expnad
+
+PM_TO_BLIB = lib/Template/Simple.pm.expnad \
+ blib/lib/Template/Simple.pm.expnad \
+ bug.pl \
+ $(INST_LIB)/Template/bug.pl \
+ lib/Template/Simple.pm \
+ blib/lib/Template/Simple.pm
+
+
+# --- MakeMaker platform_constants section:
+MM_Unix_VERSION = 1.42
+PERL_MALLOC_DEF = -DPERL_EXTMALLOC_DEF -Dmalloc=Perl_malloc -Dfree=Perl_mfree -Drealloc=Perl_realloc -Dcalloc=Perl_calloc
+
+
+# --- MakeMaker tool_autosplit section:
+# Usage: $(AUTOSPLITFILE) FileToSplit AutoDirToSplitInto
+AUTOSPLITFILE = $(PERLRUN) -e 'use AutoSplit; autosplit($$ARGV[0], $$ARGV[1], 0, 1, 1)'
+
+
+
+# --- MakeMaker tool_xsubpp section:
+
+
+# --- MakeMaker tools_other section:
+SHELL = /bin/sh
+CHMOD = chmod
+CP = cp
+MV = mv
+NOOP = $(SHELL) -c true
+NOECHO = @
+RM_F = rm -f
+RM_RF = rm -rf
+TEST_F = test -f
+TOUCH = touch
+UMASK_NULL = umask 0
+DEV_NULL = > /dev/null 2>&1
+MKPATH = $(PERLRUN) "-MExtUtils::Command" -e mkpath
+EQUALIZE_TIMESTAMP = $(PERLRUN) "-MExtUtils::Command" -e eqtime
+ECHO = echo
+ECHO_N = echo -n
+UNINST = 0
+VERBINST = 0
+MOD_INSTALL = $(PERLRUN) -MExtUtils::Install -e 'install({@ARGV}, '\''$(VERBINST)'\'', 0, '\''$(UNINST)'\'');'
+DOC_INSTALL = $(PERLRUN) "-MExtUtils::Command::MM" -e perllocal_install
+UNINSTALL = $(PERLRUN) "-MExtUtils::Command::MM" -e uninstall
+WARN_IF_OLD_PACKLIST = $(PERLRUN) "-MExtUtils::Command::MM" -e warn_if_old_packlist
+
+
+# --- MakeMaker makemakerdflt section:
+makemakerdflt: all
+ $(NOECHO) $(NOOP)
+
+
+# --- MakeMaker dist section:
+TAR = tar
+TARFLAGS = cvf
+ZIP = zip
+ZIPFLAGS = -r
+COMPRESS = gzip -9f
+SUFFIX = gz
+SHAR = shar
+PREOP = $(NOECHO) $(NOOP)
+POSTOP = $(NOECHO) $(NOOP)
+TO_UNIX = $(NOECHO) $(NOOP)
+CI = ci -u
+RCS_LABEL = rcs -Nv$(VERSION_SYM): -q
+DIST_CP = best
+DIST_DEFAULT = tardist
+DISTNAME = Template-Simple
+DISTVNAME = Template-Simple-0.02
+
+
+# --- MakeMaker macro section:
+
+
+# --- MakeMaker depend section:
+
+
+# --- MakeMaker cflags section:
+
+
+# --- MakeMaker const_loadlibs section:
+
+
+# --- MakeMaker const_cccmd section:
+
+
+# --- MakeMaker post_constants section:
+
+
+# --- MakeMaker pasthru section:
+
+PASTHRU = LIB="$(LIB)"\
+ LIBPERL_A="$(LIBPERL_A)"\
+ LINKTYPE="$(LINKTYPE)"\
+ PREFIX="$(PREFIX)"\
+ OPTIMIZE="$(OPTIMIZE)"\
+ PASTHRU_DEFINE="$(PASTHRU_DEFINE)"\
+ PASTHRU_INC="$(PASTHRU_INC)"
+
+
+# --- MakeMaker special_targets section:
+.SUFFIXES: .xs .c .C .cpp .i .s .cxx .cc $(OBJ_EXT)
+
+.PHONY: all config static dynamic test linkext manifest
+
+
+
+# --- MakeMaker c_o section:
+
+
+# --- MakeMaker xs_c section:
+
+
+# --- MakeMaker xs_o section:
+
+
+# --- MakeMaker top_targets section:
+all :: pure_all manifypods
+ $(NOECHO) $(NOOP)
+
+
+pure_all :: config pm_to_blib subdirs linkext
+ $(NOECHO) $(NOOP)
+
+subdirs :: $(MYEXTLIB)
+ $(NOECHO) $(NOOP)
+
+config :: $(FIRST_MAKEFILE) $(INST_LIBDIR)$(DIRFILESEP).exists
+ $(NOECHO) $(NOOP)
+
+config :: $(INST_ARCHAUTODIR)$(DIRFILESEP).exists
+ $(NOECHO) $(NOOP)
+
+config :: $(INST_AUTODIR)$(DIRFILESEP).exists
+ $(NOECHO) $(NOOP)
+
+$(INST_AUTODIR)/.exists :: /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h
+ $(NOECHO) $(MKPATH) $(INST_AUTODIR)
+ $(NOECHO) $(EQUALIZE_TIMESTAMP) /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h $(INST_AUTODIR)/.exists
+
+ -$(NOECHO) $(CHMOD) $(PERM_RWX) $(INST_AUTODIR)
+
+$(INST_LIBDIR)/.exists :: /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h
+ $(NOECHO) $(MKPATH) $(INST_LIBDIR)
+ $(NOECHO) $(EQUALIZE_TIMESTAMP) /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h $(INST_LIBDIR)/.exists
+
+ -$(NOECHO) $(CHMOD) $(PERM_RWX) $(INST_LIBDIR)
+
+$(INST_ARCHAUTODIR)/.exists :: /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h
+ $(NOECHO) $(MKPATH) $(INST_ARCHAUTODIR)
+ $(NOECHO) $(EQUALIZE_TIMESTAMP) /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h $(INST_ARCHAUTODIR)/.exists
+
+ -$(NOECHO) $(CHMOD) $(PERM_RWX) $(INST_ARCHAUTODIR)
+
+config :: $(INST_MAN3DIR)$(DIRFILESEP).exists
+ $(NOECHO) $(NOOP)
+
+
+$(INST_MAN3DIR)/.exists :: /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h
+ $(NOECHO) $(MKPATH) $(INST_MAN3DIR)
+ $(NOECHO) $(EQUALIZE_TIMESTAMP) /usr/local/lib/perl5/5.8.6/sun4-solaris/CORE/perl.h $(INST_MAN3DIR)/.exists
+
+ -$(NOECHO) $(CHMOD) $(PERM_RWX) $(INST_MAN3DIR)
+
+help:
+ perldoc ExtUtils::MakeMaker
+
+
+# --- MakeMaker linkext section:
+
+linkext :: $(LINKTYPE)
+ $(NOECHO) $(NOOP)
+
+
+# --- MakeMaker dlsyms section:
+
+
+# --- MakeMaker dynamic section:
+
+dynamic :: $(FIRST_MAKEFILE) $(INST_DYNAMIC) $(INST_BOOT)
+ $(NOECHO) $(NOOP)
+
+
+# --- MakeMaker dynamic_bs section:
+
+BOOTSTRAP =
+
+
+# --- MakeMaker dynamic_lib section:
+
+
+# --- MakeMaker static section:
+
+## $(INST_PM) has been moved to the all: target.
+## It remains here for awhile to allow for old usage: "make static"
+static :: $(FIRST_MAKEFILE) $(INST_STATIC)
+ $(NOECHO) $(NOOP)
+
+
+# --- MakeMaker static_lib section:
+
+
+# --- MakeMaker manifypods section:
+
+POD2MAN_EXE = $(PERLRUN) "-MExtUtils::Command::MM" -e pod2man "--"
+POD2MAN = $(POD2MAN_EXE)
+
+
+manifypods : pure_all \
+ lib/Template/Simple.pm \
+ lib/Template/Simple.pm
+ $(NOECHO) $(POD2MAN) --section=3 --perm_rw=$(PERM_RW)\
+ lib/Template/Simple.pm $(INST_MAN3DIR)/Template::Simple.$(MAN3EXT)
+
+
+
+
+# --- MakeMaker processPL section:
+
+
+# --- MakeMaker installbin section:
+
+
+# --- MakeMaker subdirs section:
+
+# none
+
+# --- MakeMaker clean_subdirs section:
+clean_subdirs :
+ $(NOECHO) $(NOOP)
+
+
+# --- MakeMaker clean section:
+
+# Delete temporary files but do not touch installed files. We don't delete
+# the Makefile here so a later make realclean still has a makefile to use.
+
+clean :: clean_subdirs
+ -$(RM_RF) Template-Simple-* ./blib $(MAKE_APERL_FILE) $(INST_ARCHAUTODIR)/extralibs.all $(INST_ARCHAUTODIR)/extralibs.ld perlmain.c tmon.out mon.out so_locations pm_to_blib *$(OBJ_EXT) *$(LIB_EXT) perl.exe perl perl$(EXE_EXT) $(BOOTSTRAP) $(BASEEXT).bso $(BASEEXT).def lib$(BASEEXT).def $(BASEEXT).exp $(BASEEXT).x core core.*perl.*.? *perl.core core.[0-9] core.[0-9][0-9] core.[0-9][0-9][0-9] core.[0-9][0-9][0-9][0-9] core.[0-9][0-9][0-9][0-9][0-9]
+ -$(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) $(DEV_NULL)
+
+
+# --- MakeMaker realclean_subdirs section:
+realclean_subdirs :
+ $(NOECHO) $(NOOP)
+
+
+# --- MakeMaker realclean section:
+
+# Delete temporary files (via clean) and also delete installed files
+realclean purge :: clean realclean_subdirs
+ $(RM_RF) $(INST_AUTODIR) $(INST_ARCHAUTODIR)
+ $(RM_RF) $(DISTVNAME)
+ $(RM_F) $(INST_LIB)/Template/bug.pl blib/lib/Template/Simple.pm.expnad blib/lib/Template/Simple.pm $(MAKEFILE_OLD) $(FIRST_MAKEFILE)
+
+
+# --- MakeMaker metafile section:
+metafile :
+ $(NOECHO) $(ECHO) '# http://module-build.sourceforge.net/META-spec.html' > META.yml
+ $(NOECHO) $(ECHO) '#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#' >> META.yml
+ $(NOECHO) $(ECHO) 'name: Template-Simple' >> META.yml
+ $(NOECHO) $(ECHO) 'version: 0.02' >> META.yml
+ $(NOECHO) $(ECHO) 'version_from: lib/Template/Simple.pm' >> META.yml
+ $(NOECHO) $(ECHO) 'installdirs: site' >> META.yml
+ $(NOECHO) $(ECHO) 'requires:' >> META.yml
+ $(NOECHO) $(ECHO) ' File::Slurp: 0' >> META.yml
+ $(NOECHO) $(ECHO) ' Test::More: 0' >> META.yml
+ $(NOECHO) $(ECHO) '' >> META.yml
+ $(NOECHO) $(ECHO) 'distribution_type: module' >> META.yml
+ $(NOECHO) $(ECHO) 'generated_by: ExtUtils::MakeMaker version 6.17' >> META.yml
+
+
+# --- MakeMaker metafile_addtomanifest section:
+metafile_addtomanifest:
+ $(NOECHO) $(PERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{META.yml} => q{Module meta-data (added by MakeMaker)}}) } ' \
+ -e ' or print "Could not add META.yml to MANIFEST: $${'\''@'\''}\n"'
+
+
+# --- MakeMaker dist_basics section:
+distclean :: realclean distcheck
+ $(NOECHO) $(NOOP)
+
+distcheck :
+ $(PERLRUN) "-MExtUtils::Manifest=fullcheck" -e fullcheck
+
+skipcheck :
+ $(PERLRUN) "-MExtUtils::Manifest=skipcheck" -e skipcheck
+
+manifest :
+ $(PERLRUN) "-MExtUtils::Manifest=mkmanifest" -e mkmanifest
+
+veryclean : realclean
+ $(RM_F) *~ *.orig */*~ */*.orig
+
+
+
+# --- MakeMaker dist_core section:
+
+dist : $(DIST_DEFAULT) $(FIRST_MAKEFILE)
+ $(NOECHO) $(PERLRUN) -l -e 'print '\''Warning: Makefile possibly out of date with $(VERSION_FROM)'\''' \
+ -e ' if -e '\''$(VERSION_FROM)'\'' and -M '\''$(VERSION_FROM)'\'' < -M '\''$(FIRST_MAKEFILE)'\'';'
+
+tardist : $(DISTVNAME).tar$(SUFFIX)
+ $(NOECHO) $(NOOP)
+
+uutardist : $(DISTVNAME).tar$(SUFFIX)
+ uuencode $(DISTVNAME).tar$(SUFFIX) $(DISTVNAME).tar$(SUFFIX) > $(DISTVNAME).tar$(SUFFIX)_uu
+
+$(DISTVNAME).tar$(SUFFIX) : distdir
+ $(PREOP)
+ $(TO_UNIX)
+ $(TAR) $(TARFLAGS) $(DISTVNAME).tar $(DISTVNAME)
+ $(RM_RF) $(DISTVNAME)
+ $(COMPRESS) $(DISTVNAME).tar
+ $(POSTOP)
+
+zipdist : $(DISTVNAME).zip
+ $(NOECHO) $(NOOP)
+
+$(DISTVNAME).zip : distdir
+ $(PREOP)
+ $(ZIP) $(ZIPFLAGS) $(DISTVNAME).zip $(DISTVNAME)
+ $(RM_RF) $(DISTVNAME)
+ $(POSTOP)
+
+shdist : distdir
+ $(PREOP)
+ $(SHAR) $(DISTVNAME) > $(DISTVNAME).shar
+ $(RM_RF) $(DISTVNAME)
+ $(POSTOP)
+
+
+# --- MakeMaker distdir section:
+distdir : metafile metafile_addtomanifest
+ $(RM_RF) $(DISTVNAME)
+ $(PERLRUN) "-MExtUtils::Manifest=manicopy,maniread" \
+ -e "manicopy(maniread(),'$(DISTVNAME)', '$(DIST_CP)');"
+
+
+
+# --- MakeMaker dist_test section:
+
+disttest : distdir
+ cd $(DISTVNAME) && $(ABSPERLRUN) Makefile.PL
+ cd $(DISTVNAME) && $(MAKE) $(PASTHRU)
+ cd $(DISTVNAME) && $(MAKE) test $(PASTHRU)
+
+
+# --- MakeMaker dist_ci section:
+
+ci :
+ $(PERLRUN) "-MExtUtils::Manifest=maniread" \
+ -e "@all = keys %{ maniread() };" \
+ -e "print(qq{Executing $(CI) @all\n}); system(qq{$(CI) @all});" \
+ -e "print(qq{Executing $(RCS_LABEL) ...\n}); system(qq{$(RCS_LABEL) @all});"
+
+
+# --- MakeMaker install section:
+
+install :: all pure_install doc_install
+
+install_perl :: all pure_perl_install doc_perl_install
+
+install_site :: all pure_site_install doc_site_install
+
+install_vendor :: all pure_vendor_install doc_vendor_install
+
+pure_install :: pure_$(INSTALLDIRS)_install
+
+doc_install :: doc_$(INSTALLDIRS)_install
+
+pure__install : pure_site_install
+ $(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site
+
+doc__install : doc_site_install
+ $(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site
+
+pure_perl_install ::
+ $(NOECHO) $(MOD_INSTALL) \
+ read $(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist \
+ write $(DESTINSTALLARCHLIB)/auto/$(FULLEXT)/.packlist \
+ $(INST_LIB) $(DESTINSTALLPRIVLIB) \
+ $(INST_ARCHLIB) $(DESTINSTALLARCHLIB) \
+ $(INST_BIN) $(DESTINSTALLBIN) \
+ $(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
+ $(INST_MAN1DIR) $(DESTINSTALLMAN1DIR) \
+ $(INST_MAN3DIR) $(DESTINSTALLMAN3DIR)
+ $(NOECHO) $(WARN_IF_OLD_PACKLIST) \
+ $(SITEARCHEXP)/auto/$(FULLEXT)
+
+
+pure_site_install ::
+ $(NOECHO) $(MOD_INSTALL) \
+ read $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist \
+ write $(DESTINSTALLSITEARCH)/auto/$(FULLEXT)/.packlist \
+ $(INST_LIB) $(DESTINSTALLSITELIB) \
+ $(INST_ARCHLIB) $(DESTINSTALLSITEARCH) \
+ $(INST_BIN) $(DESTINSTALLSITEBIN) \
+ $(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
+ $(INST_MAN1DIR) $(DESTINSTALLSITEMAN1DIR) \
+ $(INST_MAN3DIR) $(DESTINSTALLSITEMAN3DIR)
+ $(NOECHO) $(WARN_IF_OLD_PACKLIST) \
+ $(PERL_ARCHLIB)/auto/$(FULLEXT)
+
+pure_vendor_install ::
+ $(NOECHO) $(MOD_INSTALL) \
+ read $(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist \
+ write $(DESTINSTALLVENDORARCH)/auto/$(FULLEXT)/.packlist \
+ $(INST_LIB) $(DESTINSTALLVENDORLIB) \
+ $(INST_ARCHLIB) $(DESTINSTALLVENDORARCH) \
+ $(INST_BIN) $(DESTINSTALLVENDORBIN) \
+ $(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
+ $(INST_MAN1DIR) $(DESTINSTALLVENDORMAN1DIR) \
+ $(INST_MAN3DIR) $(DESTINSTALLVENDORMAN3DIR)
+
+doc_perl_install ::
+ $(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
+ -$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
+ -$(NOECHO) $(DOC_INSTALL) \
+ "Module" "$(NAME)" \
+ "installed into" "$(INSTALLPRIVLIB)" \
+ LINKTYPE "$(LINKTYPE)" \
+ VERSION "$(VERSION)" \
+ EXE_FILES "$(EXE_FILES)" \
+ >> $(DESTINSTALLARCHLIB)/perllocal.pod
+
+doc_site_install ::
+ $(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
+ -$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
+ -$(NOECHO) $(DOC_INSTALL) \
+ "Module" "$(NAME)" \
+ "installed into" "$(INSTALLSITELIB)" \
+ LINKTYPE "$(LINKTYPE)" \
+ VERSION "$(VERSION)" \
+ EXE_FILES "$(EXE_FILES)" \
+ >> $(DESTINSTALLARCHLIB)/perllocal.pod
+
+doc_vendor_install ::
+ $(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
+ -$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
+ -$(NOECHO) $(DOC_INSTALL) \
+ "Module" "$(NAME)" \
+ "installed into" "$(INSTALLVENDORLIB)" \
+ LINKTYPE "$(LINKTYPE)" \
+ VERSION "$(VERSION)" \
+ EXE_FILES "$(EXE_FILES)" \
+ >> $(DESTINSTALLARCHLIB)/perllocal.pod
+
+
+uninstall :: uninstall_from_$(INSTALLDIRS)dirs
+
+uninstall_from_perldirs ::
+ $(NOECHO) $(UNINSTALL) $(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist
+
+uninstall_from_sitedirs ::
+ $(NOECHO) $(UNINSTALL) $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist
+
+uninstall_from_vendordirs ::
+ $(NOECHO) $(UNINSTALL) $(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist
+
+
+# --- MakeMaker force section:
+# Phony target to force checking subdirectories.
+FORCE:
+ $(NOECHO) $(NOOP)
+
+
+# --- MakeMaker perldepend section:
+
+
+# --- MakeMaker makefile section:
+
+# We take a very conservative approach here, but it's worth it.
+# We move Makefile to Makefile.old here to avoid gnu make looping.
+$(FIRST_MAKEFILE) : Makefile.PL $(CONFIGDEP)
+ $(NOECHO) $(ECHO) "Makefile out-of-date with respect to $?"
+ $(NOECHO) $(ECHO) "Cleaning current config before rebuilding Makefile..."
+ $(NOECHO) $(RM_F) $(MAKEFILE_OLD)
+ $(NOECHO) $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD)
+ -$(MAKE) -f $(MAKEFILE_OLD) clean $(DEV_NULL) || $(NOOP)
+ $(PERLRUN) Makefile.PL
+ $(NOECHO) $(ECHO) "==> Your Makefile has been rebuilt. <=="
+ $(NOECHO) $(ECHO) "==> Please rerun the make command. <=="
+ false
+
+
+
+# --- MakeMaker staticmake section:
+
+# --- MakeMaker makeaperl section ---
+MAP_TARGET = perl
+FULLPERL = /usr/local/bin/perl
+
+$(MAP_TARGET) :: static $(MAKE_APERL_FILE)
+ $(MAKE) -f $(MAKE_APERL_FILE) $@
+
+$(MAKE_APERL_FILE) : $(FIRST_MAKEFILE)
+ $(NOECHO) $(ECHO) Writing \"$(MAKE_APERL_FILE)\" for this $(MAP_TARGET)
+ $(NOECHO) $(PERLRUNINST) \
+ Makefile.PL DIR= \
+ MAKEFILE=$(MAKE_APERL_FILE) LINKTYPE=static \
+ MAKEAPERL=1 NORECURS=1 CCCDLFLAGS=
+
+
+# --- MakeMaker test section:
+
+TEST_VERBOSE=0
+TEST_TYPE=test_$(LINKTYPE)
+TEST_FILE = test.pl
+TEST_FILES = t/*.t
+TESTDB_SW = -d
+
+testdb :: testdb_$(LINKTYPE)
+
+test :: $(TEST_TYPE)
+
+test_dynamic :: pure_all
+ PERL_DL_NONLAZY=1 $(FULLPERLRUN) "-MExtUtils::Command::MM" "-e" "test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')" $(TEST_FILES)
+
+testdb_dynamic :: pure_all
+ PERL_DL_NONLAZY=1 $(FULLPERLRUN) $(TESTDB_SW) "-I$(INST_LIB)" "-I$(INST_ARCHLIB)" $(TEST_FILE)
+
+test_ : test_dynamic
+
+test_static :: test_dynamic
+testdb_static :: testdb_dynamic
+
+
+# --- MakeMaker ppd section:
+# Creates a PPD (Perl Package Description) for a binary distribution.
+ppd:
+ $(NOECHO) $(ECHO) '<SOFTPKG NAME="$(DISTNAME)" VERSION="0,02,0,0">' > $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <TITLE>$(DISTNAME)</TITLE>' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <ABSTRACT>A simple and fast template module</ABSTRACT>' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <AUTHOR>Uri Guttman <uri@sysarch.com></AUTHOR>' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <IMPLEMENTATION>' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <DEPENDENCY NAME="File-Slurp" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <DEPENDENCY NAME="Test-More" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <OS NAME="$(OSNAME)" />' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <ARCHITECTURE NAME="sun4-solaris" />' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' <CODEBASE HREF="" />' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) ' </IMPLEMENTATION>' >> $(DISTNAME).ppd
+ $(NOECHO) $(ECHO) '</SOFTPKG>' >> $(DISTNAME).ppd
+
+
+# --- MakeMaker pm_to_blib section:
+
+pm_to_blib: $(TO_INST_PM)
+ $(NOECHO) $(PERLRUN) -MExtUtils::Install -e 'pm_to_blib({@ARGV}, '\''$(INST_LIB)/auto'\'', '\''$(PM_FILTER)'\'')'\
+ lib/Template/Simple.pm.expnad blib/lib/Template/Simple.pm.expnad \
+ bug.pl $(INST_LIB)/Template/bug.pl \
+ lib/Template/Simple.pm blib/lib/Template/Simple.pm
+ $(NOECHO) $(TOUCH) $@
+
+# --- MakeMaker selfdocument section:
+
+
+# --- MakeMaker postamble section:
+
+
+# End.
--- /dev/null
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+ NAME => 'Template::Simple',
+ AUTHOR => 'Uri Guttman <uri@sysarch.com>',
+ VERSION_FROM => 'lib/Template/Simple.pm',
+ ABSTRACT_FROM => 'lib/Template/Simple.pm',
+ PL_FILES => {},
+ PREREQ_PM => {
+ 'Test::More' => 0,
+ 'File::Slurp' => 0,
+ },
+ dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+ clean => { FILES => 'Template-Simple-*' },
+);
--- /dev/null
+Template-Simple
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the perldoc command.
+
+ perldoc Template::Simple
+
+You can also look for information at:
+
+ Search CPAN
+ http://search.cpan.org/dist/Template-Simple
+
+ CPAN Request Tracker:
+ http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Simple
+
+ AnnoCPAN, annotated CPAN documentation:
+ http://annocpan.org/dist/Template-Simple
+
+ CPAN Ratings:
+ http://cpanratings.perl.org/d/Template-Simple
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2006 Uri Guttman
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
--- /dev/null
+
+package Trexy::Template;
+
+###############################################################################
+# Nigel Hamilton
+#
+# Copyright Nigel Hamilton 2005
+# All Rights Reserved
+#
+# Author: Nigel Hamilton
+# Filename: Trexy::Template.pm
+# Description: Do simple, fast, "no code in the template" processing
+#
+# Based on Uri Guttman's sublime
+# TinyTemplate module - released
+# at YAPC::NA, 2006.
+#
+# This template module features:
+#
+# * implicit loops
+# * template includes
+# * template caching
+# * no coding in the template
+#
+# Date Change
+# -----------------------------------------------------------------------------
+# 24/09/2006 Took Uri's module and extended it slightly
+#
+###############################################################################
+
+use strict;
+use warnings ;
+
+use base qw(Template::Simple);
+
+# escape Regex meta characters
+my $start_delimiter = qr/\[\-/;
+my $end_delimiter = qr/\-\]/;
+
+
+###############################################################################
+#
+# new - construct a template
+#
+###############################################################################
+
+sub new {
+
+ my ($class) = @_;
+
+ return $class->SUPER::new( pre_delim => $start_delimiter,
+ post_delim => $end_delimiter );
+
+}
+
+
+###############################################################################
+#
+# render - fully render the template into a string
+#
+###############################################################################
+
+sub render {
+
+ my ($this, $string, $tokens) = @_;
+
+ # return a string from render
+ return ${ $this->SUPER::render($string, $tokens) };
+
+}
+
+
+###############################################################################
+#
+# render_as_ref - render as a string reference
+#
+###############################################################################
+
+sub render_as_ref {
+
+ my ($this, $string, $tokens) = @_;
+
+ # return a string from render
+ return $this->SUPER::render($string, $tokens);
+
+}
+
+
+###############################################################################
+#
+# get_tokens - grab the tokens values out of a template
+#
+###############################################################################
+
+sub get_tokens {
+
+ my ($string) = @_;
+
+ my @found_tokens = $string =~ m/$start_delimiter(\w+)$end_delimiter/g;
+
+ my %unique_tokens = map { $_ => 1 } @found_tokens;
+
+ return keys %unique_tokens;
+
+}
+
+
+1;
--- /dev/null
+#!/usr/local/bin/perl
+
+use strict ;
+use warnings ;
+
+use Trexy::Template ;
+
+my $template = <<TEST;
+
+
+<table width="100%" border=1>
+ [-start widgets-]
+ <tr>
+ <td>[-anchor-]</td>
+ <td>
+ <b>[-title-]</b>
+ <br>[-description-]
+ </td>
+ <td>[-escaped_anchor-]</td>
+ <td>[-options-]</td>
+ </tr>
+ [-end widgets-]
+</table>
+
+TEST
+
+
+my $tokens = {
+ widgets => [
+ { title => "bart" },
+ { title => "marge" }
+ ],
+};
+
+print Trexy::Template->new->render($template, $tokens);
+
+
+
--- /dev/null
+#!/usr/local/bin/perl
+
+use warnings ;
+use strict ;
+
+use Template::Simple ;
+
+my $data = {
+ widgets => [
+ {
+ title => "bart",
+ },
+ {
+ title => "marge",
+ }
+ ],
+} ;
+
+my $template = <<TMPL ;
+[%start widgets%][%title%][%foo%] [%end widgets%]
+TMPL
+
+my $renderer = Template::Simple->new() ;
+
+my $text = $renderer->render( $template, $data ) ;
+
+print ${$text} ;
+
+print "$Template::Simple::VERSION\n" ;
--- /dev/null
+package Template::Simple;
+
+use warnings;
+use strict;
+
+use Carp ;
+use Scalar::Util qw( reftype ) ;
+use File::Slurp ;
+
+use Data::Dumper ;
+
+our $VERSION = '0.03';
+
+my %opt_defaults = (
+
+ pre_delim => qr/\[%/,
+ post_delim => qr/%\]/,
+ greedy_chunk => 0,
+# upper_case => 0,
+# lower_case => 0,
+ include_paths => [ qw( templates ) ],
+) ;
+
+sub new {
+
+ my( $class, %opts ) = @_ ;
+
+ my $self = bless {}, $class ;
+
+# get all the options or defaults into the object
+
+ while( my( $name, $default ) = each %opt_defaults ) {
+
+ $self->{$name} = defined( $opts{$name} ) ?
+ $opts{$name} : $default ;
+ }
+
+# make up the regexes to parse the markup from templates
+
+# this matches scalar markups and grabs the name
+
+ $self->{scalar_re} = qr{
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ (\w+?) # grab scalar name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ }xi ; # case insensitive
+
+#print "RE <$self->{scalar_re}>\n" ;
+
+# this grabs the body of a chunk in either greedy or non-greedy modes
+
+ my $chunk_body = $self->{greedy_chunk} ? qr/.+/s : qr/.+?/s ;
+
+# this matches a marked chunk and grabs its name and text body
+
+ $self->{chunk_re} = qr{
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ START # required START token
+ \s+ # required whitespace
+ (\w+?) # grab the chunk name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ ($chunk_body) # grab the chunk body
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ END # required END token
+ \s+ # required whitespace
+ \1 # match the grabbed chunk name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ }xi ; # case insensitive
+
+#print "RE <$self->{chunk_re}>\n" ;
+
+# this matches a include markup and grabs its template name
+
+ $self->{include_re} = qr{
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ INCLUDE # required INCLUDE token
+ \s+ # required whitespace
+ (\w+?) # grab the included template name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ }xi ; # case insensitive
+
+# load in any templates
+
+ $self->add_templates( $opts{templates} ) ;
+
+ return $self ;
+}
+
+
+
+sub render {
+
+ my( $self, $template, $data ) = @_ ;
+
+# make a copy if a scalar ref is passed as the template text is
+# modified in place
+
+ my $tmpl_ref = ref $template eq 'SCALAR' ? $template : \$template ;
+
+ my $rendered = $self->_render_includes( $tmpl_ref ) ;
+
+#print "INC EXP <$rendered>\n" ;
+
+ $rendered = eval {
+ $self->_render_chunk( $rendered, $data ) ;
+ } ;
+
+ croak "Template::Simple $@" if $@ ;
+
+ return $rendered ;
+}
+
+sub _render_includes {
+
+ my( $self, $tmpl_ref ) = @_ ;
+
+# make a copy of the initial template so we can render it.
+
+ my $rendered = ${$tmpl_ref} ;
+
+# loop until we can render no more include markups
+
+ 1 while $rendered =~
+ s{$self->{include_re}}
+ { ${ $self->_get_template($1) }
+ }e ;
+
+ return \$rendered ;
+}
+
+my %renderers = (
+
+ HASH => \&_render_hash,
+ ARRAY => \&_render_array,
+ CODE => \&_render_code,
+# if no ref then data is a scalar so replace the template with just the data
+ '' => sub { \$_[2] },
+) ;
+
+
+sub _render_chunk {
+
+ my( $self, $tmpl_ref, $data ) = @_ ;
+
+#print "T ref [$tmpl_ref] [$$tmpl_ref]\n" ;
+#print "CHUNK ref [$tmpl_ref] TMPL\n<$$tmpl_ref>\n" ;
+
+#print Dumper $data ;
+
+ return \'' unless defined $data ;
+
+# now render this chunk based on the type of data
+
+ my $renderer = $renderers{reftype $data || ''} ;
+
+#print "EXP $renderer\nREF ", reftype $data, "\n" ;
+
+ die "unknown template data type '$data'\n" unless defined $renderer ;
+
+ return $self->$renderer( $tmpl_ref, $data ) ;
+}
+
+sub _render_hash {
+
+ my( $self, $tmpl_ref, $href ) = @_ ;
+
+ return $tmpl_ref unless keys %{$href} ;
+
+# we need a local copy of the template to render
+
+ my $rendered = ${$tmpl_ref} ;
+
+
+# recursively render all top level chunks in this chunk
+
+ $rendered =~ s{$self->{chunk_re}}
+ {
+ # print "CHUNK $1\nBODY\n----\n<$2>\n\n------\n" ;
+ ${ $self->_render_chunk( \"$2", $href->{$1} ) }
+ }gex ;
+
+# now render scalars
+
+#print "HREF: ", Dumper $href ;
+
+ $rendered =~ s{$self->{scalar_re}}
+ {
+ # print "SCALAR $1 VAL $href->{$1}\n" ;
+ defined $href->{$1} ? $href->{$1} : ''
+ }ge ;
+
+#print "HASH REND3\n<$rendered>\n" ;
+
+ return \$rendered ;
+}
+
+sub _render_array {
+
+ my( $self, $tmpl_ref, $aref ) = @_ ;
+
+# render this $tmpl_ref for each element of the aref and join them
+
+ my $rendered ;
+
+#print "AREF: ", Dumper $aref ;
+
+ $rendered .= ${$self->_render_chunk( $tmpl_ref, $_ )} for @{$aref} ;
+
+ return \$rendered ;
+}
+
+sub _render_code {
+
+ my( $self, $tmpl_ref, $cref ) = @_ ;
+
+ my $rendered = $cref->( $tmpl_ref ) ;
+
+ die <<DIE if ref $rendered ne 'SCALAR' ;
+data callback to code didn't return a scalar or scalar reference
+DIE
+
+ return $rendered ;
+}
+
+sub add_templates {
+
+ my( $self, $tmpls ) = @_ ;
+
+#print Dumper $tmpls ;
+ return unless defined $tmpls ;
+
+ ref $tmpls eq 'HASH' or croak "templates argument is not a hash ref" ;
+
+ @{ $self->{templates}}{ keys %{$tmpls} } =
+ map ref $_ eq 'SCALAR' ? \"${$_}" : \"$_", values %{$tmpls} ;
+
+#print Dumper $self->{templates} ;
+
+ return ;
+}
+
+sub delete_templates {
+
+ my( $self, @names ) = @_ ;
+
+ @names = keys %{$self->{templates}} unless @names ;
+
+ delete @{$self->{templates}}{ @names } ;
+
+ delete @{$self->{template_paths}}{ @names } ;
+
+ return ;
+}
+
+sub _get_template {
+
+ my( $self, $tmpl_name ) = @_ ;
+
+#print "INC $tmpl_name\n" ;
+
+ my $tmpls = $self->{templates} ;
+
+# get the template from the cache and send it back if it was found there
+
+ my $template = $tmpls->{ $tmpl_name } ;
+ return $template if $template ;
+
+# not found, so find, slurp in and cache the template
+
+ $template = $self->_find_template( $tmpl_name ) ;
+ $tmpls->{ $tmpl_name } = $template ;
+
+ return $template ;
+}
+
+sub _find_template {
+
+ my( $self, $tmpl_name ) = @_ ;
+
+ foreach my $dir ( @{$self->{include_paths}} ) {
+
+ my $tmpl_path = "$dir/$tmpl_name.tmpl" ;
+
+#print "PATH: $tmpl_path\n" ;
+ next unless -r $tmpl_path ;
+
+# cache the path to this template
+
+ $self->{template_paths}{$tmpl_name} = $tmpl_path ;
+
+# slurp in the template file and return it as a scalar ref
+
+ return scalar read_file( $tmpl_path, scalar_ref => 1 ) ;
+ }
+
+ die <<DIE ;
+can't find template '$tmpl_name' in '@{$self->{include_paths}}'
+DIE
+
+}
+
+1; # End of Template::Simple
+
+__END__
+
+=head1 NAME
+
+Template::Simple - A simple and fast template module
+
+=head1 VERSION
+
+Version 0.03
+
+=head1 SYNOPSIS
+
+ use Template::Simple;
+
+ my $tmpl = Template::Simple->new();
+
+ my $template = <<TMPL ;
+[%INCLUDE header%]
+[%START row%]
+ [%first%] - [%second%]
+[%END row%]
+[%INCLUDE footer%]
+TMPL
+
+ my $data = {
+ header => {
+ date => 'Jan 1, 2008',
+ author => 'Me, myself and I',
+ },
+ row => [
+ {
+ first => 'row 1 value 1',
+ second => 'row 1 value 2',
+ },
+ {
+ first => 'row 2 value 1',
+ second => 'row 2 value 2',
+ },
+ ],
+ footer => {
+ modified => 'Aug 31, 2006',
+ },
+ } ;
+
+ my $rendered = $tmpl->render( $template, $data ) ;
+
+=head1 DESCRIPTION
+
+Template::Simple has these goals:
+
+=over 4
+
+=item * Support most common template operations
+
+It can recursively include other templates, replace tokens (scalars),
+recursively render nested chunks of text and render lists. By using
+simple idioms you can get conditional renderings.
+
+=item * Complete isolation of template from program code
+
+This is very important as template design can be done by different
+people than the program logic. It is rare that one person is well
+skilled in both template design and also programming.
+
+=item * Very simple template markup (only 4 markups)
+
+The only markups are C<INCLUDE>, C<START>, C<END> and C<token>. See
+MARKUP for more.
+
+=item * Easy to follow rendering rules
+
+Rendering of templates and chunks is driven from a data tree. The type
+of the data element used in an rendering controls how the rendering
+happens. The data element can be a scalar or scalar reference or an
+array, hash or code reference.
+
+=item * Efficient template rendering
+
+Rendering is very simple and uses Perl's regular expressions
+efficiently. Because the markup is so simple less processing is needed
+than many other templaters. Precompiling templates is not supported
+yet but that optimization is on the TODO list.
+
+=item * Easy user extensions
+
+User code can be called during an rendering so you can do custom
+renderings and plugins. Closures can be used so the code can have its
+own private data for use in rendering its template chunk.
+
+=back
+
+=head2 new()
+
+You create a Template::Simple by calling the class method new:
+
+ my $tmpl = Template::Simple->new() ;
+
+All the arguments to C<new()> are key/value options that change how
+the object will do renderings.
+
+=over 4
+
+=item pre_delim
+
+This option sets the string or regex that is the starting delimiter
+for all markups. You can use a plain string or a qr// but you need to
+escape (with \Q or \) any regex metachars if you want them to be plain
+chars. The default is qr/\[%/.
+
+ my $tmpl = Template::Simple->new(
+ pre_delim => '<%',
+ );
+
+ my $rendered = $tmpl->render( '<%FOO%]', 'bar' ) ;
+
+=item post_delim
+
+This option sets the string or regex that is the ending delimiter
+for all markups. You can use a plain string or a qr// but you need to
+escape (with \Q or \) any regex metachars if you want them to be plain
+chars. The default is qr/%]/.
+
+ my $tmpl = Template::Simple->new(
+ post_delim => '%>',
+ );
+
+ my $rendered = $tmpl->render( '[%FOO%>', 'bar' ) ;
+
+=item greedy_chunk
+
+This boolean option will cause the regex that grabs a chunk of text
+between the C<START/END> markups to become greedy (.+). The default is
+a not-greedy grab of the chunk text. (UNTESTED)
+
+=item templates
+
+This option lets you load templates directly into the cache of the
+Template::Simple object. This cache will be searched by the C<INCLUDE>
+markup which will be replaced by the template if found. The option
+value is a hash reference which has template names (the name in the
+C<INCLUDE> markup) for keys and their template text as their
+values. You can delete or clear templates from the object cache with
+the C<delete_template> method.
+
+
+ my $tmpl = Template::Simple->new(
+ templates => {
+
+ foo => <<FOO,
+[%baz%] is a [%quux%]
+FOO
+ bar => <<BAR,
+[%user%] is not a [%fool%]
+BAR
+ },
+ );
+
+ my $template = <<TMPL ;
+[%INCLUDE foo %]
+TMPL
+
+ my $rendered = $tmpl->render(
+ $template,
+ {
+ baz => 'blue',
+ quux => 'color,
+ }
+ ) ;
+
+=item include_paths
+
+Template::Simple can also load C<INCLUDE> templates from files. This
+option lets you set the directory paths to search for those
+files. Note that the template name in the C<INCLUDE> markup has the
+.tmpl suffix appended to it when searched for in one of these
+paths. The loaded file is cached inside the Template::Simple object
+along with any loaded by the C<templates> option.
+
+=back
+
+=head1 METHODS
+
+=head2 render
+
+This method is passed a template and a data tree and it renders it and
+returns a reference to the resulting string. The template argument can
+be a scalar or a scalar reference. The data tree argument can be any
+value allowed by Template::Simple when rendering a template. It can
+also be a blessed reference (Perl object) since
+C<Scalar::Util::reftype> is used instead of C<ref> to determine the
+data type.
+
+Note that the author recommends against passing in an object as this
+breaks encapsulation and forces your object to be (most likely) a
+hash. It would be better to create a simple method that copies the
+object contents to a hash reference and pass that. But current
+templaters allow passing in objects so that is supported here as well.
+
+ my $rendered = $tmpl->render( $template, $data ) ;
+
+=head2 add_templates
+
+This method adds templates to the object cache. It takes a list of template names and texts just like the C<templates> constructor option.
+
+ $tmpl->add_templates(
+ {
+ foo => \$foo_template,
+ bar => '[%include bar%]',
+ }
+ ) ;
+
+=head2 delete_templates
+
+This method takes a list of template names and will delete them from
+the template cache in the object. If you pass in an empty list then
+all the templates will be deleted. This can be used when you know a
+template file has been updated and you want to get it loaded back into
+the cache. Note that you can delete templates that were loaded
+directly (via the C<templates> constructor option or the
+C<add_templates> method) or loaded from a file.
+
+ # this deletes only the foo and bar templates from the object cache
+
+ $tmpl->delete_templates( qw( foo bar ) ;
+
+ # this deletes all of templates from the object cache
+
+ $tmpl->delete_templates() ;
+
+=head2 get_dependencies
+
+This method render the only C<INCLUDE> markups of a template and it
+returns a list of the file paths that were found and loaded. It is
+meant to be used to build up a dependency list of included templates
+for a main template. Typically this can be called from a script (see
+TODO) that will do this for a set of main templates and will generate
+Makefile dependencies for them. Then you can regenerate rendered
+templates only when any of their included templates have changed. It
+takes a single argument of a template.
+
+UNKNOWN: will this require a clearing of the cache or will it do the
+right thing on its own? or will it use the file path cache?
+
+ my @dependencies =
+ $tmpl->get_dependencies( '[%INCLUDE top_level%]' );
+
+=head1 MARKUP
+
+All the markups in Template::Simple use the same delimiters which are
+C<[%> and C<%]>. You can change the delimiters with the C<pre_delim>
+and C<post_delim> options in the C<new()> constructor.
+
+=head2 Tokens
+
+A token is a single markup with a C<\w+> Perl word inside. The token
+can have optional whitespace before and after it. A token is replaced
+by a value looked up in a hash with the token as the key. The hash
+lookup keeps the same case as parsed from the token markup.
+
+ [% foo %] [%BAR%]
+
+Those will be replaced by C<$href->{foo}> and C<$href->{BAR}> assuming
+C<$href> is the current data for this rendering. Tokens are only
+parsed out during hash data rendering so see Hash Data for more.
+
+=head2 Chunks
+
+Chunks are regions of text in a template that are marked off with a
+start and end markers with the same name. A chunk start marker is
+C<[%START name%]> and the end marker for that chunk is C<[%END
+name%]>. C<name> is a C<\w+> Perl word which is the name of this
+chunk. The whitespace between C<START/END> and C<name> is required and
+there is optional whitespace before C<START/END> and after the
+C<name>. C<START/END> are case insensitive but the C<name>'s case is
+kept. C<name> must match in the C<START/END> pair and it used as a key
+in a hash data rendering. Chunks are the primary way to markup
+templates for structures (sets of tokens), nesting (hashes of hashes),
+repeats (array references) and callbacks to user code. Chunks are only
+parsed out during hash data rendering so see Hash Data for more.
+
+The body of text between the C<START/END> markups is grabbed with a
+C<.+?> regular expression with the /s option enabled so it will match
+all characters. By default it will be a non-greedy grab but you can
+change that in the constructor by enabling the C<greedy_chunk> option.
+
+ [%Start FOO%]
+ [% START bar %]
+ [% field %]
+ [% end bar %]
+ [%End FOO%]
+
+=head2 Includes
+
+=head1 RENDERING RULES
+
+Template::Simple has a short list of rendering rules and they are easy
+to understand. There are two types of renderings, include rendering
+and chunk rendering. In the C<render> method, the template is an
+unnamed top level chunk of text and it first gets its C<INCLUDE>
+markups rendered. The text then undergoes a chunk rendering and a
+scalar reference to that rendered template is returned to the caller.
+
+=head2 Include Rendering
+
+Include rendering is performed one time on a top level template. When
+it is done the template is ready for chunk rendering. Any markup of
+the form C<[%INCLUDE name]%> will be replaced by the text found in the
+template C<name>. The template name is looked up in the object's
+template cache and if it is found there its text is used as the
+replacement.
+
+If a template is not found in the cache, it will be searched for in
+the list of directories in the C<include_paths> option. The file name
+will be a directory in that list appended with the template name and
+the C<.tmpl> suffix. The first template file found will be read in and
+stored in the cache. Its path is also saved and those will be returned
+in the C<get_dependencies> method. See the C<add_templates> and
+C<delete_templates> methods and the C<include_paths> option.
+
+Rendered include text can contain more C<INCLUDE> markups and they
+will also be rendered. The include rendering phase ends where there
+are no more C<INCLUDE> found.
+
+=head2 Chunk Rendering
+
+A chunk is the text found between C<START> and C<END> markups and it
+gets its named from the C<START> markup. The top level template is
+considered an unamed chunk and also gets chunk rendered.
+
+The data for a chunk determines how it will be rendered. The data can
+be a scalar or scalar reference or an array, hash or code
+reference. Since chunks can contain nested chunks, rendering will
+recurse down the data tree as it renders the chunks. Each of these
+renderings are explained below. Also see the IDIOMS and BEST PRACTICES
+section for examples and used of these renderings.
+
+=head2 Scalar Data Rendering
+
+If the current data for a chunk is a scalar or scalar reference, the
+chunk's text in the templated is replaced by the scalar's value. This
+can be used to overwrite one default section of text with from the
+data tree.
+
+=head2 Code Data Rendering
+
+If the current data for a chunk is a code reference (also called
+anonymous sub) then the code reference is called and it is passed a
+scalar reference to the that chunk's text. The code must return a
+scalar or a scalar reference and its value replaces the chunk's text
+in the template. If the code returns any other type of data it is a
+fatal error. Code rendering is how you can do custom renderings and
+plugins. A key idiom is to use closures as the data in code renderings
+and keep the required outside data in the closure.
+
+=head2 Array Data Rendering
+
+If the current data for a chunk is an array reference do a full chunk
+rendering for each value in the array. It will replace the original
+chunk text with the joined list of rendered chunks. This is how you do
+repeated sections in Template::Simple and why there is no need for any
+loop markups. Note that this means that rendering a chunk with $data
+and [ $data ] will do the exact same thing. A value of an empty array
+C<[]> will cause the chunk to be replaced by the empty string.
+
+=head2 Hash Data Rendering
+
+If the current data for a chunk is a hash reference then two phases of
+rendering happen, nested chunk rendering and token rendering. First
+nested chunks are parsed of of this chunk along with their names. Each
+parsed out chunk is rendered based on the value in the current hash
+with the nested chunk's name as the key.
+
+If a value is not found (undefined), then the nested chunk is replaced
+by the empty string. Otherwise the nested chunk is rendered according
+to the type of its data (see chunk rendering) and it is replaced by
+the rendered text.
+
+Chunk name and token lookup in the hash data is case sensitive (see
+the TODO for cased lookups).
+
+Note that to keep a plain text chunk or to just have the all of its
+markups (chunks and tokens) be deleted just pass in an empty hash
+reference C<{}> as the data for the chunk. It will be rendered but all
+markups will be replaced by the empty string.
+
+=head2 Token Rendering
+
+The second phase is token rendering. Markups of the form [%token%] are
+replaced by the value of the hash element with the token as the
+key. If a token's value is not defined it is replaced by the empty
+string. This means if a token key is missing in the hash or its value
+is undefined or its value is the empty string, the [%token%] markup
+will be deleted in the rendering.
+
+=head1 IDIOMS and BEST PRACTICES
+
+With all template systems there are better ways to do things and
+Template::Simple is no different. This section will show some ways to
+handle typical template needs while using only the 4 markups in this
+module.
+
+=head2 Conditionals
+
+This conditional idiom can be when building a fresh data tree or
+modifying an existing one.
+
+ $href->{$chunk_name} = $keep_chunk ? {} : '' ;
+
+If you are building a fresh data tree you can use this idiom to do a
+conditional chunk:
+
+ $href->{$chunk_name} = {} if $keep_chunk ;
+
+To handle an if/else conditional use two chunks, with the else chunk's
+name prefixed with NOT_ (or use any name munging you want). Then you
+set the data for either the true chunk (just the plain name) or the
+false trunk with the NOT_ name. You can use a different name for the
+else chunk if you want but keeping the names of the if/else chunks
+related is a good idea. Here are two ways to set the if/else data. The
+first one uses the same data for both the if and else chunks and the
+second one uses different data so the it uses the full if/else code
+for that.
+
+ $href->{ ($boolean ? '' : 'NOT_') . $chunk_name} = $data
+
+ if ( $boolean ) {
+ $href->{ $chunk_name} = $true_data ;
+ else {
+ $href->{ "NOT_$chunk_name" } = $false_data ;
+ }
+
+NOTE TO ALPHA USERS: i am also thinking that a non-existing key or
+undefined hash value should leave the chunk as is. then you would need
+to explicitly replace a chunk with the empty string if you wanted it
+deleted. It does affect the list of styles idiom. Any thoughts on
+this change of behavior? Since this hasn't been released it is the
+time to decide this.
+
+=head2 Chunked Includes
+
+One of the benefits of using include templates is the ability to share
+and reuse existing work. But if an included template has a top level
+named chunk, then that name would also be the same everywhere where
+this template is included. If a template included another template in
+multiple places, its data tree would use the same name for each and
+not allow unique data to be rendered for each include. A better way is
+to have the current template wrap an include markup in a named chunk
+markup. Then the data tree could use unique names for each included
+template. Here is how it would look:
+
+ [%START foo_prime%][%INCLUDE foo%][%START foo_prime%]
+ random noise
+ [%START foo_second%][%INCLUDE foo%][%START foo_second%]
+
+See the TODO section for some ideas on how to make this even more high level.
+
+=head2 Repeated Sections
+
+If you looked at the markup of Template::Simple you have noticed that
+there is no loop or repeat construct. That is because there is no need
+for one. Any chunk can be rendered in a loop just by having its
+rendering data be an anonymous array. The renderer will loop over each
+element of the array and do a fresh rendering of the chunk with this
+data. A join (on '') of the list of renderings replaces the original
+chunk and you have a repeated chunk.
+
+=head2 A List of Mixed Styles
+
+One formating style is to have a list of sections each which can have
+its own style or content. Template::Simple can do this very easily
+with just a 2 level nested chunk and an array of data for
+rendering. The outer chunk includes (or contains) each of the desired
+styles in any order. It looks like this:
+
+ [%START para_styles%]
+ [%START main_style%]
+ [%INCLUDE para_style_main%]
+ [%END main_style%]
+ [%START sub_style%]
+ [%INCLUDE para_style_sub%]
+ [%END sub_style%]
+ [%START footer_style%]
+ [%INCLUDE para_style_footer%]
+ [%END footer_style%]
+ [%END para_styles%]
+
+The other part to make this work is in the data tree. The data for
+para_styles should be a list of hashes. Each hash contains the data
+for one pargraph style which is keyed by the style's chunk name. Since
+the other styles's chunk names are not hash they are deleted. Only the
+style which has its name as a key in the hash is rendered. The data
+tree would look something like this:
+
+ [
+ {
+ main_style => $main_data,
+ },
+ {
+ sub_style => $sub_data,
+ },
+ {
+ sub_style => $other_sub_data,
+ },
+ {
+ footer_style => $footer_data,
+ },
+ ]
+
+=head1 TESTS
+
+The test scripts use a common test driver module in t/common.pl. It is
+passed a list of hashes, each of which has the data for one test. A
+test can create a ne Template::Simple object or use the one from the
+previous test. The template source, the data tree and the expected
+results are also important keys. See the test scripts for examples of
+how to write tests using this common driver.
+
+=over 4
+
+=item name
+
+This is the name of the test and is used by Test::More
+
+=item opts
+
+This is a hash ref of the options passed to the Template::Simple
+constructor. The object is not built if the C<keep_obj> key is set.
+
+=item keep_obj
+
+If set, this will make this test keep the Template::Simple object from
+the previous test and not build a new one.
+
+=item template
+
+This is the template to render for this test. If not set, the test
+driver will use the template from the previous test. This is useful to
+run a series of test variants with the same template.
+
+=item data
+
+This is the data tree for the rendering of the template.
+
+=item expected
+
+This is the text that is expected after the rendering.
+
+=item skip
+
+If set, this test is skipped.
+
+=back
+
+=head1 TODO
+
+Even though this template system is simple, that doesn't mean it can't
+be extended in many ways. Here are some features and designs that
+would be good extensions which add useful functionality without adding
+too much complexity.
+
+=head2 Compiled Templates
+
+A commonly performed optimization in template modules is to precompile
+(really preparse) templates into a internal form that will render
+faster. Precompiling is slower than rendering from the original
+template which means you won't want to do it for each rendering. This
+means it has a downside that you lose out when you want to render
+using templates which change often. Template::Simple makes it very
+easy to precompile as it already has the regexes to parse out the
+markup. So instead of calling subs to do actual rendering, a
+precompiler would call subs to generate a compiled rendering tree.
+The rendering tree can then be run or processes with rendering data
+passed to it. You can think of a precompiled template as having all
+the nested chunks be replaced by nested code that does the same
+rendering. It can still do the dynamic rendering of the data but it
+saves the time of parsing the template souice. There are three
+possible internal formats for the precompiled template:
+
+=over 4
+
+=item Source code
+
+This precompiler will generate source code that can be stored and/or
+eval'ed. The eval'ed top level sub can then be called and passed the
+rendering data.
+
+=item Closure call tree
+
+The internal format can be a nested set of closures. Each closure would contain
+private data such as fixed text parts of the original template, lists
+of other closures to run, etc. It is trivial to write a basic closure
+generator which will make build this tree a simple task.
+
+=item Code ref call tree
+
+This format is a Perl data tree where the nodes have a code reference
+and its args (which can be nested instances of the same
+nodes). Instead of executing this directly, you will need a small
+interpreter to execute all the code refs as it runs through the tree.
+
+This would make for a challenging project to any intermediate Perl
+hacker. It just involves knowing recursion, data trees and code refs.
+Contact me if you are interested in doing this.
+
+=back
+
+=head2 Cased Hash Lookups
+
+One possible option is to allow hash renderings to always use upper or
+lower cased keys in their lookups.
+
+=head2 Render tokens before includes and chunks
+
+Currently tokens are rendered after includes and chunks. If tokens
+were rendered in a pass before the others, the include and chunk names
+could be dynamically set. This would make it harder to precompile
+templates as too much would be dynamic, i.e. you won't know what the
+fixed text to parse out is since anything can be included at render
+time. But the extra flexibility of changing the include and chunk
+names would be interesting. It could be done easily and enabled by an
+option.
+
+=head2 Plugins
+
+There are two different potential areas in Template::Simple that could
+use plugins. The first is with the rendering of chunkas and
+dispatching based on the data type. This dispatch table can easily be
+replaced by loaded modules which offer a different way to
+render. These include the precompiled renderers mentioned above. The
+other area is with code references as the data type. By defining a
+closure (or a closure making) API you can create different code refs
+for the rendering data. The range of plugins is endless some of the
+major template modules have noticed. One idea is to make a closure
+which contains a different Template::Simple object than the current
+one. This will allow rendering of a nested chunk with different rules
+than the current chunk being rendered.
+
+=head2 Data Escaping
+
+Some templaters have options to properly escape data for some types of
+text files such as html. this can be done with some variant of the
+_render_hash routine which also does the scalar rendering (which is
+where data is rendered). The rendering scalars code could be factored
+out into a set of subs one of which is used based on any escaping
+needs.
+
+=head2 Data Tree is an Object
+
+This is a concept I don't like but it was requested so it goes into
+the TODO file. Currently C<render> can only be passed a regular
+(unblessed) ref (or a scalar) for its data tree. Passing in an object
+would break encapsulation and force the object layout to be a hash
+tree that matches the layout of the template. I doubt that most
+objects will want to be organized to match a template. I have two
+ideas, one is that you add a method to that object that builds up a
+proper (unblessed) data tree to pass to C<render>. The other is by
+subclassing C<Template::Simple> and overriding C<render> with a sub
+that does take an object hash and it can unbless it or build a proper
+data tree and then call C<render> in SUPER::. A quick solution is to
+use C<reftype> (from Scalar::Utils) instead of C<ref> to allow object
+hashes to be passed in.
+
+=head2 Includes and Closure Synergy
+
+By pairing up an include template along with code that can generate
+the appropriate data tree for its rendering, you can create a higher
+level template framework (the synergy). Additional code can be
+associated with them that will handle input processing and
+verification for the templates (e.g. web forms) that need it. A key to
+this will be making all the closures for the data tree. This can be
+greatly simplified by using a closure maker sub that can create all
+the required closures.
+
+=head2 Metafields and UI Generation
+
+Taking the synergy up to a much higher level is the concept of meta
+knowledge of fields which can generate templates, output processing
+(data tree generation), input processing, DB backing and more. If you
+want to discuss such grandiose wacky application schemes in a long
+rambling mind bending conversation, please contact me.
+
+=head2 More Examples and Idioms
+
+As I convert several scripts over to this module (they all used the
+hack version), I will add them to an examples section or possibly put
+them in another (pod only) module. Similarly the Idioms section needs
+rendering and could be also put into a pod module. One goal requested
+by an early alpha tester is to keep the primary docs as simple as the
+markup itself. This means moving all the extra stuff (and plenty of
+that) into other pod modules. All the pod modules would be in the same
+cpan tarball so you get all the docs and examples when you install
+this.
+
+=head1 AUTHOR
+
+Uri Guttman, C<< <uri at sysarch.com> >>
+
+=head1 BUGS
+
+Please report any bugs or feature requests to
+C<bug-template-simple at rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Template-Simple>.
+I will be notified, and then you'll automatically be notified of progress on
+your bug as I make changes.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc Template::Simple
+
+You can also look for information at:
+
+=over 4
+
+=item * RT: CPAN's request tracker
+
+L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Simple>
+
+=item * Search CPAN
+
+L<http://search.cpan.org/dist/Template-Simple>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+I wish to thank Turbo10 for their support in developing this module.
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2006 Uri Guttman, all rights reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
+
+
+find templates and tests
+
+deep nesting tests
+
+greedy tests
+
+methods pod
+
+delete_templates test
+
+pod cleanup
+
+fine edit
+
+more tests
+
+slurp dependency in makefile.pl
+
--- /dev/null
+package Template::Simple;
+
+use warnings;
+use strict;
+
+use Carp ;
+use File::Slurp ;
+
+use Data::Dumper ;
+
+our $VERSION = '0.01';
+
+my %opt_defaults = (
+
+ pre_delim => qr/\[%/,
+ post_delim => qr/%\]/,
+ greedy_chunk => 0,
+# upper_case => 0,
+# lower_case => 0,
+ include_paths => [ qw( templates ) ],
+) ;
+
+sub new {
+
+ my( $class, %opts ) = @_ ;
+
+ my $self = bless {}, $class ;
+
+# get all the options or defaults into the object
+
+ while( my( $name, $default ) = each %opt_defaults ) {
+
+ $self->{$name} = defined( $opts{$name} ) ?
+ $opts{$name} : $default ;
+ }
+
+# make up the regexes to parse the markup from templates
+
+# this matches scalar markups and grabs the name
+
+ $self->{scalar_re} = qr{
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ (\w+?) # grab scalar name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ }xi ; # case insensitive
+
+#print "RE <$self->{scalar_re}>\n" ;
+
+# this grabs the body of a chunk in either greedy or non-greedy modes
+
+ my $chunk_body = $self->{greedy_chunk} ? qr/.+/s : qr/.+?/s ;
+
+# this matches a marked chunk and grabs its name and text body
+
+ $self->{chunk_re} = qr{
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ START # required START token
+ \s+ # required whitespace
+ (\w+?) # grab the chunk name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ ($chunk_body) # grab the chunk body
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ END # required END token
+ \s+ # required whitespace
+ \1 # match the grabbed chunk name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ }xi ; # case insensitive
+
+#print "RE <$self->{chunk_re}>\n" ;
+
+# this matches a include markup and grabs its template name
+
+ $self->{include_re} = qr{
+ $self->{pre_delim}
+ \s* # optional leading whitespace
+ INCLUDE # required INCLUDE token
+ \s+ # required whitespace
+ (\w+?) # grab the included template name
+ \s* # optional trailing whitespace
+ $self->{post_delim}
+ }xi ; # case insensitive
+
+# load in any templates
+
+ $self->add_templates( $opts{templates} ) ;
+
+ return $self ;
+}
+
+
+
+sub expand {
+
+ my( $self, $template, $data ) = @_ ;
+
+# make a copy if a scalar ref is passed as the template text is
+# modified in place
+
+ my $tmpl_ref = ref $template eq 'SCALAR' ? $template : \$template ;
+
+ my $expanded = $self->_expand_includes( $tmpl_ref ) ;
+
+#print "INC EXP <$expanded>\n" ;
+
+ $expanded = eval {
+ $self->_expand_chunk( $expanded, $data ) ;
+ } ;
+
+ croak "Template::Simple $@" if $@ ;
+
+ return $expanded ;
+}
+
+sub _expand_includes {
+
+ my( $self, $tmpl_ref ) = @_ ;
+
+# make a copy of the initial template so we can expand it.
+
+ my $expanded = ${$tmpl_ref} ;
+
+# loop until we can expand no more include markups
+
+ 1 while $expanded =~
+ s{$self->{include_re}}
+ { ${ $self->_get_template($1) }
+ }e ;
+
+ return \$expanded ;
+}
+
+my %expanders = (
+
+ HASH => \&_expand_hash,
+ ARRAY => \&_expand_array,
+ CODE => \&_expand_code,
+# if no ref then data is a scalar so replace the template with just the data
+ '' => sub { \$_[2] },
+) ;
+
+sub _expand_chunk {
+
+ my( $self, $tmpl_ref, $data ) = @_ ;
+
+#print "T ref [$tmpl_ref] [$$tmpl_ref]\n" ;
+#print "CHUNK TMPL\n<$$tmpl_ref>\n" ;
+
+#print Dumper $data ;
+
+ return \'' unless defined $data ;
+
+# now expand this chunk based on the type of data
+
+ my $expander = $expanders{ref $data} ;
+
+#print "EXP $expander\nREF ", ref $data, "\n" ;
+
+ die "unknown template data type '$data'\n" unless defined $expander ;
+
+ return $self->$expander( $tmpl_ref, $data ) ;
+}
+
+sub _expand_hash {
+
+ my( $self, $tmpl_ref, $href ) = @_ ;
+
+ return $tmpl_ref unless keys %{$href} ;
+
+# print "T ref [$tmpl_ref] [$$tmpl_ref]\n" ;
+# print "HASH TMPL\n$$tmpl_ref\n" ;
+
+# we need a local copy of the template to expand
+
+ my $expanded = ${$tmpl_ref} ;
+
+# recursively expand all top level chunks in this chunk
+
+ $expanded =~ s{$self->{chunk_re}}
+ {
+ # print "CHUNK $1\nBODY\n----\n<$2>\n\n------\n" ;
+ ${$self->_expand_chunk( \$2, $href->{$1} ) }}gex ;
+
+# now expand scalars
+
+#print "HASH TMPL\n<$expanded>\n" ;
+#print Dumper $href ;
+
+ $expanded =~ s{$self->{scalar_re}}
+ {
+ #print "SCALAR $1 VAL $href->{$1}\n" ;
+ defined $href->{$1} ? $href->{$1} : '' }ge ;
+
+#print "HASH2 TMPL\n$$expanded\n" ;
+
+ return \$expanded ;
+}
+
+sub _expand_array {
+
+ my( $self, $tmpl_ref, $aref ) = @_ ;
+
+# expand this $tmpl_ref for each element of the aref and join them
+
+ my $expanded ;
+
+#print Dumper $aref ;
+
+ $expanded .= ${$self->_expand_chunk( $tmpl_ref, $_ )} for @{$aref} ;
+
+ return \$expanded ;
+}
+
+sub _expand_code {
+
+ my( $self, $tmpl_ref, $cref ) = @_ ;
+
+ my $expanded = $cref->( $tmpl_ref ) ;
+
+ croak <<CROAK if ref $expanded ne 'SCALAR' ;
+code expansion didn't return a scalar or scalar reference
+CROAK
+
+ return $expanded ;
+}
+
+sub add_templates {
+
+ my( $self, $tmpls ) = @_ ;
+
+#print Dumper $tmpls ;
+ return unless defined $tmpls ;
+
+ ref $tmpls eq 'HASH' or croak "templates argument is not a hash ref" ;
+
+ @{ $self->{templates}}{ keys %{$tmpls} } =
+ map ref $_ eq 'SCALAR' ? \"${$_}" : \"$_", values %{$tmpls} ;
+
+#print Dumper $self->{templates} ;
+
+ return ;
+}
+
+sub delete_templates {
+
+ my( $self, @names ) = @_ ;
+
+ @names = keys %{$self->{templates}} unless @names ;
+
+ delete @{$self->{templates}}{ @names } ;
+
+ delete @{$self->{template_paths}}{ @names } ;
+
+ return ;
+}
+
+sub _get_template {
+
+ my( $self, $tmpl_name ) = @_ ;
+
+#print "INC $tmpl_name\n" ;
+
+ my $tmpls = $self->{templates} ;
+
+# get the template from the cache and send it back if it was found there
+
+ my $template = $tmpls->{ $tmpl_name } ;
+ return $template if $template ;
+
+# not found, so find, slurp in and cache the template
+
+ $template = $self->_find_template( $tmpl_name ) ;
+ $tmpls->{ $tmpl_name } = $template ;
+
+ return $template ;
+}
+
+sub _find_template {
+
+ my( $self, $tmpl_name ) = @_ ;
+
+ foreach my $dir ( @{$self->{include_paths}} ) {
+
+ my $tmpl_path = "$dir/$tmpl_name.tmpl" ;
+
+print "PATH: $tmpl_path\n" ;
+ next unless -r $tmpl_path ;
+
+# cache the path to this template
+
+ $self->{template_paths}{$tmpl_name} = $tmpl_path ;
+
+# slurp in the template file and return it as a scalar ref
+
+ return scalar read_file( $tmpl_path, scalar_ref => 1 ) ;
+ }
+
+ croak <<CROAK ;
+can't find template '$tmpl_name' in '@{$self->{include_paths}}'
+CROAK
+
+}
+
+1; # End of Template::Simple
+
+__END__
+
+=head1 NAME
+
+Template::Simple - A simple and fast template module
+
+=head1 VERSION
+
+Version 0.01
+
+=head1 SYNOPSIS
+
+ use Template::Simple;
+
+ my $tmpl = Template::Simple->new();
+
+ my $template = <<TMPL ;
+[%INCLUDE header%]
+[%START row%]
+ [%first%] - [%second%]
+[%END row%]
+[%INCLUDE footer%]
+TMPL
+
+ my $data = {
+ header_data => {
+ date => 'Jan 1, 2008',
+ author => 'Me, myself and I',
+ },
+ row => [
+ {
+ first => 'row 1 value 1',
+ second => 'row 1 value 2',
+ },
+ {
+ first => 'row 2 value 1',
+ second => 'row 2 value 2',
+ },
+ ],
+ footer_data => {
+ modified => 'Aug 31, 2006',
+ },
+ } ;
+
+ my $expanded = $tmpl->expand( $template, $data ) ;
+
+=head1 DESCRIPTION
+
+Template::Simple has these goals:
+
+=over 4
+
+=item * Support most common template operations
+
+It can recursively include other templates, replace tokens (scalars),
+recursively expand nested chunks of text and expand lists. By using
+simple idioms you can get conditional expansions.
+
+=item * Complete isolation of template from program code
+
+This is very important as template design can be done by different
+people than the program logic. It is rare that one person is well
+skilled in both template design and also programming.
+
+=item * Very simple template markup (only 4 markups)
+
+The only markups are C<INCLUDE>, C<START>, C<END> and C<token>. See
+MARKUP for more.
+
+=item * Easy to follow expansion rules
+
+Expansion of templates and chunks is driven from a data tree. The type
+of the data element used in an expansion controls how the expansion
+happens. The data element can be a scalar or scalar reference or an
+array, hash or code reference.
+
+=item * Efficient template expansion
+
+Expansion is very simple and uses Perl's regular expressions
+efficiently. Because the markup is so simple less processing is needed
+than many other templaters. Precompiling templates is not supported
+yet but that optimization is on the TODO list.
+
+=item * Easy user extensions
+
+User code can be called during an expansion so you can do custom
+expansions and plugins. Closures can be used so the code can have its
+own private data for use in expanding its template chunk.
+
+=back
+
+=head2 new()
+
+You create a Template::Simple by calling the class method new:
+
+ my $tmpl = Template::Simple->new() ;
+
+All the arguments to C<new()> are key/value options that change how
+the object will do expansions.
+
+=over 4
+
+=item pre_delim
+
+This option sets the string or regex that is the starting delimiter
+for all markups. You can use a plain string or a qr// but you need to
+escape (with \Q or \) any regex metachars if you want them to be plain
+chars. The default is qr/\[%/.
+
+ my $tmpl = Template::Simple->new(
+ pre_delim => '<%',
+ );
+
+ my $expanded = $tmpl->expand( '<%FOO%]', 'bar' ) ;
+
+=item post_delim
+
+This option sets the string or regex that is the ending delimiter
+for all markups. You can use a plain string or a qr// but you need to
+escape (with \Q or \) any regex metachars if you want them to be plain
+chars. The default is qr/%]/.
+
+ my $tmpl = Template::Simple->new(
+ post_delim => '%>',
+ );
+
+ my $expanded = $tmpl->expand( '[%FOO%>', 'bar' ) ;
+
+=item greedy_chunk
+
+This boolean option will cause the regex that grabs a chunk of text
+between the C<START/END> markups to become greedy (.+). The default is
+a not-greedy grab of the chunk text. (UNTESTED)
+
+=item templates
+
+This option lets you load templates directly into the cache of the
+Template::Simple object. This cache will be searched by the C<INCLUDE>
+markup which will be replaced by the template if found. The option
+value is a hash reference which has template names (the name in the
+C<INCLUDE> markup) for keys and their template text as their
+values. You can delete or clear templates from the object cache with
+the C<delete_template> method.
+
+
+ my $tmpl = Template::Simple->new(
+ templates => {
+
+ foo => <<FOO,
+[%baz%] is a [%quux%]
+FOO
+ bar => <<BAR,
+[%user%] is not a [%fool%]
+BAR
+ },
+ );
+
+ my $template = <<TMPL ;
+[%INCLUDE foo %]
+TMPL
+
+ my $expanded = $tmpl->expand(
+ $template,
+ {
+ baz => 'blue',
+ quux => 'color,
+ }
+ ) ;
+
+=item include_paths
+
+Template::Simple can also load C<INCLUDE> templates from files. This
+option lets you set the directory paths to search for those
+files. Note that the template name in the C<INCLUDE> markup has the
+.tmpl suffix appended to it when searched for in one of these
+paths. The loaded file is cached inside the Template::Simple object
+along with any loaded by the C<templates> option.
+
+=back
+
+=head1 METHODS
+
+=head2 expand( $template, $data )
+
+=head2 add_templates
+
+This method adds templates to the object cache. It takes a list of template names and texts just like the C<templates> constructor option.
+
+ $tmpl->add_templates(
+ {
+ foo => \$foo_template,
+ bar => '[%include bar%]',
+ }
+ ) ;
+
+=head2 delete_templates
+
+This method takes a list of template names and will delete them from
+the template cache in the object. If you pass in an empty list then
+all the templates will be deleted. This can be used when you know a
+template file has been updated and you want to get it loaded back into
+the cache. Note that you can delete templates that were loaded
+directly (via the C<templates> constructor option or the
+C<add_templates> method) or loaded from a file.
+
+ # this deletes only the foo and bar templates from the object cache
+
+ $tmpl->delete_templates( qw( foo bar ) ;
+
+ # this deletes all of templates from the object cache
+
+ $tmpl->delete_templates() ;
+
+=head2 get_dependencies
+
+This method expand the only C<INCLUDE> markups of a template and it
+returns a list of the file paths that were found and loaded. It is
+meant to be used to build up a dependency list of included templates
+for a main template. Typically this can be called from a script (see
+TODO) that will do this for a set of main templates and will generate
+Makefile dependencies for them. Then you can regenerate expanded
+templates only when any of their included templates have changed. It
+takes a single argument of a template.
+
+UNKNOWN: will this require a clearing of the cache or will it do the
+right thing on its own? or will it use the file path cache?
+
+ my @dependencies =
+ $tmpl->get_dependencies( '[%INCLUDE top_level%]' );
+
+=head1 MARKUP
+
+All the markups in Template::Simple use the same delimiters which are
+C<[%> and C<%]>. You can change the delimiters with the C<pre_delim>
+and C<post_delim> options in the C<new()> constructor.
+
+=head2 Tokens
+
+A token is a single markup with a C<\w+> Perl word inside. The token
+can have optional whitespace before and after it. A token is replaced
+by a value looked up in a hash with the token as the key. The hash
+lookup keeps the same case as parsed from the token markup.
+
+ [% foo %] [%BAR%]
+
+Those will be replaced by C<$href->{foo}> and C<$href->{BAR}> assuming
+C<$href> is the current data for this expansion. Tokens are only
+parsed out during hash data expansion so see Hash Data for more.
+
+=head2 Chunks
+
+Chunks are regions of text in a template that are marked off with a
+start and end markers with the same name. A chunk start marker is
+C<[%START name%]> and the end marker for that chunk is C<[%END
+name%]>. C<name> is a C<\w+> Perl word which is the name of this
+chunk. The whitespace between C<START/END> and C<name> is required and
+there is optional whitespace before C<START/END> and after the
+C<name>. C<START/END> are case insensitive but the C<name>'s case is
+kept. C<name> must match in the C<START/END> pair and it used as a key
+in a hash data expansion. Chunks are the primary way to markup
+templates for structures (sets of tokens), nesting (hashes of hashes),
+repeats (array references) and callbacks to user code. Chunks are only
+parsed out during hash data expansion so see Hash Data for more.
+
+The body of text between the C<START/END> markups is grabbed with a
+C<.+?> regular expression with the /s option enabled so it will match
+all characters. By default it will be a non-greedy grab but you can
+change that in the constructor by enabling the C<greedy_chunk> option.
+
+ [%Start FOO%]
+ [% START bar %]
+ [% field %]
+ [% end bar %]
+ [%End FOO%]
+
+=head2 Includes
+
+=head1 EXPANSION RULES
+
+Template::Simple has a short list of expansion rules and they are easy
+to understand. There are two types of expansions, include expansion
+and chunk expansion. In the C<expand> method, the template is an
+unnamed top level chunk of text and it first gets its C<INCLUDE>
+markups expanded. The text then undergoes a chunk expansion and a
+scalar reference to that expanded template is returned to the caller.
+
+=head2 Include Expansion
+
+Include expansion is performed one time on a top level template. When
+it is done the template is ready for chunk expansion. Any markup of
+the form C<[%INCLUDE name]%> will be replaced by the text found in the
+template C<name>. The template name is looked up in the object's
+template cache and if it is found there its text is used as the
+replacement.
+
+If a template is not found in the cache, it will be searched for in
+the list of directories in the C<include_paths> option. The file name
+will be a directory in that list appended with the template name and
+the C<.tmpl> suffix. The first template file found will be read in and
+stored in the cache. Its path is also saved and those will be returned
+in the C<get_dependencies> method. See the C<add_templates> and
+C<delete_templates> methods and the C<include_paths> option.
+
+Expanded include text can contain more C<INCLUDE> markups and they
+will also be expanded. The include expansion phase ends where there
+are no more C<INCLUDE> found.
+
+=head2 Chunk Expansion
+
+A chunk is the text found between C<START> and C<END> markups and it
+gets its named from the C<START> markup. The top level template is
+considered an unamed chunk and also gets chunk expanded.
+
+The data for a chunk determines how it will be expanded. The data can
+be a scalar or scalar reference or an array, hash or code
+reference. Since chunks can contain nested chunks, expansion will
+recurse down the data tree as it expands the chunks. Each of these
+expansions are explained below. Also see the IDIOMS and BEST PRACTICES
+section for examples and used of these expansions.
+
+=head2 Scalar Data Expansion
+
+If the current data for a chunk is a scalar or scalar reference, the
+chunk's text in the templated is replaced by the scalar's value. This
+can be used to overwrite one default section of text with from the
+data tree.
+
+=head2 Code Data Expansion
+
+If the current data for a chunk is a code reference (also called
+anonymous sub) then the code reference is called and it is passed a
+scalar reference to the that chunk's text. The code must return a
+scalar or a scalar reference and its value replaces the chunk's text
+in the template. If the code returns any other type of data it is a
+fatal error. Code expansion is how you can do custom expansions and
+plugins. A key idiom is to use closures as the data in code expansions
+and keep the required outside data in the closure.
+
+=head2 Array Data Expansion
+
+If the current data for a chunk is an array reference do a full chunk
+expansion for each value in the array. It will replace the original
+chunk text with the joined list of expanded chunks. This is how you do
+repeated sections in Template::Simple and why there is no need for any
+loop markups. Note that this means that expanding a chunk with $data
+and [ $data ] will do the exact same thing. A value of an empty array
+C<[]> will cause the chunk to be replaced by the empty string.
+
+=head2 Hash Data Expansion
+
+If the current data for a chunk is a hash reference then two phases of
+expansion happen, nested chunk expansion and token expansion. First
+nested chunks are parsed of of this chunk along with their names. Each
+parsed out chunk is expanded based on the value in the current hash
+with the nested chunk's name as the key.
+
+If a value is not found (undefined), then the nested chunk is replaced
+by the empty string. Otherwise the nested chunk is expanded according
+to the type of its data (see chunk expansion) and it is replaced by
+the expanded text.
+
+Chunk name and token lookup in the hash data is case sensitive (see
+the TODO for cased lookups).
+
+Note that to keep a plain text chunk or to just have the all of its
+markups (chunks and tokens) be deleted just pass in an empty hash
+reference C<{}> as the data for the chunk. It will be expanded but all
+markups will be replaced by the empty string.
+
+=head2 Token Expansion
+
+The second phase is token expansion. Markups of the form [%token%] are
+replaced by the value of the hash element with the token as the
+key. If a token's value is not defined it is replaced by the empty
+string. This means if a token key is missing in the hash or its value
+is undefined or its value is the empty string, the [%token%] markup
+will be deleted in the expansion.
+
+=head1 IDIOMS and BEST PRACTICES
+
+With all template systems there are better ways to do things and
+Template::Simple is no different. This section will show some ways to
+handle typical template needs while using only the 4 markups in this
+module.
+
+=head2 Conditionals
+
+This conditional idiom can be when building a fresh data tree or
+modifying an existing one.
+
+ $href->{$chunk_name} = $keep_chunk ? {} : '' ;
+
+If you are building a fresh data tree you can use this idiom to do a
+conditional chunk:
+
+ $href->{$chunk_name} = {} if $keep_chunk ;
+
+To handle an if/else conditional use two chunks, with the else chunk's
+name prefixed with NOT_ (or use any name munging you want). Then you
+set the data for either the true chunk (just the plain name) or the
+false trunk with the NOT_ name. You can use a different name for the
+else chunk if you want but keeping the names of the if/else chunks
+related is a good idea. Here are two ways to set the if/else data. The
+first one uses the same data for both the if and else chunks and the
+second one uses different data so the it uses the full if/else code
+for that.
+
+ $href->{ ($boolean ? '' : 'NOT_') . $chunk_name} = $data
+
+ if ( $boolean ) {
+ $href->{ $chunk_name} = $true_data ;
+ else {
+ $href->{ "NOT_$chunk_name" } = $false_data ;
+ }
+
+NOTE TO ALPHA USERS: i am also thinking that a non-existing key or
+undefined hash value should leave the chunk as is. then you would need
+to explicitly replace a chunk with the empty string if you wanted it
+deleted. It does affect the list of styles idiom. Any thoughts on
+this change of behavior? Since this hasn't been released it is the
+time to decide this.
+
+=head2 Chunked Includes
+
+One of the benefits of using include templates is the ability to share
+and reuse existing work. But if an included template has a top level
+named chunk, then that name would also be the same everywhere where
+this template is included. If a template included another template in
+multiple places, its data tree would use the same name for each and
+not allow unique data to be expanded for each include. A better way is
+to have the current template wrap an include markup in a named chunk
+markup. Then the data tree could use unique names for each included
+template. Here is how it would look:
+
+ [%START foo_prime%][%INCLUDE foo%][%START foo_prime%]
+ random noise
+ [%START foo_second%][%INCLUDE foo%][%START foo_second%]
+
+See the TODO section for some ideas on how to make this even more high level.
+
+=head2 Repeated Sections
+
+If you looked at the markup of Template::Simple you have noticed that
+there is no loop or repeat construct. That is because there is no need
+for one. Any chunk can be expanded in a loop just by having its
+expansion data be an anonymous array. The expander will loop over each
+element of the array and do a fresh expansion of the chunk with this
+data. A join (on '') of the list of expansions replaces the original
+chunk and you have a repeated chunk.
+
+=head2 A List of Mixed Styles
+
+One formating style is to have a list of sections each which can have
+its own style or content. Template::Simple can do this very easily
+with just a 2 level nested chunk and an array of data for
+expansion. The outer chunk includes (or contains) each of the desired
+styles in any order. It looks like this:
+
+ [%START para_styles%]
+ [%START main_style%]
+ [%INCLUDE para_style_main%]
+ [%END main_style%]
+ [%START sub_style%]
+ [%INCLUDE para_style_sub%]
+ [%END sub_style%]
+ [%START footer_style%]
+ [%INCLUDE para_style_footer%]
+ [%END footer_style%]
+ [%END para_styles%]
+
+The other part to make this work is in the data tree. The data for
+para_styles should be a list of hashes. Each hash contains the data
+for one pargraph style which is keyed by the style's chunk name. Since
+the other styles's chunk names are not hash they are deleted. Only the
+style which has its name as a key in the hash is expanded. The data
+tree would look something like this:
+
+ [
+ {
+ main_style => $main_data,
+ },
+ {
+ sub_style => $sub_data,
+ },
+ {
+ sub_style => $other_sub_data,
+ },
+ {
+ footer_style => $footer_data,
+ },
+ ]
+
+=head1 TESTS
+
+The test scripts use a common test driver module in t/common.pl. It is
+passed a list of hashes, each of which has the data for one test. A
+test can create a ne Template::Simple object or use the one from the
+previous test. The template source, the data tree and the expected
+results are also important keys. See the test scripts for examples of
+how to write tests using this common driver.
+
+=over 4
+
+=item name
+
+This is the name of the test and is used by Test::More
+
+=item opts
+
+This is a hash ref of the options passed to the Template::Simple
+constructor. The object is not built if the C<keep_obj> key is set.
+
+=item keep_obj
+
+If set, this will make this test keep the Template::Simple object from
+the previous test and not build a new one.
+
+=item template
+
+This is the template to expand for this test.
+
+=item data
+
+This is the data tree for the expansion of the template.
+
+=item expected
+
+This is the text that is expected after the expansion.
+
+=item skip
+
+If set, this test is skipped.
+
+=back
+
+=head1 TODO
+
+Even though this template system is simple, that doesn't mean it can't
+be extended in many ways. Here are some features and designs that
+would be good extensions which add useful functionality without adding
+too much complexity.
+
+=head2 Compiled Templates
+
+A commonly performed optimization in template modules is to precompile
+(really preparse) templates into a internal form that will expand
+faster. Precompiling is slower than expansion from the original
+template which means you won't want to do it for each expansion. This
+means it has a downside that you lose out when you want to expand
+using templates which change often. Template::Simple makes it very
+easy to precompile as it already has the regexes to parse out the
+markup. So instead of calling subs to do actual expansion, a
+precompiler would call subs to generate a compiled expansion tree.
+The expansion tree can then be run or processes with expansion data
+passed to it. You can think of a precompiled template as having all
+the nested chunks be replaced by nested code that does the same
+expansion. It can still do the dynamic expansion of the data but it
+saves the time of parsing the template souice. There are three
+possible internal formats for the precompiled template:
+
+=over 4
+
+=item Source code
+
+This precompiler will generate source code that can be stored and/or
+eval'ed. The eval'ed top level sub can then be called and passed the
+expansion data.
+
+=item Closure call tree
+
+The internal format can be a nested set of closures. Each closure would contain
+private data such as fixed text parts of the original template, lists
+of other closures to run, etc. It is trivial to write a basic closure
+generator which will make build this tree a simple task.
+
+=item Code ref call tree
+
+This format is a Perl data tree where the nodes have a code reference
+and its args (which can be nested instances of the same
+nodes). Instead of executing this directly, you will need a small
+interpreter to execute all the code refs as it runs through the tree.
+
+This would make for a challenging project to any intermediate Perl
+hacker. It just involves knowing recursion, data trees and code refs.
+Contact me if you are interested in doing this.
+
+=back
+
+=head2 Cased Hash Lookups
+
+One possible option is to allow hash expansions to always use upper or
+lower cased keys in their lookups.
+
+=head2 Expand tokens before includes and chunks
+
+Currently tokens are expanded after includes and chunks. If tokens
+were expanded in a pass before the others, the include and chunk names
+could be dynamically set. This would make it harder to precompile
+templates as too much would be dynamic, i.e. you won't know what the
+fixed text to parse out is since anything can be included at expand
+time. But the extra flexibility of changing the include and chunk
+names would be interesting. It could be done easily and enabled by an
+option.
+
+=head2 Plugins
+
+There are two different potential areas in Template::Simple that could
+use plugins. The first is with the expansion of chunkas and
+dispatching based on the data type. This dispatch table can easily be
+replaced by loaded modules which offer a different way to
+expand. These include the precompiled expanders mentioned above. The
+other area is with code references as the data type. By defining a
+closure (or a closure making) API you can create different code refs
+for the expansion data. The range of plugins is endless some of the
+major template modules have noticed. One idea is to make a closure
+which contains a different Template::Simple object than the current
+one. This will allow expansion of a nested chunk with different rules
+than the current chunk being expanded.
+
+=head2 Data Escaping
+
+Some templaters have options to properly escape data for some types of
+text files such as html. this can be done with some variant of the
+_expand_hash routine which also does the scalar expansion (which is
+where data is expanded). The expanding scalars code could be factored
+out into a set of subs one of which is used based on any escaping
+needs.
+
+=head2 Data Tree is an Object
+
+This is a concept I don't like but it was requested so it goes into
+the TODO file. Currently C<expand> can only be passed a regular
+(unblessed) ref (or a scalar) for its data tree. Passing in an object
+would break encapsulation and force the object layout to be a hash
+tree that matches the layout of the template. I doubt that most
+objects will want to be organized to match a template. I have two
+ideas, one is that you add a method to that object that builds up a
+proper (unblessed) data tree to pass to C<expand>. The other is by
+subclassing C<Template::Simple> and overriding C<expand> with a sub
+that does take an object hash and it can unbless it or build a proper
+data tree and then call C<expand> in SUPER::. A quick solution is to
+use C<reftype> (from Scalar::Utils) instead of C<ref> to allow object
+hashes to be passed in.
+
+=head2 Includes and Closure Synergy
+
+By pairing up an include template along with code that can generate
+the appropriate data tree for its expansion, you can create a higher
+level template framework (the synergy). Additional code can be
+associated with them that will handle input processing and
+verification for the templates (e.g. web forms) that need it. A key to
+this will be making all the closures for the data tree. This can be
+greatly simplified by using a closure maker sub that can create all
+the required closures.
+
+=head2 Metafields and UI Generation
+
+Taking the synergy up to a much higher level is the concept of meta
+knowledge of fields which can generate templates, output processing
+(data tree generation), input processing, DB backing and more. If you
+want to discuss such grandiose wacky application schemes in a long
+rambling mind bending conversation, please contact me.
+
+=head2 More Examples and Idioms
+
+As I convert several scripts over to this module (they all used the
+hack version), I will add them to an examples section or possibly put
+them in another (pod only) module. Similarly the Idioms section needs
+expansion and could be also put into a pod module. One goal requested
+by an early alpha tester is to keep the primary docs as simple as the
+markup itself. This means moving all the extra stuff (and plenty of
+that) into other pod modules. All the pod modules would be in the same
+cpan tarball so you get all the docs and examples when you install
+this.
+
+=head1 AUTHOR
+
+Uri Guttman, C<< <uri at sysarch.com> >>
+
+=head1 BUGS
+
+Please report any bugs or feature requests to
+C<bug-template-simple at rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Template-Simple>.
+I will be notified, and then you'll automatically be notified of progress on
+your bug as I make changes.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc Template::Simple
+
+You can also look for information at:
+
+=over 4
+
+=item * RT: CPAN's request tracker
+
+L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Simple>
+
+=item * Search CPAN
+
+L<http://search.cpan.org/dist/Template-Simple>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+I wish to thank Turbo10 for their support in developing this module.
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2006 Uri Guttman, all rights reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
+
+
+find templates and tests
+
+deep nesting tests
+
+greedy tests
+
+methods pod
+
+delete_templates test
+
+pod cleanup
+
+fine edit
+
+more tests
+
+slurp dependency in makefile.pl
+
--- /dev/null
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+ use_ok( 'Template::Simple' );
+}
+
+#diag( "Testing Template::Simple $Template::Simple::VERSION, Perl $], $^X" );
--- /dev/null
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+sub not_in_file_ok {
+ my ($filename, %regex) = @_;
+ open my $fh, "<", $filename
+ or die "couldn't open $filename for reading: $!";
+
+ my %violated;
+
+ while (my $line = <$fh>) {
+ while (my ($desc, $regex) = each %regex) {
+ if ($line =~ $regex) {
+ push @{$violated{$desc}||=[]}, $.;
+ }
+ }
+ }
+
+ if (%violated) {
+ fail("$filename contains boilerplate text");
+ diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
+ } else {
+ pass("$filename contains no boilerplate text");
+ }
+}
+
+not_in_file_ok(README =>
+ "The README is used..." => qr/The README is used/,
+ "'version information here'" => qr/to provide version information/,
+);
+
+not_in_file_ok(Changes =>
+ "placeholder date/time" => qr(Date/time)
+);
+
+sub module_boilerplate_ok {
+ my ($module) = @_;
+ not_in_file_ok($module =>
+ 'the great new $MODULENAME' => qr/ - The great new /,
+ 'boilerplate description' => qr/Quick summary of what the module/,
+ 'stub function definition' => qr/function[12]/,
+ );
+}
+
+module_boilerplate_ok('lib/Template/Simple.pm');
--- /dev/null
+#!perl
+
+use strict ;
+use lib qw(t) ;
+use common ;
+
+use File::Slurp ;
+use Data::Dumper ;
+
+my $tests = [
+
+ {
+ name => 'bug',
+ skip => 0,
+ opts => {
+
+ pre_delim => qr/\[\-/,
+ post_delim => qr/\-\]/,
+ },
+ },
+ data => {
+ widgets => [
+ {
+ title => "bart"
+ },
+ {
+ title => "marge",
+ }
+ ],
+ },
+
+
+
+<table width="100%" border=1>
+ [-start widgets-]
+ <tr>
+ <td>[-anchor-]</td>
+ <td>
+ <b>[-title-]</b>
+ <br>[-description-]
+ </td>
+ <td>[-escaped_anchor-]</td>
+ <td>[-options-]</td>
+ </tr>
+ [-end widgets-]
+</table>
+
+
+ expected => 'bar',
+ },
+] ;
+
+
+write_tmpl_files() ;
+
+template_tester( $tests ) ;
+
+#remove_tmpl_files() ;
+
+exit ;
+
+
+sub write_tmpl_files {
+
+ mkdir $_, 0755 for @tmpl_dirs ;
+
+ while( my( $file, $tmpl ) = each %tmpl_files ) {
+
+ write_file( $file, $tmpl ) ;
+ }
+}
+
+sub remove_tmpl_files {
+
+ unlink keys %tmpl_files ;
+
+ rmdir $_ for reverse @tmpl_dirs ;
+}
+#!/usr/local/bin/perl
--- /dev/null
+# common.pm - common test driver code
+
+use Test::More ;
+use Template::Simple ;
+
+sub template_tester {
+
+ my( $tests ) = @_ ;
+
+# plan for one expected ok() call per test
+
+ plan( tests => scalar @{$tests} ) ;
+
+ my( $obj, $tmpl ) ;
+
+# loop over all the tests
+
+ foreach my $test ( @{$tests} ) {
+
+ if ( $test->{skip} ) {
+ ok( 1, "SKIPPING $test->{name}" ) ;
+ next ;
+ }
+
+ unless( $obj && $test->{keep_obj} ) {
+
+# if there is no kept object, we will constuct one
+
+ $obj = eval {
+ Template::Simple->new(
+ %{ $test->{opts} || {} }
+ ) ;
+ } ;
+
+print $@ if $@ ;
+
+# check for expected errors
+# no errors in new() to catch (yet)
+
+ }
+
+ $test->{obj} = $obj ;
+
+# see if we use the test's template or keep the previous one
+
+ $tmpl = $test->{template} if defined $test->{template} ;
+
+# run any setup sub before this test. this can is used to modify the
+# object for this test (e.g. delete templates from the cache).
+
+ if( my $pretest = $test->{pretest} ) {
+
+ $pretest->($test) ;
+ }
+
+# get any existing template object
+
+# render the template and catch any fatal errors
+
+ my $rendered = eval {
+ $obj->render( $tmpl, $test->{data} ) ;
+ } ;
+
+#print "ERR $@\n" if $@;
+
+# if we had an error and expected it, we pass this test
+
+ if ( $@ ) {
+
+ if ( $test->{error} && $@ =~ /$test->{error}/ ) {
+
+ ok( 1, $test->{name} ) ;
+ }
+ else {
+
+ print "unexpected error: $@\n" ;
+ ok( 0, $test->{name} ) ;
+ }
+ next ;
+ }
+
+# see if the expansion was what we expected
+
+ my $ok = ${$rendered} eq $test->{expected} ;
+
+# dump any bad expansions
+
+ print <<ERR unless $ok ;
+RENDERED
+[${$rendered}]
+EXPECTED
+[$test->{expected}]
+------
+ERR
+
+# report success/failure for this test
+
+ ok( $ok, $test->{name} ) ;
+ }
+}
+
+1 ;
--- /dev/null
+#!perl
+
+use lib qw(t) ;
+use common ;
+
+my $tests = [
+
+ {
+ name => 'unknown data type',
+ opts => {},
+ data => qr//,
+ template => <<TMPL,
+foo
+TMPL
+ expected => <<EXPECT,
+bar
+EXPECT
+ error => qr/unknown template data/,
+ },
+
+ {
+ name => 'missing include',
+ skip => 0,
+ data => {},
+ template => '[%INCLUDE foo%]',
+ error => qr/can't find/,
+ },
+
+ {
+ name => 'code data',
+ skip => 0,
+ data => sub { return '' },
+ template => 'bar',
+ error => qr/data callback/,
+ },
+
+
+] ;
+
+template_tester( $tests ) ;
+
+exit ;
+
--- /dev/null
+#!perl
+
+use strict ;
+use lib qw(t) ;
+use common ;
+
+use File::Slurp ;
+use Data::Dumper ;
+
+# these dirs must be in order to the deepest for rmdir to work properly
+
+my @tmpl_dirs = qw( templates templates/deeper templates/deeper/deepest ) ;
+
+my %tmpl_files = (
+
+ 'templates/FOO.tmpl' => <<FOO,
+this loads bar <[%include BAR%]>
+FOO
+ 'templates/deeper/BAR.tmpl' => <<BAR,
+{this should hide}
+BAR
+ 'templates/deeper/deepest/BAR.tmpl' => <<BAR,
+[this should be hidden then revealed]
+BAR
+
+) ;
+
+my $tests = [
+
+ {
+ name => 'simple include',
+ skip => 0,
+ opts => {
+
+ templates => {
+ 'foo' => 'bar',
+ }
+ },
+ data => {},
+ template => '[%INCLUDE foo%]',
+ expected => 'bar',
+ },
+ {
+ name => 'nested includes',
+ skip => 0,
+ opts => {
+ templates => {
+ foo => '[%include bar%]',
+ bar => 'quux',
+ },
+ },
+ data => {},
+ template => '[%INCLUDE foo%]',
+ expected => 'quux',
+ },
+ {
+ name => 'serial includes',
+ skip => 0,
+ opts => {
+ templates => {
+ foo => 'foo is here',
+ bar => 'bar is too',
+ quux => 'quux is on the drums',
+ },
+ },
+ data => {},
+ template => '[%INCLUDE foo%] [%INCLUDE bar%] [%INCLUDE quux%]',
+ expected => 'foo is here bar is too quux is on the drums',
+ },
+
+ {
+ name => 'missing include',
+ skip => 0,
+ data => {},
+ keep_obj => 1,
+ pretest => sub { $_[0]{obj}->delete_templates() },
+ error => qr/can't find/,
+ },
+
+ {
+ name => 'load include files',
+ skip => 0,
+ opts => {
+ include_paths => [ qw(
+ templates
+ templates/deeper
+ templates/deeper/deepest
+ ) ],
+ },
+ data => {},
+ template => '[%INCLUDE FOO%]',
+ expected => <<EXPECTED,
+this loads bar <{this should hide}
+>
+EXPECTED
+
+ },
+ {
+ name => 'use lower path',
+ skip => 0,
+ opts => {
+ include_paths => [ qw(
+ templates
+ templates/deeper/deepest
+ ) ],
+ },
+ data => {},
+ expected => <<EXPECTED,
+this loads bar <[this should be hidden then revealed]
+>
+EXPECTED
+
+ },
+ {
+ name => 'delete covering file',
+ skip => 0,
+ opts => {
+ include_paths => [ qw(
+ templates
+ templates/deeper
+ templates/deeper/deepest
+ ) ],
+ },
+ pretest => sub { unlink 'templates/deeper/BAR.tmpl' },
+ data => {},
+ expected => <<EXPECTED,
+this loads bar <[this should be hidden then revealed]
+>
+EXPECTED
+
+ },
+] ;
+
+
+write_tmpl_files() ;
+
+template_tester( $tests ) ;
+
+#remove_tmpl_files() ;
+
+exit ;
+
+
+sub write_tmpl_files {
+
+ mkdir $_, 0755 for @tmpl_dirs ;
+
+ while( my( $file, $tmpl ) = each %tmpl_files ) {
+
+ write_file( $file, $tmpl ) ;
+ }
+}
+
+sub remove_tmpl_files {
+
+ unlink keys %tmpl_files ;
+
+ rmdir $_ for reverse @tmpl_dirs ;
+}
--- /dev/null
+#!perl
+
+use strict ;
+use lib qw(t) ;
+use common ;
+
+use File::Slurp ;
+use Data::Dumper ;
+
+my $tests = [
+
+ {
+ name => 'nested bug [- -]',
+ skip => 0,
+ opts => {
+ pre_delim => qr/\[\-/,
+ post_delim => qr/\-\]/,
+ },
+ data => {
+ widgets => [
+ {
+ title => "bart",
+ },
+ {
+ title => "marge",
+ }
+ ],
+ },
+ template => <<TEMPLATE,
+<table width="100%" border=1>
+ [-start widgets-]
+ <tr>
+ <td>[-anchor-]</td>
+ <td>
+ <b>[-title-]</b>
+ <br>[-description-]
+ </td>
+ <td>[-escaped_anchor-]</td>
+ <td>[-options-]</td>
+ </tr>
+ [-end widgets-]
+</table>
+TEMPLATE
+
+ expected => <<EXPECTED,
+<table width="100%" border=1>
+
+ <tr>
+ <td></td>
+ <td>
+ <b>bart</b>
+ <br>
+ </td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>
+ <b>marge</b>
+ <br>
+ </td>
+ <td></td>
+ <td></td>
+ </tr>
+
+</table>
+EXPECTED
+ },
+ {
+ name => 'nested bug',
+ skip => 0,
+ data => {
+ widgets => [
+ {
+ title => "bart",
+ },
+ {
+ title => "marge",
+ }
+ ],
+ },
+ template => <<TEMPLATE,
+<table width="100%" border=1>
+ [%start widgets%]
+ <tr>
+ <td>[%anchor%]</td>
+ <td>
+ <b>[%title%]</b>
+ <br>[%description%]
+ </td>
+ <td>[%escaped_anchor%]</td>
+ <td>[%options%]</td>
+ </tr>
+ [%end widgets%]
+</table>
+TEMPLATE
+
+ expected => <<EXPECTED,
+<table width="100%" border=1>
+
+ <tr>
+ <td></td>
+ <td>
+ <b>bart</b>
+ <br>
+ </td>
+ <td></td>
+ <td></td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td>
+ <b>marge</b>
+ <br>
+ </td>
+ <td></td>
+ <td></td>
+ </tr>
+
+</table>
+EXPECTED
+ },
+ {
+ name => 'nested bug ,',
+ skip => 0,
+ opts => {
+ },
+ data => {
+ widgets => [
+ {
+ title => "bart",
+ },
+ {
+ title => "marge",
+ }
+ ],
+ },
+ template => <<TEMPLATE,
+,,,,,[%start widgets%]
+,,,,,,,,,{[%title%]}
+[% s %]
+,,,,,,,,,[%end widgets%]
+TEMPLATE
+
+ expected => <<EXPECTED,
+,,,,,
+,,,,,,,,,{bart}
+
+,,,,,,,,,
+,,,,,,,,,{marge}
+
+,,,,,,,,,
+EXPECTED
+ },
+ {
+ name => 'nested bug short',
+ skip => 0,
+ data => {
+ widgets => [
+ {
+ title => "bart",
+ },
+ {
+ title => "marge",
+ }
+ ],
+ },
+ template => <<TEMPLATE,
+ [%start widgets%]
+ <b>[%title%]</b>
+ [%end widgets%]
+TEMPLATE
+
+ expected => <<EXPECTED,
+
+ <b>bart</b>
+
+ <b>marge</b>
+
+EXPECTED
+ },
+
+] ;
+
+template_tester( $tests ) ;
+
+exit ;
+
+
--- /dev/null
+#!perl
+
+use lib qw(t) ;
+use common ;
+
+my $tests = [
+
+ {
+ name => 'pre_delim',
+ opts => {
+ pre_delim => '<%',
+ },
+ data => {
+ foo => 'FOO is 3',
+ BAR => 'bar is baz',
+ },
+ template => <<TEMPLATE,
+<%foo%]
+<%BAR%]
+TEMPLATE
+ expected => <<EXPECTED,
+FOO is 3
+bar is baz
+EXPECTED
+ },
+ {
+ name => 'post_delim',
+ opts => {
+ post_delim => '%>',
+ },
+ data => {
+ foo => 'FOO is 3',
+ BAR => 'bar is baz',
+ },
+ template => <<TEMPLATE,
+[%foo%>
+[%BAR%>
+TEMPLATE
+ expected => <<EXPECTED,
+FOO is 3
+bar is baz
+EXPECTED
+ },
+ {
+ name => 'pre/post_delim',
+ opts => {
+ pre_delim => '<%',
+ post_delim => '%>',
+ },
+ data => {
+ foo => 'FOO is 3',
+ BAR => 'bar is baz',
+ },
+ template => <<TEMPLATE,
+<%foo%>
+<%BAR%>
+TEMPLATE
+ expected => <<EXPECTED,
+FOO is 3
+bar is baz
+EXPECTED
+ },
+ {
+ name => 'pre/post_delim regexes',
+ opts => {
+ pre_delim => qr/A+/,
+ post_delim => qr/B+/,
+ },
+ data => {
+ foo => 'FOO is 3',
+ bAR => 'bar is baz',
+ },
+ template => <<TEMPLATE,
+AAAfooBBBBB
+AbARB
+TEMPLATE
+ expected => <<EXPECTED,
+FOO is 3
+bar is baz
+EXPECTED
+ },
+ {
+ name => 'chunk delim',
+ opts => {
+ pre_delim => '<%',
+ post_delim => '%>',
+ },
+ data => {
+ foo => { FOO => 3 },
+ bar => { BAR => 4 },
+ },
+ template => <<TEMPLATE,
+<%START foo%>
+<%FOO%>
+<%END foo%>
+<%START bar%><%BAR%><%END bar%>
+TEMPLATE
+ expected => <<EXPECTED,
+
+3
+
+4
+EXPECTED
+ },
+
+ {
+ name => 'chunk delim - array of hashes',
+ opts => {
+ pre_delim => '<%',
+ post_delim => '%>',
+ },
+ data => [
+ {
+ foo => { FOO => 3 },
+ bar => { BAR => 4 },
+ },
+ {
+ foo => { FOO => 6 },
+ bar => { BAR => 'quux' },
+ }
+ ],
+ template => <<TEMPLATE,
+<%START foo%>
+<%FOO%>
+<%END foo%>
+<%START bar%><%BAR%><%END bar%>
+TEMPLATE
+ expected => <<EXPECTED,
+
+3
+
+4
+
+6
+
+quux
+EXPECTED
+ },
+ {
+ name => 'greedy chunk',
+ opts => {
+ greedy_chunk => 1,
+ },
+ data => {
+ FOO => 'foo',
+ },
+ template => <<TEMPLATE,
+[%START FOO%]
+[%START FOO%]
+bar
+[%END FOO%]
+[%END FOO%]
+TEMPLATE
+ expected => <<EXPECTED,
+foo
+EXPECTED
+ },
+ {
+ name => 'not greedy chunk',
+ opts => {
+ greedy_chunk => 0,
+ },
+ data => {
+ FOO => 'foo',
+ },
+ template => <<TEMPLATE,
+[%START FOO%]
+[%START FOO%]
+bar
+[%END FOO%]
+[%END FOO%]
+TEMPLATE
+ expected => <<EXPECTED,
+foo
+[%END FOO%]
+EXPECTED
+ },
+] ;
+
+template_tester( $tests ) ;
+
+exit ;
--- /dev/null
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod::Coverage 1.04";
+print "ERR [$@]\n";
+plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@;
+all_pod_coverage_ok();
--- /dev/null
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod 1.14";
+plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
+all_pod_files_ok();
--- /dev/null
+#!perl
+
+use lib qw(t) ;
+use common ;
+
+my $tests = [
+
+ {
+# skip => 1,
+ name => 'simple scalar',
+ data => {
+ foo => 'FOO is 3',
+ BAR => 'bar is baz',
+ },
+ template => <<TEMPLATE,
+[%foo%]
+junk
+[%BAR%]
+TEMPLATE
+ expected => <<EXPECTED,
+FOO is 3
+junk
+bar is baz
+EXPECTED
+ },
+
+ {
+# skip => 1,
+ name => 'simple chunk - hash data',
+ data => {
+ foo => { FOO => 3 },
+ bar => { BAR => 4 },
+ },
+ template => <<TEMPLATE,
+[%START foo%]
+[%FOO%]
+[%END foo%]
+[%START bar%][%BAR%][%END bar%]
+TEMPLATE
+ expected => <<EXPECTED,
+
+3
+
+4
+EXPECTED
+ },
+
+ {
+# skip => 1,
+ name => 'simple chunk - scalar data',
+ data => {
+ foo => 3,
+ bar => { BAR => 4 },
+ },
+ template => <<TEMPLATE,
+[%START foo%]
+FOO
+[%END foo%]
+[%START bar%][%BAR%][%END bar%]
+TEMPLATE
+ expected => <<EXPECTED,
+3
+4
+EXPECTED
+ },
+ {
+# skip => 1,
+ name => 'simple chunk - array of hashes',
+ data => [
+ {
+ foo => { FOO => 3 },
+ bar => { BAR => 4 },
+ },
+ {
+ foo => { FOO => 6 },
+ bar => { BAR => 'quux' },
+ }
+ ],
+ template => <<TEMPLATE,
+[%START foo%]
+[%FOO%]
+[%END foo%]
+[%START bar%][%BAR%][%END bar%]
+TEMPLATE
+ expected => <<EXPECTED,
+
+3
+
+4
+
+6
+
+quux
+EXPECTED
+ },
+] ;
+
+template_tester( $tests ) ;
+
+exit ;
+
--- /dev/null
+#!perl
+
+use lib qw(t) ;
+use common ;
+
+my $tests = [
+
+ {
+ name => 'top level - scalar data',
+ data => 'bar',
+ template => <<TEMPLATE,
+junk
+TEMPLATE
+ expected => 'bar',
+ },
+ {
+ name => 'top level - array data',
+ data => [
+ "foo\n",
+ "bar\n",
+ ],
+ template => <<TEMPLATE,
+junk
+TEMPLATE
+ expected => <<EXPECTED,
+foo
+bar
+EXPECTED
+ },
+ {
+ name => 'top level - blessed array data',
+ data => bless( [
+ "foo\n",
+ "bar\n",
+ ] ),
+ template => <<TEMPLATE,
+junk
+TEMPLATE
+ expected => <<EXPECTED,
+foo
+bar
+EXPECTED
+ },
+ {
+ name => 'top level - code data',
+ data => sub { \uc ${$_[0]} },
+ template => <<TEMPLATE,
+junk
+TEMPLATE
+ expected => <<EXPECTED,
+JUNK
+EXPECTED
+ },
+] ;
+
+template_tester( $tests ) ;
+
+exit ;
+
--- /dev/null
+this loads bar <[%include BAR%]>
--- /dev/null
+[this should be hidden then revealed]