changeset 348:f116cd9fe7d9

working on TTView: pre-alpha version
author cin
date Thu, 03 Oct 2013 19:48:57 +0400
parents 3eafa6fefa9f
children 86b470004d47
files Lib/IMPL/DOM/Transform/ObjectToDOM.pm Lib/IMPL/Web/View/TTContext.pm _test/Resources/view/packages/templates/ARRAY.tt _test/Resources/view/packages/templates/HASH.tt _test/Resources/view/packages/templates/plain.tt _test/Resources/view/site/index.tt _test/Resources/view/site/product/view.tt _test/Resources/view/site/product/view.tt.labels _test/temp.pl
diffstat 9 files changed, 221 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/DOM/Transform/ObjectToDOM.pm	Mon Sep 30 17:36:17 2013 +0400
+++ b/Lib/IMPL/DOM/Transform/ObjectToDOM.pm	Thu Oct 03 19:48:57 2013 +0400
@@ -80,7 +80,6 @@
         my $value = $data->{$key};
         
         if (ref $value eq 'ARRAY') {
-            #TODO: collapse empty values only if needed
             foreach my $subval (grep $_, @$value) {
                 
                 $this->_navi->saveState();
--- 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
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_test/Resources/view/packages/templates/ARRAY.tt	Thu Oct 03 19:48:57 2013 +0400
@@ -0,0 +1,3 @@
+[% FOR item IN model %]
+    * ${loop.index}. [% display(loop.index) %]
+[% END %]
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_test/Resources/view/packages/templates/HASH.tt	Thu Oct 03 19:48:57 2013 +0400
@@ -0,0 +1,3 @@
+[% FOR item IN model %]
+    <div>$item.key : [% display(item.key) %]</div>
+[% END %]
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_test/Resources/view/packages/templates/plain.tt	Thu Oct 03 19:48:57 2013 +0400
@@ -0,0 +1,1 @@
+<span prefix="$prefix">$model</span>
\ No newline at end of file
--- a/_test/Resources/view/site/index.tt	Mon Sep 30 17:36:17 2013 +0400
+++ b/_test/Resources/view/site/index.tt	Thu Oct 03 19:48:57 2013 +0400
@@ -1,1 +1,2 @@
-<h1>hello</h1>
\ No newline at end of file
+<h1>hello</h1>
+[% display({ name = 'a2r2', description = 'service droid' }, prefix = 'data') %]
\ No newline at end of file
--- a/_test/Resources/view/site/product/view.tt	Mon Sep 30 17:36:17 2013 +0400
+++ b/_test/Resources/view/site/product/view.tt	Thu Oct 03 19:48:57 2013 +0400
@@ -1,10 +1,3 @@
-[% display('name') %]
-[% display('description') %]
-<div>
-    <div>
-        $AddressLabel
-    </div>
-    <div>
-        [% display('address', 'templates/My_App_Model_Address', { nolinks => 1 } ) %]
-    </div>
-</div>
\ No newline at end of file
+<h1> PageTitle </h1>
+<div>name: [% display('name') %]</div>
+<div>description: [% display(model) %]</div>
\ No newline at end of file
--- a/_test/Resources/view/site/product/view.tt.labels	Mon Sep 30 17:36:17 2013 +0400
+++ b/_test/Resources/view/site/product/view.tt.labels	Thu Oct 03 19:48:57 2013 +0400
@@ -1,1 +1,1 @@
-AddressLabel = Address
\ No newline at end of file
+PageTitle = View page title
\ No newline at end of file
--- a/_test/temp.pl	Mon Sep 30 17:36:17 2013 +0400
+++ b/_test/temp.pl	Thu Oct 03 19:48:57 2013 +0400
@@ -1,42 +1,44 @@
 #!/usr/bin/perl
 use strict;
 
-{
-    package Foo;
-    use IMPL::declare {
-        base => [
-            'IMPL::Object::Disposable' => undef
-        ]
-    };
-}
-
-use Time::HiRes qw(gettimeofday tv_interval);
-
-
-use IMPL::lang;
 use IMPL::require {
-    AutoDispose => 'IMPL::Object::AutoDispose',
-    DBSchema => 'IMPL::SQL::Schema'    
+	TTContext => 'IMPL::Web::View::TTContext'
 };
 
-my $real = DBSchema->new( name => 'simple', version => 1);
-my $proxy = AutoDispose->new($real);
-
-print typeof($proxy),"\n";
+my $ctx = TTContext->new({
+	INCLUDE_PATH => './Resources/view',
+	INTERPOLATE => 1,
+	RECURSION => 1000
+});
 
-my $t = [gettimeofday];
-
-for (1..1000000) {
-    $proxy->name;
-}
+warn $ctx->load_templates->[0]->include_path->[0];
 
-print "proxy: ",tv_interval($t,[gettimeofday]),"\n";
-
-$t = [gettimeofday];
-
-for (1..1000000) {
-    $real->name;
-}
-
-print "real:  ",tv_interval($t,[gettimeofday]),"\n";
+print $ctx->invoke_environment(sub {
+	return shift->render(
+	    'product/view',{
+			model => {
+				name => 'debugger',
+				manufature => {
+					name => 'DEBUGGERS INC',
+					address => [
+					     {
+					     	coutry => 'Russuia',
+					     	city => 'Moscow'
+					     },
+					     {
+					     	country => 'GB',
+					     	city => 'Essex'
+					     }
+					]
+				}
+			}
+	   }
+    );
+}, {
+	base => 'site',
+	includes => [
+	   'packages'
+	],
+	tt_ext => 'tt'
+});