# ================================================================== # Gossamer Threads Module Library - http://gossamer-threads.com/ # # GT::Template::Inheritance # Author: Scott Beck # CVS Info : # $Id: Inheritance.pm,v 1.7 2005/02/09 20:51:27 jagerman Exp $ # # Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. # ================================================================== # # Description: Provides class methods to deal with template # inheritance. # package GT::Template::Inheritance; # ================================================================== use strict; use vars qw($ERRORS); use bases 'GT::Base' => ''; use GT::Template; $ERRORS = { RECURSION => q _Recursive inheritance detected and interrupted: '%s'_ }; sub get_all_paths { # ---------------------------------------------------------------------------- my ($class, %opts) = @_; my $file = delete $opts{file}; my $single = delete $opts{_single}; $class->fatal(BADARGS => "No file specified to $class->" . ($single ? 'get_path' : 'get_all_paths')) unless defined $file; my $root = delete $opts{path}; $class->fatal(BADARGS => "No path specified to $class->" . ($single ? 'get_path' : 'get_all_paths')) unless defined $root; $class->fatal(BADARGS => "Path $root does not exist or is not a directory") unless -d $root; my $local = exists $opts{local} ? delete $opts{local} : 1; my $inheritance = exists $opts{inheritance} ? delete $opts{inheritance} : 1; # Old no-longer-supported option: delete @opts{qw/use_inheritance use_local local_inheritance/}; $class->fatal(BADARGS => "Unknown arguments: " . join ", ", keys %opts) if keys %opts; my @paths = $class->tree(path => $root, local => $local, inheritance => $inheritance); my @files; for (@paths) { if (-f "$_/$file" and -r _) { return "$_/$file" if $single; push @files, "$_/$file"; } } return if $single; return @files; } sub get_path { # ---------------------------------------------------------------------------- shift->get_all_paths(@_, _single => 1); } sub tree { # ----------------------------------------------------------------------------- my $class = shift; my %opts = @_ > 1 ? @_ : (path => shift); my $root = delete $opts{path}; $class->fatal(BADARGS => "No path specified for $class->tree") unless defined $root; $class->fatal(BADARGS => "Path '$root' does not exist or is not a directory") unless -d $root; my $local = exists $opts{local} ? delete $opts{local} : 1; my $inheritance = exists $opts{inheritance} ? delete $opts{inheritance} : 1; $class->fatal(BADARGS => "Unknown arguments: " . join ", ", keys %opts) if keys %opts; my @paths; push @paths, $root; my %encountered = ($root => 1); if ($inheritance) { for my $path (@paths) { my $tplinfo = GT::Template->load_tplinfo($path); next if not defined $tplinfo->{inheritance}; my @inherit = ref $tplinfo->{inheritance} eq 'ARRAY' ? @{$tplinfo->{inheritance}} : $tplinfo->{inheritance}; for (@inherit) { my $inh = m!^(?:[a-zA-Z]:)?[\\/]! ? $_ : "$path/$_"; if (length $inh > 500 or $encountered{$inh}++) { return $class->fatal(RECURSION => $inh); } push @paths, $inh; } } } if ($local) { for (my $i = 0; $i < @paths; $i++) { if (-d "$paths[$i]/local") { splice @paths, $i, 0, "$paths[$i]/local"; $i++; } } } return @paths; } 1; __END__ =head1 NAME GT::Template::Inheritance - Provides GT::Template inheritance/local file determination. =head1 SYNOPSIS use GT::Template::Inheritance; my $file = GT::Template::Inheritance->get_path( file => "foo.htm", path => "/path/to/my/template/set" ); my @files = GT::Template::Inheritance->get_all_paths( file => "foo.htm", path => "/path/to/my/template/set" ); my @paths = GT::Template::Inheritance->tree( path => "/path/to/my/template/set" ); =head1 DESCRIPTION GT::Template::Inheritance provides an interface to accessing files for GT::Template template parsing and include handling. It supports following inheritance directories and respects "local" template directories. =head2 Inheritance GT::Template inheritance works by looking for a .tplinfo file in the template directory (or local/.tplinfo, if it exists). In order for the template directory to inherit from another template directory, this file must exist and must evaluate to a hash reference containing an C key. The following is a possible .tplinfo file contents: { inheritance => '../other' } The above example would indicate that files in this template set can be inherited from the ../other path, relative to the current template set directory. The inheritance directory may also contain a full path. =head2 Inheriting from multiple locations You may also inherit from multiple locations by using an array reference for the inheritance value: { inheritance => ['../other', '/full/path/to/a/third'] } With the above .tplinfo file, files would be checked for in the current path, then C<../other>, then any of C<../other>'s inherited directories, then in C, then in any of C's inherited directories. Also keep in mind that "local" directories, if they exist, will be checked for the file before each of their respective directories. Assuming that the initial template path was C, and assuming that C<../other> inherited from C<../other2>, the directories checked would be as follows: /full/path/one/local /full/path/one /full/path/one/../other/local # i.e. /full/path/other/local /full/path/one/../other # i.e. /full/path/other /full/path/one/../other/../other2/local # i.e. /full/path/other2/local /full/path/one/../other/../other2 # i.e. /full/path/other2 /full/path/to/a/third/local /full/path/to/a/third =head1 METHODS All methods in GT::Template::Inheritance are class methods. Each method takes a hash of options as an argument. =head2 get_path =head2 get_all_paths These methods are used to obtain the location of the file GT::Template will use, taking into account all inherited and "local" template directories. The get_path option will return the path to the file that will be included, while the get_all_paths option returns the path to B copies of the file found in the local/inheritance tree. Both methods take a hash containing the following: =over 4 =item file The name of the file desired. =item path The template directory at which to start looking for the above file. Depending on the existance of "local" directories and template inheritance, more than just this directory will be checked for the file. =item local Optional. Can be passed with a false value to override the checking of "local" directories for files. =item inheritance Optional. Can be passed with a false value to override the checking of inheritance directories for files. =back =head2 tree This method returns a list of directories that would be searched for a given file, in the order they would be searched. It takes the C, C, and C options above, but not the C option. =head1 SEE ALSO L =head1 MAINTAINER Jason Rhinelander =head1 COPYRIGHT Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. http://www.gossamer-threads.com/ =head1 VERSION Revision: $Id: Inheritance.pm,v 1.7 2005/02/09 20:51:27 jagerman Exp $ =cut