initial commit
Uri Guttman [Fri, 31 Oct 2008 01:40:05 +0000 (20:40 -0500)]
29 files changed:
.cvsignore [new file with mode: 0644]
.gitignore [new file with mode: 0644]
Changes [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
META.yml [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
Trexy/Template.pm [new file with mode: 0644]
bug.pl [new file with mode: 0644]
bug2.pl [new file with mode: 0644]
lib/Template/Simple.pm [new file with mode: 0644]
lib/Template/Simple.pm.expnad [new file with mode: 0644]
pm_to_blib [new file with mode: 0644]
s5-blank.zip [new file with mode: 0644]
t/00-load.t [new file with mode: 0644]
t/boilerplate.t [new file with mode: 0644]
t/bug.pl [new file with mode: 0644]
t/common.pm [new file with mode: 0644]
t/error.t [new file with mode: 0644]
t/include.t [new file with mode: 0644]
t/nested.t [new file with mode: 0644]
t/options.t [new file with mode: 0644]
t/pod-coverage.t [new file with mode: 0644]
t/pod.t [new file with mode: 0644]
t/scalar.t [new file with mode: 0644]
t/top.t [new file with mode: 0644]
templates/FOO.tmpl [new file with mode: 0644]
templates/deeper/deepest/BAR.tmpl [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..cb5c7a5
--- /dev/null
@@ -0,0 +1,10 @@
+blib*
+Makefile
+Makefile.old
+Build
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+Template-Tiny-*
+cover_db
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d1f8c03
--- /dev/null
@@ -0,0 +1,11 @@
+;; 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
+*~
+
diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..177ebbb
--- /dev/null
+++ b/Changes
@@ -0,0 +1,11 @@
+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
+
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..80ecf65
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,17 @@
+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
diff --git a/META.yml b/META.yml
new file mode 100644 (file)
index 0000000..b85e551
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,12 @@
+# 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
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..db8b045
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,742 @@
+# 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 &lt;uri@sysarch.com&gt;</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.
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..8713e6e
--- /dev/null
@@ -0,0 +1,17 @@
+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-*' },
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..e72fdf0
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+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.
diff --git a/Trexy/Template.pm b/Trexy/Template.pm
new file mode 100644 (file)
index 0000000..3a22e9f
--- /dev/null
@@ -0,0 +1,108 @@
+
+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;
diff --git a/bug.pl b/bug.pl
new file mode 100644 (file)
index 0000000..0a4d9da
--- /dev/null
+++ b/bug.pl
@@ -0,0 +1,38 @@
+#!/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);
+
+
+
diff --git a/bug2.pl b/bug2.pl
new file mode 100644 (file)
index 0000000..ceac363
--- /dev/null
+++ b/bug2.pl
@@ -0,0 +1,29 @@
+#!/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" ;
diff --git a/lib/Template/Simple.pm b/lib/Template/Simple.pm
new file mode 100644 (file)
index 0000000..173c605
--- /dev/null
@@ -0,0 +1,1068 @@
+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
+
diff --git a/lib/Template/Simple.pm.expnad b/lib/Template/Simple.pm.expnad
new file mode 100644 (file)
index 0000000..d1801d5
--- /dev/null
@@ -0,0 +1,1049 @@
+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
+
diff --git a/pm_to_blib b/pm_to_blib
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/s5-blank.zip b/s5-blank.zip
new file mode 100644 (file)
index 0000000..d9c820e
Binary files /dev/null and b/s5-blank.zip differ
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644 (file)
index 0000000..d53d3cc
--- /dev/null
@@ -0,0 +1,9 @@
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+       use_ok( 'Template::Simple' );
+}
+
+#diag( "Testing Template::Simple $Template::Simple::VERSION, Perl $], $^X" );
diff --git a/t/boilerplate.t b/t/boilerplate.t
new file mode 100644 (file)
index 0000000..a256135
--- /dev/null
@@ -0,0 +1,48 @@
+#!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');
diff --git a/t/bug.pl b/t/bug.pl
new file mode 100644 (file)
index 0000000..3e8f503
--- /dev/null
+++ b/t/bug.pl
@@ -0,0 +1,79 @@
+#!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
diff --git a/t/common.pm b/t/common.pm
new file mode 100644 (file)
index 0000000..d61d67c
--- /dev/null
@@ -0,0 +1,102 @@
+# 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 ;
diff --git a/t/error.t b/t/error.t
new file mode 100644 (file)
index 0000000..1579882
--- /dev/null
+++ b/t/error.t
@@ -0,0 +1,43 @@
+#!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 ;
+
diff --git a/t/include.t b/t/include.t
new file mode 100644 (file)
index 0000000..4352416
--- /dev/null
@@ -0,0 +1,159 @@
+#!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 ;
+}
diff --git a/t/nested.t b/t/nested.t
new file mode 100644 (file)
index 0000000..f151054
--- /dev/null
@@ -0,0 +1,192 @@
+#!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 ;
+
+
diff --git a/t/options.t b/t/options.t
new file mode 100644 (file)
index 0000000..320fe15
--- /dev/null
@@ -0,0 +1,182 @@
+#!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 ;
diff --git a/t/pod-coverage.t b/t/pod-coverage.t
new file mode 100644 (file)
index 0000000..89bed19
--- /dev/null
@@ -0,0 +1,7 @@
+#!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();
diff --git a/t/pod.t b/t/pod.t
new file mode 100644 (file)
index 0000000..976d7cd
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,6 @@
+#!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();
diff --git a/t/scalar.t b/t/scalar.t
new file mode 100644 (file)
index 0000000..1861345
--- /dev/null
@@ -0,0 +1,101 @@
+#!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 ;
+
diff --git a/t/top.t b/t/top.t
new file mode 100644 (file)
index 0000000..49d3b72
--- /dev/null
+++ b/t/top.t
@@ -0,0 +1,59 @@
+#!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 ;
+
diff --git a/templates/FOO.tmpl b/templates/FOO.tmpl
new file mode 100644 (file)
index 0000000..324870f
--- /dev/null
@@ -0,0 +1 @@
+this loads bar <[%include BAR%]>
diff --git a/templates/deeper/deepest/BAR.tmpl b/templates/deeper/deepest/BAR.tmpl
new file mode 100644 (file)
index 0000000..9fd8acc
--- /dev/null
@@ -0,0 +1 @@
+[this should be hidden then revealed]