changeset 353:feeb3bc4a818

corrected error handling while loading templates corrected variables lookup in controls updated handles to use the new view features
author cin
date Fri, 11 Oct 2013 15:49:04 +0400
parents 675cd1829255
children 9330835535b9
files Lib/IMPL/Web/Handler/ErrorHandler.pm Lib/IMPL/Web/Handler/TTView.pm Lib/IMPL/Web/Handler/View.pm Lib/IMPL/Web/View/TTContext.pm Lib/IMPL/Web/View/TTControl.pm Lib/IMPL/Web/View/TTFactory.pm Lib/IMPL/Web/View/TTView.pm _test/temp.pl
diffstat 8 files changed, 391 insertions(+), 684 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/Web/Handler/ErrorHandler.pm	Thu Oct 10 19:51:19 2013 +0400
+++ b/Lib/IMPL/Web/Handler/ErrorHandler.pm	Fri Oct 11 15:49:04 2013 +0400
@@ -17,7 +17,8 @@
 	},
 	props => [
         errors => PROP_RW,
-        loader => PROP_RW,
+        view => PROP_RW,
+        layout => PROP_RW,
         fallback => PROP_RW,
         contentType => PROP_RW
 	]
@@ -26,7 +27,7 @@
 sub CTOR {
 	my ($this) = @_;
 	
-	die ArgumentException->new("loader") unless $this->loader;
+	die ArgumentException->new("view") unless $this->view;
 	die ArgumentException->new("fallback") unless $this->fallback;
 	
 	$this->errors({}) unless $this->errors;
@@ -44,7 +45,7 @@
 	
 	if (my $err = $@) {
 	    
-	    warn "$err";
+	    warn "error handler: $err";
 	    
 		my $vars = {
 			error => $err
@@ -58,12 +59,13 @@
 		
 		my ($code) = ($status =~ m/^(\d+)/);
 		
-		my $doc = $this->loader->document(
-            $this->errors->{$code} || $this->fallback,
-            $vars
-        );
-        
-        my $text = $doc->Render($vars);
+        my $text = $this->view->display(
+        	$err,
+        	$this->errors->{$code} || $this->fallback,
+        	{
+        		layout => $this->layout
+        	}
+		);
         
         $result = HttpResponse->new(
             status => $status,
--- a/Lib/IMPL/Web/Handler/TTView.pm	Thu Oct 10 19:51:19 2013 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,348 +0,0 @@
-package IMPL::Web::Handler::TTView;
-use strict;
-
-use Carp qw(carp);
-use List::Util qw(first);
-use IMPL::lang;
-use IMPL::Const qw(:prop);
-use IMPL::declare {
-    require => {
-        Factory      => 'IMPL::Web::View::ObjectFactory',
-        HttpResponse => 'IMPL::Web::HttpResponse',
-        Loader       => 'IMPL::Code::Loader',
-        ViewResult   => '-IMPL::Web::ViewResult'
-      },
-      base => [
-        'IMPL::Object'               => undef,
-        'IMPL::Object::Autofill'     => '@_',
-        'IMPL::Object::Serializable' => undef
-      ],
-
-      props => [
-        contentType     => PROP_RO,
-        contentCharset  => PROP_RO,
-        loader          => PROP_RO,
-        selectors       => PROP_RO,
-        defaultDocument => PROP_RW,
-        _selectorsCache => PROP_RW
-      ]
-};
-
-sub CTOR {
-    my ($this) = @_;
-
-    $this->_selectorsCache([ map $this->ParseRule($_), @{$this->selectors || []} ]);
-}
-
-sub Invoke {
-    my ( $this, $action, $next ) = @_;
-
-    my $result = $next ? $next->($action) : undef;
-    
-    my ($model,$view);
-    if( ref $result and eval { $result->isa(ViewResult) } ) {
-        $model = $result->model;
-        $view = $result;      
-    } else {
-        $model = $result;
-        $view = ViewResult->new(model => $model);
-    }
-    
-    my $vars = {
-        view        => $view,
-        model       => $model,
-        #TODO cleanup
-        action      => sub { carp "action variable is deprecated"; $action },
-        request     => sub { $action },
-        app         => $action->application,
-        context     => $action->context,
-        env         => _cached($action->context->{environment}),
-        ImportClass => sub {
-            my $class = shift;
-            
-            carp "ImportClass is obsolete use import instead";
-            
-            $class = Loader->safe->Require($class);
-            
-            return Factory->new($class);
-        },
-        import      => sub {
-            my $class = shift;
-
-            $class = Loader->safe->Require($class);
-            
-            return Factory->new($class);
-        }
-    };
-
-    my $doc =
-      $this->loader->document( eval { $view->template } || $this->SelectView( $action, ref $model ),
-        $vars );
-        
-    $doc->location($action->context->{resourceLocation});
-        
-    my %responseParams = (
-        type => $this->contentType,
-        charset => $this->contentCharset,
-        body => $doc->Render()
-    );
-    
-    $responseParams{status} = $view->status if $view->status;
-    $responseParams{cookies} = $view->cookies if ref $view->cookies eq 'HASH';
-    $responseParams{headers} = $view->headers if ref $view->headers eq 'HASH';
-
-    return HttpResponse->new(
-        %responseParams        
-    );
-}
-
-sub _cached {
-    my $arg = shift;
-    
-    return $arg unless ref $arg eq 'CODE';
-    
-    return sub {
-        ref $arg eq 'CODE' ? $arg = &$arg() : $arg;
-    }
-}
-
-sub SelectView {
-    my ($this,$action) = @_;
-    
-    my @path;
-    
-    for(my $r = $action->context->{resource}; $r ; $r = $r->parent ) {
-        unshift @path, {
-            name => $r->id,
-            class => typeof($r->model)
-        };
-    }
-    
-    @path = map { name => $_}, split /\/+/, $action->query->path_info()
-        unless (@path);
-    
-    return $this->MatchPath(\@path,$this->_selectorsCache) || $this->defaultDocument;
-}
-
-sub ParseRule {
-    my ($this, $rule) = @_;
-    
-    my ($selector,$data) = split /\s+=>\s+/, $rule;
-    
-    my @parts;
-    my $first = 1;
-    my $weight = 0;
-    foreach my $part ( split /\//, $selector ) {
-        # если первым символом является /
-        # значит путь в селекторе абсолютный и не нужно
-        # добавлять "любой" элемент в начало
-        
-        if($part) {
-            $weight ++;
-            push @parts,{ any => 1 } if $first;
-        } else {
-            push @parts,{ any => 1 } unless $first;
-            next;
-        }        
-
-        my ($name,$class) = split /@/, $part;
-        
-        if ( my ( $varName, $rx ) = ( $name =~ m/^\{(?:(\w+)\:)?(.*)\}$/ ) ) {
-            #this is a regexp
-
-            push @parts, {
-                rx => $rx,
-                var => $varName,
-                class => $class,
-            };
-        } else {
-            push @parts, {
-                name => length($name) ? $name : undef,
-                class => $class,
-            };
-        }        
-    } continue {
-        $first = 0;
-    }
-     
-    return { selector => \@parts, data => $data, weight => $weight };
-}
-
-sub MatchPath {
-    my ($this,$path,$rules) = @_;
-    
-    $path ||= [];
-    $rules ||= [];
-    
-    my @next;
-    
-    foreach my $segment (@$path) {
-        foreach my $rule (@$rules) {
-            my @selector = @{$rule->{selector}};
-            
-            my $part = shift @selector;
-            
-            # if this rule doesn't have a selector
-            next unless $part;
-
-            if ($part->{any}) {
-                #keep the rule for the next try
-                push @next, $rule;
-
-                $part = shift @selector while $part->{any};
-            }
-            
-            my $newRule = {
-                selector => \@selector,
-                data => $rule->{data},
-                weight => $rule->{weight},
-                vars => { %{$rule->{vars} || {}} }
-            };
-            
-            my $success = 1;
-            if (my $class = $part->{class}) {
-                $success = isclass($segment->{class},$class);
-            }
-            
-            if($success && (my $name = $part->{name})) {
-                $success = $segment->{name} eq $name;
-            } elsif ($success && (my $rx = $part->{rx})) {
-                if( my @captures = ($segment->{name} =~ m/($rx)/) ) {
-                    $newRule->{vars}->{$part->{var}} = \@captures
-                        if $part->{var};
-                } else {
-                    $success = 0;
-                }
-            }
-            
-            push @next, $newRule if $success;
-            
-        }
-        $rules = [@next];
-        undef @next;
-    }
-    
-    my $result = (
-        sort {
-            $b->{weight} <=> $a->{weight}
-        }
-        grep {
-            scalar(@{$_->{selector}}) == 0
-        }
-        @$rules
-    )[0];
-    
-    if($result) {
-        my $data = $result->{data};
-        $data =~ s/{(\w+)(?:\:(\d+))?}/
-            my ($name,$index) = ($1,$2 || 0);
-            
-            if ($result->{vars}{$name}) {
-                $result->{vars}{$name}[$index];
-            } else {
-                "";
-            }
-        /gex;
-        
-        return $data;
-    } else {
-        return;
-    }
-}
-
-1;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-C<IMPL::Web::Handler::TTView> - использует шаблоны для построения представления.
-
-=head1 SYNOPSIS
-
-=begin code xml
-
-<item id="html-view" type="IMPL::Web::Handler::TTView">
-    <contentType>text/html</contentType>
-    <loader id="tt-loader" type="IMPL::Web::View::TTLoader">
-	    <options type="HASH">
-	        <INCLUDE_PATH type="IMPL::Config::Reference">
-	            <target>IMPL::Config</target>
-	            <AppBase>view</AppBase>
-	        </INCLUDE_PATH>
-	        <INTERPOLATE>1</INTERPOLATE>
-	        <POST_CHOMP>1</POST_CHOMP>
-	        <ENCODING>utf-8</ENCODING>
-	    </options>
-	    <ext>.tt</ext>
-	    <initializer>global.tt</initializer>
-	    <layoutBase>layouts</layoutBase>
-    </loader>
-    <defaultDocument>default</defaultDocument>
-    <selectors type="ARRAY">
-        <item>@HASH => dump</item>
-        <item>@My::Data::Product => product/info</item>
-        <item>{action:.*} @My::Data::Product => product/{action}</item>
-    </selectors>                    
-</item>
-
-=end code xml
-
-=head1 DESCRIPTION
-
-Подбирает шаблон для представления результата, полученного при выполнении следующего обработчика. При
-выборе используется принцип похожий на селекторы C<CSS>, основывающийся на именах ресурсов и их типах
-данных.
-
-Данный обработчик понимает определенные свойства контекста:
-
-=over
-
-=item * C<resourceLocation>
-
-В данном свойстве может быть передана информация о текущем расположении ресурса,
-для которого строится представление. Эта информация будет доступна в шаблоне
-через свойство документа C<location>.
-
-=item * C<environment>
-
-В данном совойстве контекста передается дополнительная информация об окружении
-ресурса, например, которую задали родительские ресурсы. Использование данного
-свойства позволяет не загромождать ресурс реализацией функциональности по
-поддержке окружения. Это свойство может быть ссылкой на функцию, что позволяет
-формировать контекст только по необходимости, при этом указанная функция будет
-выполнена только один раз, при первом обращении.
-
-=back 
-
-=head1 SELECTORS
-
-=begin text
-
-syntax::= selector => template
-
-selector::= ([>]segment-template[@class-name])
-
-segment-template::= {'{'name:regular-expr'}'|segment-name}
-
-name::= \w+
-
-segment-name::= \S+
-
-class-name::= name[(::name)]
-
-url-template@class => template
-
-shoes => product/list
-/shop//{action:*.}@My::Data::Product => product/{action}
-
-stuff >list => product/list
-details => product/details
-
-=end text
-
-
-=cut
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/Handler/View.pm	Fri Oct 11 15:49:04 2013 +0400
@@ -0,0 +1,337 @@
+package IMPL::Web::Handler::View;
+use strict;
+
+use Carp qw(carp);
+use List::Util qw(first);
+use IMPL::lang;
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+    require => {
+        Factory      => 'IMPL::Web::View::ObjectFactory',
+        HttpResponse => 'IMPL::Web::HttpResponse',
+        Loader       => 'IMPL::Code::Loader',
+        ViewResult   => '-IMPL::Web::ViewResult'
+      },
+      base => [
+        'IMPL::Object'               => undef,
+        'IMPL::Object::Autofill'     => '@_',
+        'IMPL::Object::Serializable' => undef
+      ],
+
+      props => [
+        contentType     => PROP_RO,
+        contentCharset  => PROP_RO,
+        view            => PROP_RO,
+        layout          => PROP_RO,
+        selectors       => PROP_RO,
+        defaultDocument => PROP_RW,
+        _selectorsCache => PROP_RW
+      ]
+};
+
+sub CTOR {
+    my ($this) = @_;
+
+    $this->_selectorsCache([ map $this->ParseRule($_), @{$this->selectors || []} ]);
+}
+
+sub Invoke {
+    my ( $this, $action, $next ) = @_;
+
+    my $result = $next ? $next->($action) : undef;
+    
+    my ($model,$view,$template);
+    if( ref $result and eval { $result->isa(ViewResult) } ) {
+        $model = $result->model;
+        $view = $result;
+        $template = $result->template;
+    } else {
+        $model = $result;
+        $view = ViewResult->new(model => $model);
+    }
+    
+    my $vars = {
+        view        => $view,
+        request     => sub { $action },
+        app         => $action->application,
+        context     => $action->context,
+        env         => _cached($action->context->{environment}),
+        location    => $action->context->{resourceLocation},
+        layout      => $this->layout
+	};
+
+    $this->view->display(
+      	$model,
+      	$template || $this->SelectView( $action, ref $model ),
+        $vars
+    );
+        
+    my %responseParams = (
+        type => $this->contentType,
+        charset => $this->contentCharset,
+	        body => $this->view->display(
+	      	$model,
+	      	$template || $this->SelectView( $action, ref $model ),
+	        $vars
+	    )
+    );
+    
+    $responseParams{status} = $view->status if $view->status;
+    $responseParams{cookies} = $view->cookies if ref $view->cookies eq 'HASH';
+    $responseParams{headers} = $view->headers if ref $view->headers eq 'HASH';
+
+    return HttpResponse->new(
+        %responseParams        
+    );
+}
+
+sub _cached {
+    my $arg = shift;
+    
+    return $arg unless ref $arg eq 'CODE';
+    
+    return sub {
+        ref $arg eq 'CODE' ? $arg = &$arg() : $arg;
+    }
+}
+
+sub SelectView {
+    my ($this,$action) = @_;
+    
+    my @path;
+    
+    for(my $r = $action->context->{resource}; $r ; $r = $r->parent ) {
+        unshift @path, {
+            name => $r->id,
+            class => typeof($r->model)
+        };
+    }
+    
+    @path = map { name => $_}, split /\/+/, $action->query->path_info()
+        unless (@path);
+    
+    return $this->MatchPath(\@path,$this->_selectorsCache) || $this->defaultDocument;
+}
+
+sub ParseRule {
+    my ($this, $rule) = @_;
+    
+    my ($selector,$data) = split /\s+=>\s+/, $rule;
+    
+    my @parts;
+    my $first = 1;
+    my $weight = 0;
+    foreach my $part ( split /\//, $selector ) {
+        # если первым символом является /
+        # значит путь в селекторе абсолютный и не нужно
+        # добавлять "любой" элемент в начало
+        
+        if($part) {
+            $weight ++;
+            push @parts,{ any => 1 } if $first;
+        } else {
+            push @parts,{ any => 1 } unless $first;
+            next;
+        }        
+
+        my ($name,$class) = split /@/, $part;
+        
+        if ( my ( $varName, $rx ) = ( $name =~ m/^\{(?:(\w+)\:)?(.*)\}$/ ) ) {
+            #this is a regexp
+
+            push @parts, {
+                rx => $rx,
+                var => $varName,
+                class => $class,
+            };
+        } else {
+            push @parts, {
+                name => length($name) ? $name : undef,
+                class => $class,
+            };
+        }        
+    } continue {
+        $first = 0;
+    }
+     
+    return { selector => \@parts, data => $data, weight => $weight };
+}
+
+sub MatchPath {
+    my ($this,$path,$rules) = @_;
+    
+    $path ||= [];
+    $rules ||= [];
+    
+    my @next;
+    
+    foreach my $segment (@$path) {
+        foreach my $rule (@$rules) {
+            my @selector = @{$rule->{selector}};
+            
+            my $part = shift @selector;
+            
+            # if this rule doesn't have a selector
+            next unless $part;
+
+            if ($part->{any}) {
+                #keep the rule for the next try
+                push @next, $rule;
+
+                $part = shift @selector while $part->{any};
+            }
+            
+            my $newRule = {
+                selector => \@selector,
+                data => $rule->{data},
+                weight => $rule->{weight},
+                vars => { %{$rule->{vars} || {}} }
+            };
+            
+            my $success = 1;
+            if (my $class = $part->{class}) {
+                $success = isclass($segment->{class},$class);
+            }
+            
+            if($success && (my $name = $part->{name})) {
+                $success = $segment->{name} eq $name;
+            } elsif ($success && (my $rx = $part->{rx})) {
+                if( my @captures = ($segment->{name} =~ m/($rx)/) ) {
+                    $newRule->{vars}->{$part->{var}} = \@captures
+                        if $part->{var};
+                } else {
+                    $success = 0;
+                }
+            }
+            
+            push @next, $newRule if $success;
+            
+        }
+        $rules = [@next];
+        undef @next;
+    }
+    
+    my $result = (
+        sort {
+            $b->{weight} <=> $a->{weight}
+        }
+        grep {
+            scalar(@{$_->{selector}}) == 0
+        }
+        @$rules
+    )[0];
+    
+    if($result) {
+        my $data = $result->{data};
+        $data =~ s/{(\w+)(?:\:(\d+))?}/
+            my ($name,$index) = ($1,$2 || 0);
+            
+            if ($result->{vars}{$name}) {
+                $result->{vars}{$name}[$index];
+            } else {
+                "";
+            }
+        /gex;
+        
+        return $data;
+    } else {
+        return;
+    }
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+C<IMPL::Web::Handler::TTView> - использует шаблоны для построения представления.
+
+=head1 SYNOPSIS
+
+=begin code xml
+
+<item id="html-view" type="IMPL::Web::Handler::View">
+    <contentType>text/html</contentType>
+    <view id="tt-loader" type="IMPL::Web::View::TTView">
+	    <options type="HASH">
+	        <INCLUDE_PATH type="IMPL::Config::Reference">
+	            <target>IMPL::Config</target>
+	            <AppBase>view</AppBase>
+	        </INCLUDE_PATH>
+	        <INTERPOLATE>1</INTERPOLATE>
+	        <POST_CHOMP>1</POST_CHOMP>
+	        <ENCODING>utf-8</ENCODING>
+	    </options>
+	    <ext>.tt</ext>
+	    <initializer>global.tt</initializer>
+	    <layoutBase>layouts</layoutBase>
+    </view>
+    <defaultDocument>default</defaultDocument>
+    <selectors type="ARRAY">
+        <item>@HASH => dump</item>
+        <item>@My::Data::Product => product/info</item>
+        <item>{action:.*} @My::Data::Product => product/{action}</item>
+    </selectors>                    
+</item>
+
+=end code xml
+
+=head1 DESCRIPTION
+
+Подбирает шаблон для представления результата, полученного при выполнении следующего обработчика. При
+выборе используется принцип похожий на селекторы C<CSS>, основывающийся на именах ресурсов и их типах
+данных.
+
+Данный обработчик понимает определенные свойства контекста:
+
+=over
+
+=item * C<resourceLocation>
+
+В данном свойстве может быть передана информация о текущем расположении ресурса,
+для которого строится представление. Эта информация будет доступна в шаблоне
+через свойство документа C<location>.
+
+=item * C<environment>
+
+В данном совойстве контекста передается дополнительная информация об окружении
+ресурса, например, которую задали родительские ресурсы. Использование данного
+свойства позволяет не загромождать ресурс реализацией функциональности по
+поддержке окружения. Это свойство может быть ссылкой на функцию, что позволяет
+формировать контекст только по необходимости, при этом указанная функция будет
+выполнена только один раз, при первом обращении.
+
+=back 
+
+=head1 SELECTORS
+
+=begin text
+
+syntax::= selector => template
+
+selector::= ([>]segment-template[@class-name])
+
+segment-template::= {'{'name:regular-expr'}'|segment-name}
+
+name::= \w+
+
+segment-name::= \S+
+
+class-name::= name[(::name)]
+
+url-template@class => template
+
+shoes => product/list
+/shop//{action:*.}@My::Data::Product => product/{action}
+
+stuff >list => product/list
+details => product/details
+
+=end text
+
+
+=cut
+
--- a/Lib/IMPL/Web/View/TTContext.pm	Thu Oct 10 19:51:19 2013 +0400
+++ b/Lib/IMPL/Web/View/TTContext.pm	Fri Oct 11 15:49:04 2013 +0400
@@ -110,6 +110,11 @@
 		        $this->modules->{$file} = $info;
 		        return $cache->{$name} = $info;
             } else {
+            	my $err = $@;
+            	
+            	for(my $t = $err; is($t,'Template::Exception'); $t = $t->info ) {
+            		die $err unless $t->type eq Template::Constants::ERROR_FILE;
+            	}
             	$this->modules->{$file} = undef;
             }
 		}
--- a/Lib/IMPL/Web/View/TTControl.pm	Thu Oct 10 19:51:19 2013 +0400
+++ b/Lib/IMPL/Web/View/TTControl.pm	Fri Oct 11 15:49:04 2013 +0400
@@ -14,7 +14,6 @@
 	props => [
 		context => PROP_RO,
 		template => PROP_RO,
-		_stash => PROP_RO,
 		id => {
 	   		get => sub { shift->_stash->get('id') },
 	   		set => sub { shift->_stash->set('id',shift) }
@@ -39,8 +38,10 @@
     	or die ArgException->new(context => 'A context is required');
     $this->template($template)
     	or die ArgException->new(template => 'A template is required');
-    	
-    $this->_stash($context->stash);
+}
+
+sub _stash {
+	$_[0]->context->stash;
 }
 
 sub Render {
@@ -52,17 +53,18 @@
 sub AUTOLOAD {
 	my ($prop) = ($AUTOLOAD =~ m/(\w+)$/);
 	
-	die Exception->new("Control doesn't have method '$prop'") unless $prop=~/$AUTOLOAD_REGEX/;
+	die Exception->new("Method not found: $AUTOLOAD") unless $prop=~/$AUTOLOAD_REGEX/ and $_[0];
 	
 	no strict 'refs';
 	
 	my $method = sub {
-		if (@_ == 1) {
-			return shift->_stash->get($prop);
-		} elsif (@_ == 2) {
-			return shift->_stash->set($prop,shift);
+		my $that = shift;
+		if (@_ == 0) {
+			return $that->_stash->get($prop);
+		} elsif (@_ == 1) {
+			return $that->_stash->set($prop,shift);
 		} else {
-			return shift->_stash->get([$prop,[@_]]);
+			return $that->_stash->get([$prop,[@_]]);
 		}
 	};
 	
--- a/Lib/IMPL/Web/View/TTFactory.pm	Thu Oct 10 19:51:19 2013 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,296 +0,0 @@
-package IMPL::Web::View::TTFactory;
-use strict;
-
-use Template::Context();
-
-use Carp qw(carp);
-use IMPL::lang qw(:hash is);
-use IMPL::Exception();
-use Scalar::Util qw(weaken);
-use IMPL::Resources::Format qw(FormatMessage);
-use IMPL::Resources::Strings();
-
-
-use IMPL::Const qw(:prop);
-use IMPL::declare {
-    require => {
-        Loader => 'IMPL::Code::Loader',
-        Resources => 'IMPL::Resources', 
-        OpException => '-IMPL::InvalidOperationException',
-        ArgException => '-IMPL::InvalidArgumentException'
-    },
-    base => [
-        'IMPL::Object::Factory' => sub {
-            shift;
-        }
-    ],
-    props => [
-        template => PROP_RW,
-        activation => PROP_RW,
-        context => PROP_RW,
-        baseLocation => PROP_RW,
-        base => PROP_RW,
-        registry => PROP_RO, 
-        blocks => PROP_RO,
-        path => PROP_RO,
-        _instance => PROP_RW,
-        _labels => PROP_RW
-    ]
-};
-
-sub CTOR {
-    my ($this,$class,$template,$context,$path,$registry) = @_;
-    
-    die ArgException->new("A control class must be specified")
-        unless $class;
-    die ArgException->new("A template is required") unless $template;
-    
-    Loader->safe->Require($class)
-        unless ref $class ;
-    
-    $context ||= new Template::Context();
-    my $baseLocation = join( '/', splice( @{[split(/\//,$path)]}, 0, -1 ) );
-    
-    $this->activation($template->activation || 'new');
-    $this->template($template);
-    $this->context($context);
-    $this->baseLocation($baseLocation);
-    $this->path($path);
-    $this->registry($registry);
-    
-    if (my $baseTplName = $template->extends) {
-        $baseTplName =~ s{^\./}{$baseLocation/};
-        
-        my $base = $registry->Require($baseTplName)
-            or die OpException->new("The specified base template isn't found");
-            
-        $this->base($base);
-            
-        $this->blocks(hashMerge($base->blocks, $template->blocks));
-
-        # блок BASE должен меняться в процессе выполнения, в зависимости от
-        # шаблона, который рендерится, по мере перехода в BASE 
-        
-        my @baseStack;
-        
-        $this->blocks->{BASE} = sub {
-            my $ctx = shift;
-            
-            die OpException->new("This tamplate doesn't have a base template")
-                unless $base;
-            
-            push @baseStack, $base;            
-            my $block = $base->template->block;
-            
-            $base = $base->base;
-            
-            my $result = eval {
-                $ctx->process($block);
-            };
-            my $e = $@;
-            
-            $base = pop @baseStack;
-            
-            die $e if $e;
-            return $result;
-        };
-    } else {
-        $this->blocks( $template->blocks );
-    }
-    
-    if(my $blocks = $this->blocks) {
-        while (my ($name,$block) = each %$blocks) {
-            $context->define_block($name,$block);
-        }
-    }
-    
-    $context->stash->update({
-        require => sub {
-            my ($module) = @_;
-                
-            $module =~ s/^\.\//$baseLocation\//;
-            return $registry->Require($module);
-        }
-    });
-    $this->LoadLabelsToContext($context);
-}
-
-sub LoadLabelsToContext {
-	my ($this,$context) = @_;
-	
-	my %vars;
-	
-	%vars = %{$this->base->_labels || {}}
-	   if $this->base;
-	$this->_labels(\%vars);
-	
-	if(my $fname = $this->registry->loader->ResolveFileName($this->path)) {
-        my $flabels = "$fname.labels";
-        
-        if (-f $flabels) {
-            
-            my %labels;
-            $labels{default} = IMPL::Resources::Strings::ParseStringsMap($flabels);
-            
-            while(my($label,$text) = each %{$labels{default}}) {
-                $vars{$label} = sub {
-                    my ($params) = @_;
-                    my $locale = Resources->currentLocale;
-                    
-                    unless ($labels{$locale}) {
-                        $labels{$locale} = -f "$fname.$locale" ? 
-                          IMPL::Resources::Strings::ParseStringsMap("$fname.$locale") :
-                          {};
-                    }
-                    
-                    return FormatMessage(($labels{$locale}{$label} || $text),$params);
-                }
-            }
-            
-        }
-    }
-    $context->stash->update(\%vars);
-}
-
-sub MergeParameters {
-    my $this = shift;
-    my $refProps = shift || {};
-    
-    unless (ref($refProps) eq 'HASH') {
-        carp "Passing control name through the first parameter is deprecated";
-        my $name = $refProps;
-        $refProps = shift;
-        $refProps->{name} ||= $name;
-    }
-    
-    $refProps->{factory} = $this;
-    my $ctx = $this->context->clone();
-        
-    return ($this->template, $ctx, $refProps);
-}
-
-sub CreateObject {
-    my $this = shift;
-    
-    $this->activation eq 'singleton' ?
-        $this->_instance || $this->_instance($this->next::method(@_)) :
-        $this->next::method(@_);
-}
-
-sub Render {
-    my ($this, $args) = @_;
-    
-    return $this->new()->Render($args);
-}
-
-sub save {
-    die new IMPL::NotImplementedException("This class doesn't support serialization");
-}
-
-sub restore {
-    die new IMPL::NotImplementedException("This class doesn't support serialization");
-}
-
-1;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-C<IMPL::Web::View::TTFactory> - фабрика элементов управления
-
-=head1 SYNOPSIS
-
-=begin code
-
-my $factory = new IMPL::Web::View::TTFactory(
-    'IMPL::Web::View::TTControl',
-    $doc,
-    $context,
-    {
-        TRIM => 1
-    },
-    {
-        myprop => 'my value'
-    },
-);
-
-my $input1 = $factory->new('login', { class => "required" } );
-
-my $propval = $input->nodeProperty('myprop'); # 'my value'
-
-=end code
-
-=begin text
-
-[%
-    this.appendChild(
-        my.org.input.new('login', class = this.errors('login') ? "invalid" : "" )
-    );
-%]
-
-=end text
-
-=head1 DESCRIPTION
-
-C< Inherits L<IMPL::Object::Factory> >
-
-Соединяет в себе шаблон и заранее подготовленный контекст, который будет базой
-для создаваемых элементов управления.
-
-=head1 MEMBERS
-
-=over
-
-=item C<[get,set]template>
-
-Документ C<Template::Document> который описывает элемент управления. См. C<IMPL::Web::View::TTControl>.
-
-=item C<[get,set]context>
-
-Контекст фабрики элементов управления, в этом контексте выполняет шаблон элемента управления при загрузке.
-Далее в этом контексте будет выполнен блок инициализации при создании первого элемента управления.
-
-=item C<[get,set]opts>
-
-Параметры контекста элемента управления (ссылка на хеш). Каждый элемент управления при создании получает свой контекст,
-который создает с данными параметрами и хранилищем переменных, дочерним к контексту фабрики.
-
-=item C<[get,set]nodeProperties>
-
-Ссылка на хеш со значениями свойств по умолчанию для создаваемого элемента управления.
-
-=item C<[get]instances>
-
-Количество созданных элементов управления данной фабрикой
-
-=item C<[override]MergeParameters($name,$nodeProps)>
-
-Превращает значения переданные методу C<new> фабрики в параметры для создания элемента управления.
-
-=over
-
-=item C<$name>
-
-Имя создаваемого узла (C<nodeName>).
-
-=item C<$nodeProps>
-
-Ссылка на шех со значениями свойств узла. Данные значения будут совмещены со значениями из свойства C<nodeProperties>
-
-=back
-
-=item C<[override]CreateObject(@params)>
-
-Создает экземпляр элемента управления стандартным образом. Учитывает количество экземпляров и если это первый,
-то производит дополнительную инициализацию контекста выполнив блок шаблона C<INIT>.
-
-=item C<[inherited]new($name,$nodeProps)>
-
-Создает элемент управления с указанным именем и набором свойств. 
-
-=back
-
-=cut
\ No newline at end of file
--- a/Lib/IMPL/Web/View/TTView.pm	Thu Oct 10 19:51:19 2013 +0400
+++ b/Lib/IMPL/Web/View/TTView.pm	Fri Oct 11 15:49:04 2013 +0400
@@ -1,6 +1,7 @@
 package IMPL::Web::View::TTView;
 use strict;
 
+use IMPL::lang qw(hashMerge);
 use IMPL::Const qw(:prop);
 use IMPL::declare {
 	require => {
@@ -13,10 +14,12 @@
 	],
 	props => [
 		options => PROP_RW,
-		view => PROP_RW,
+		viewBase => PROP_RW,
+		layoutBase => PROP_RW,
 		layout => PROP_RW,
 		tt_ext => PROP_RW,
-		includes => PROP_RW | PROP_LIST
+		includes => PROP_RW | PROP_LIST,
+		globals => PROP_RW
 	]
 };
 
@@ -30,7 +33,7 @@
 	my ($this,$model,$template,$args) = @_;
 	
 	my $context = Context->new($this->options);
-	my $layout = delete $args->{layout};
+	my $layout = delete $args->{layout} || $this->layout;
 	
 	return $context->invoke_environment(
 		sub {
@@ -41,14 +44,14 @@
 						return $ctx->render($layout,$args);
 					},
 					{
-						base => $this->layout,
+						base => $this->layoutBase,
 						content => sub {
 							$ctx->invoke_environment(
 								sub {
-									return shift->display($model,$template,$args);
+									return shift->display_model($model,$template,$args);
 								},
 								{
-									base => $this->view
+									base => $this->viewBase
 								}
 							)
 						}
@@ -57,21 +60,24 @@
 			} else {
 				return $ctx->invoke_environment(
 					sub {
-						return $ctx->display($model,$template,$args);
+						return $ctx->display_model($model,$template,$args);
 					},
 					{
-						base => $this->view
+						base => $this->viewBase
 					}
 				);
 			}
-		},{
-			includes => scalar($this->includes),
-			tt_ext => 'tt',
-			document => {},
-			debug => sub {
-				warn @_;
-			}
-		}
+		},hashMerge(
+			{
+				includes => scalar($this->includes),
+				tt_ext => $this->tt_ext,
+				document => {},
+				debug => sub {
+					warn @_;
+				}
+			},
+			$this->globals
+		)
 	);
 }
 
--- a/_test/temp.pl	Thu Oct 10 19:51:19 2013 +0400
+++ b/_test/temp.pl	Fri Oct 11 15:49:04 2013 +0400
@@ -16,8 +16,9 @@
 		RECURSION => 1000,
 		COMPILE_DIR => '/tmp/ttc'
 	},
-	view => 'site',
-	layout => 'layout',
+	viewBase => 'site',
+	layoutBase => 'layout',
+	layout => 'default',
 	includes => [
 		'packages'
 	]
@@ -42,8 +43,7 @@
 
 print $view->display(
 	$model,
-	'product/view',
-	{ layout => 'default' }
+	'product/view'
 ), "\n";
 
 print "render page: ",tv_interval($t,[gettimeofday]),"s\n";
@@ -52,8 +52,7 @@
 
 $view->display(
 	$model,
-	'product/view',
-	{ layout => 'default' }
+	'product/view'
 );
 
 print "2nd render page: ",tv_interval($t,[gettimeofday]),"s\n";