diff Lib/IMPL/Web/View/TTContext.pm @ 348:f116cd9fe7d9

working on TTView: pre-alpha version
author cin
date Thu, 03 Oct 2013 19:48:57 +0400
parents 3eafa6fefa9f
children 86b470004d47
line wrap: on
line diff
--- a/Lib/IMPL/Web/View/TTContext.pm	Mon Sep 30 17:36:17 2013 +0400
+++ b/Lib/IMPL/Web/View/TTContext.pm	Thu Oct 03 19:48:57 2013 +0400
@@ -1,29 +1,36 @@
 package IMPL::Web::View::TTContext;
 use strict;
 use Template::Base;
+use Carp qw(carp);
 
-use IMPL::lang qw(is);
+use IMPL::Exception();
+use IMPL::lang qw(is typeof hashApply);
 use IMPL::declare {
-	require => [
-	   Document => '-Template::Document'
-	],
-	base => {
+	require => {
+	   Document => '-Template::Document',
+	   TypeKeyedCollection => 'IMPL::TypeKeyedCollection',
+	   ArgException => "-IMPL::InvalidArgumentException"
+	},
+	base => [
 		'Template::Context' => '@_'
-	}
+	]
 };
 
 BEGIN {
-	no strict 'ref';
+	no strict 'refs';
 	foreach my $prop (qw(
+	   root
 	   base
 	   tt_ext
-	   shared
+	   tt_cache
 	   parent
 	   prefix
+	   cache
+	   includes
 	)) {
 		my $t = $prop;
 		
-		*{__PACKAGE__ . '::' . $name} = sub {
+		*{__PACKAGE__ . '::' . $prop} = sub {
 			my $this = shift;
 			return @_ ? $this->stash->set($t, @_) : $this->stash->get($t);
 		}
@@ -40,7 +47,7 @@
 
 	$this->delocalise();
 	
-	my $class = typeof($this);
+	my $class = ref($this);
 	
     delete $args->{CONFIG};
     
@@ -52,38 +59,45 @@
 }
 
 sub find_template {
-	my ($this,$name,@inc) = @_;
+	my ($this,$name) = @_;
+	
+	my $cache = $this->tt_cache;
+	$this->tt_cache($cache = {}) unless $cache;
 	
-	push @inc, "";
+	if(my $tpl = $cache->{$name}) {
+        return $tpl;
+	}
+	
+	my @inc = ($this->base, @{$this->includes || []});
+	
 	my $ext = $this->tt_ext || "";
+	
+	my $file;
     
 	foreach my $dir (@inc) {
-		my $file = "$dir/$name$ext";
+		$file = $dir ? "$dir/$name" : $name;
+		
+		my $base = join('/',splice([split(/\/+/,$file)],0,-1));
+		
+		$file =  $ext ? "$file.$ext" : $file;
+		
+		warn "lookup: $file";
+		
 		my $tt = eval { $this->template($file) };
 		
-		return {
-			# if we load a block then we should use the current base directory
-			base => is($tt,Document) ? $dir : $this->base,
-			isDocument => is($tt,Document),
-			name => $name,
-			file => $file,
-			template => $tt
+		return $cache->{$name} = {
+			base => $base,
+			template => $tt,
 		} if $tt;
 	}
 	
 	$this->throw(Template::Constants::ERROR_FILE, "$name: not found");
 }
 
-sub require {
-	my ($this,$name) = @_;
-	
-	return $this->stash->get([ 'require', [$name] ]);
-}
-
 sub display {
 	my $this = shift;
 	my $model = shift;
-	my $template, $args;
+	my ($template, $args);
 	
 	if (ref $_[0] eq 'HASH') {
 		$args = shift;
@@ -92,16 +106,138 @@
 		$args = shift;
 	}
 	
-	my $prefix = $this->prefix
+	my $prefix = $this->prefix;
+	
+	if (not(($args and delete $args->{'-no-resolve'}) or ref $model)) {
+		$prefix = $prefix ? "${prefix}.${model}" : $model;
+		$model = $this->resolve_model($model);
+	} else {
+		$prefix = "";
+	}
+	
+	$template = $template ? $this->find_template($template) : $this->find_template_for($model);
+	
+	return $this->render(
+        $template,
+        hashApply(
+            {
+                prefix => $prefix,
+                model => $model,
+	        },
+            $args
+        )
+    );
+}
+
+sub invoke_environment {
+	my ($this,$code,$env) = @_;
+	
+	$env ||= {};
+	
+	my $out = eval {
+		$this->localise(
+            hashApply(
+	            {
+					root => $this->root || $this,
+					cache => TypeKeyedCollection->new(),
+		            display => sub {
+		                $this->display(@_);
+		            },
+		            render => sub {
+		            	$this->render(@_);
+		            }
+				},
+                $env
+            )
+        );
+		
+		&$code($this);
+	};
+	
+	my $e = $@;
+	$this->delocalise();
+	
+	die $e if $e;
+    
+    return $out;
+}
+
+sub render {
+	my ($this,$template,$args) = @_;
+	
+	$args ||= {};
+	
+	#TODO handle classes
+	
+	my $base;
 	
-	my $cloned = $this->clone({
-		prefix => $prefix,
-		shared => $this->shared || $this,
-		parent => $this 
-	});
+	$template = $this->find_template($template) unless ref $template;
+	
+	if (ref $template eq 'HASH') {
+        $base = $template->{base};
+        $template = $template->{template};
+    } else {
+        carp "got an invalid template object: $template";
+        $base = $this->base;
+    }
+	
+	return $this->invoke_environment(
+	   sub {
+	       return shift->include($template,$args);
+	   },
+	   {
+	   	base => $base,
+	   	parent => $this
+	   }
+	)
+}
+
+sub resolve_model {
+	my ($this,$prefix) = @_;
+	
+	die ArgException->new(prefix => "the prefix must be specified")
+	   unless defined $prefix;
+	
+	#TODO handle DOM models
+	
+	my @comp = map { $_, 0 } grep length($_), split(/\.|\[(\d+)\]/, $prefix);
 	
+	return $this->stash->get(['model',0,@comp]);
+}
+
+sub find_template_for {
+	my ($this,$model) = @_;
 	
+	my $type = typeof($model);
 	
+	return $this->find_template('templates/plain') unless $type;
+	
+	if (my $template = $this->cache->Get($type)) {
+		return $template;
+	} else {
+		
+		no strict 'refs';
+               
+        my @isa = $type;
+        
+        while (@isa) {
+            my $sclass = shift @isa;
+            
+            (my $name = $sclass) =~ s/:+/_/g;
+
+            $template = $this->find_template("templates/$name");
+            
+            if ($template) {
+            	$this->cache->Set($sclass,$template);
+            	return $template;
+            } 
+                
+            push @isa, @{"${sclass}::ISA"};
+        }
+		
+	}
+	
+	return;
 }
 
 1;
@@ -122,7 +258,7 @@
 
 @startuml
 
-object SharedContext {
+object RootContext {
     document
     globals
 }
@@ -137,13 +273,13 @@
     extends
 }
 
-SharedContext o-- DocumentContext 
-SharedContext o-- ControlContext 
+RootContext o-- DocumentContext 
+RootContext o-- ControlContext 
 
 Document -- DocumentContext
 Control - ControlContext
 
-Loader . SharedContext: <<creates>>
+Loader . RootContext: <<creates>>
 Loader . Document: <<creates>>
 Loader -up- Registry