Mercurial > pub > Impl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 347:3eafa6fefa9f | 348:f116cd9fe7d9 |
|---|---|
| 1 package IMPL::Web::View::TTContext; | 1 package IMPL::Web::View::TTContext; |
| 2 use strict; | 2 use strict; |
| 3 use Template::Base; | 3 use Template::Base; |
| 4 | 4 use Carp qw(carp); |
| 5 use IMPL::lang qw(is); | 5 |
| 6 use IMPL::Exception(); | |
| 7 use IMPL::lang qw(is typeof hashApply); | |
| 6 use IMPL::declare { | 8 use IMPL::declare { |
| 7 require => [ | 9 require => { |
| 8 Document => '-Template::Document' | 10 Document => '-Template::Document', |
| 9 ], | 11 TypeKeyedCollection => 'IMPL::TypeKeyedCollection', |
| 10 base => { | 12 ArgException => "-IMPL::InvalidArgumentException" |
| 13 }, | |
| 14 base => [ | |
| 11 'Template::Context' => '@_' | 15 'Template::Context' => '@_' |
| 12 } | 16 ] |
| 13 }; | 17 }; |
| 14 | 18 |
| 15 BEGIN { | 19 BEGIN { |
| 16 no strict 'ref'; | 20 no strict 'refs'; |
| 17 foreach my $prop (qw( | 21 foreach my $prop (qw( |
| 22 root | |
| 18 base | 23 base |
| 19 tt_ext | 24 tt_ext |
| 20 shared | 25 tt_cache |
| 21 parent | 26 parent |
| 22 prefix | 27 prefix |
| 28 cache | |
| 29 includes | |
| 23 )) { | 30 )) { |
| 24 my $t = $prop; | 31 my $t = $prop; |
| 25 | 32 |
| 26 *{__PACKAGE__ . '::' . $name} = sub { | 33 *{__PACKAGE__ . '::' . $prop} = sub { |
| 27 my $this = shift; | 34 my $this = shift; |
| 28 return @_ ? $this->stash->set($t, @_) : $this->stash->get($t); | 35 return @_ ? $this->stash->set($t, @_) : $this->stash->get($t); |
| 29 } | 36 } |
| 30 } | 37 } |
| 31 } | 38 } |
| 38 | 45 |
| 39 my $args = { %{$this} }; | 46 my $args = { %{$this} }; |
| 40 | 47 |
| 41 $this->delocalise(); | 48 $this->delocalise(); |
| 42 | 49 |
| 43 my $class = typeof($this); | 50 my $class = ref($this); |
| 44 | 51 |
| 45 delete $args->{CONFIG}; | 52 delete $args->{CONFIG}; |
| 46 | 53 |
| 47 my $clone = $class->new($args); | 54 my $clone = $class->new($args); |
| 48 | 55 |
| 50 | 57 |
| 51 return $clone; | 58 return $clone; |
| 52 } | 59 } |
| 53 | 60 |
| 54 sub find_template { | 61 sub find_template { |
| 55 my ($this,$name,@inc) = @_; | 62 my ($this,$name) = @_; |
| 56 | 63 |
| 57 push @inc, ""; | 64 my $cache = $this->tt_cache; |
| 65 $this->tt_cache($cache = {}) unless $cache; | |
| 66 | |
| 67 if(my $tpl = $cache->{$name}) { | |
| 68 return $tpl; | |
| 69 } | |
| 70 | |
| 71 my @inc = ($this->base, @{$this->includes || []}); | |
| 72 | |
| 58 my $ext = $this->tt_ext || ""; | 73 my $ext = $this->tt_ext || ""; |
| 74 | |
| 75 my $file; | |
| 59 | 76 |
| 60 foreach my $dir (@inc) { | 77 foreach my $dir (@inc) { |
| 61 my $file = "$dir/$name$ext"; | 78 $file = $dir ? "$dir/$name" : $name; |
| 79 | |
| 80 my $base = join('/',splice([split(/\/+/,$file)],0,-1)); | |
| 81 | |
| 82 $file = $ext ? "$file.$ext" : $file; | |
| 83 | |
| 84 warn "lookup: $file"; | |
| 85 | |
| 62 my $tt = eval { $this->template($file) }; | 86 my $tt = eval { $this->template($file) }; |
| 63 | 87 |
| 64 return { | 88 return $cache->{$name} = { |
| 65 # if we load a block then we should use the current base directory | 89 base => $base, |
| 66 base => is($tt,Document) ? $dir : $this->base, | 90 template => $tt, |
| 67 isDocument => is($tt,Document), | |
| 68 name => $name, | |
| 69 file => $file, | |
| 70 template => $tt | |
| 71 } if $tt; | 91 } if $tt; |
| 72 } | 92 } |
| 73 | 93 |
| 74 $this->throw(Template::Constants::ERROR_FILE, "$name: not found"); | 94 $this->throw(Template::Constants::ERROR_FILE, "$name: not found"); |
| 75 } | |
| 76 | |
| 77 sub require { | |
| 78 my ($this,$name) = @_; | |
| 79 | |
| 80 return $this->stash->get([ 'require', [$name] ]); | |
| 81 } | 95 } |
| 82 | 96 |
| 83 sub display { | 97 sub display { |
| 84 my $this = shift; | 98 my $this = shift; |
| 85 my $model = shift; | 99 my $model = shift; |
| 86 my $template, $args; | 100 my ($template, $args); |
| 87 | 101 |
| 88 if (ref $_[0] eq 'HASH') { | 102 if (ref $_[0] eq 'HASH') { |
| 89 $args = shift; | 103 $args = shift; |
| 90 } else { | 104 } else { |
| 91 $template = shift; | 105 $template = shift; |
| 92 $args = shift; | 106 $args = shift; |
| 93 } | 107 } |
| 94 | 108 |
| 95 my $prefix = $this->prefix | 109 my $prefix = $this->prefix; |
| 96 | 110 |
| 97 my $cloned = $this->clone({ | 111 if (not(($args and delete $args->{'-no-resolve'}) or ref $model)) { |
| 98 prefix => $prefix, | 112 $prefix = $prefix ? "${prefix}.${model}" : $model; |
| 99 shared => $this->shared || $this, | 113 $model = $this->resolve_model($model); |
| 100 parent => $this | 114 } else { |
| 101 }); | 115 $prefix = ""; |
| 102 | 116 } |
| 103 | 117 |
| 104 | 118 $template = $template ? $this->find_template($template) : $this->find_template_for($model); |
| 119 | |
| 120 return $this->render( | |
| 121 $template, | |
| 122 hashApply( | |
| 123 { | |
| 124 prefix => $prefix, | |
| 125 model => $model, | |
| 126 }, | |
| 127 $args | |
| 128 ) | |
| 129 ); | |
| 130 } | |
| 131 | |
| 132 sub invoke_environment { | |
| 133 my ($this,$code,$env) = @_; | |
| 134 | |
| 135 $env ||= {}; | |
| 136 | |
| 137 my $out = eval { | |
| 138 $this->localise( | |
| 139 hashApply( | |
| 140 { | |
| 141 root => $this->root || $this, | |
| 142 cache => TypeKeyedCollection->new(), | |
| 143 display => sub { | |
| 144 $this->display(@_); | |
| 145 }, | |
| 146 render => sub { | |
| 147 $this->render(@_); | |
| 148 } | |
| 149 }, | |
| 150 $env | |
| 151 ) | |
| 152 ); | |
| 153 | |
| 154 &$code($this); | |
| 155 }; | |
| 156 | |
| 157 my $e = $@; | |
| 158 $this->delocalise(); | |
| 159 | |
| 160 die $e if $e; | |
| 161 | |
| 162 return $out; | |
| 163 } | |
| 164 | |
| 165 sub render { | |
| 166 my ($this,$template,$args) = @_; | |
| 167 | |
| 168 $args ||= {}; | |
| 169 | |
| 170 #TODO handle classes | |
| 171 | |
| 172 my $base; | |
| 173 | |
| 174 $template = $this->find_template($template) unless ref $template; | |
| 175 | |
| 176 if (ref $template eq 'HASH') { | |
| 177 $base = $template->{base}; | |
| 178 $template = $template->{template}; | |
| 179 } else { | |
| 180 carp "got an invalid template object: $template"; | |
| 181 $base = $this->base; | |
| 182 } | |
| 183 | |
| 184 return $this->invoke_environment( | |
| 185 sub { | |
| 186 return shift->include($template,$args); | |
| 187 }, | |
| 188 { | |
| 189 base => $base, | |
| 190 parent => $this | |
| 191 } | |
| 192 ) | |
| 193 } | |
| 194 | |
| 195 sub resolve_model { | |
| 196 my ($this,$prefix) = @_; | |
| 197 | |
| 198 die ArgException->new(prefix => "the prefix must be specified") | |
| 199 unless defined $prefix; | |
| 200 | |
| 201 #TODO handle DOM models | |
| 202 | |
| 203 my @comp = map { $_, 0 } grep length($_), split(/\.|\[(\d+)\]/, $prefix); | |
| 204 | |
| 205 return $this->stash->get(['model',0,@comp]); | |
| 206 } | |
| 207 | |
| 208 sub find_template_for { | |
| 209 my ($this,$model) = @_; | |
| 210 | |
| 211 my $type = typeof($model); | |
| 212 | |
| 213 return $this->find_template('templates/plain') unless $type; | |
| 214 | |
| 215 if (my $template = $this->cache->Get($type)) { | |
| 216 return $template; | |
| 217 } else { | |
| 218 | |
| 219 no strict 'refs'; | |
| 220 | |
| 221 my @isa = $type; | |
| 222 | |
| 223 while (@isa) { | |
| 224 my $sclass = shift @isa; | |
| 225 | |
| 226 (my $name = $sclass) =~ s/:+/_/g; | |
| 227 | |
| 228 $template = $this->find_template("templates/$name"); | |
| 229 | |
| 230 if ($template) { | |
| 231 $this->cache->Set($sclass,$template); | |
| 232 return $template; | |
| 233 } | |
| 234 | |
| 235 push @isa, @{"${sclass}::ISA"}; | |
| 236 } | |
| 237 | |
| 238 } | |
| 239 | |
| 240 return; | |
| 105 } | 241 } |
| 106 | 242 |
| 107 1; | 243 1; |
| 108 | 244 |
| 109 __END__ | 245 __END__ |
| 120 | 256 |
| 121 =begin plantuml | 257 =begin plantuml |
| 122 | 258 |
| 123 @startuml | 259 @startuml |
| 124 | 260 |
| 125 object SharedContext { | 261 object RootContext { |
| 126 document | 262 document |
| 127 globals | 263 globals |
| 128 } | 264 } |
| 129 | 265 |
| 130 object DocumentContext { | 266 object DocumentContext { |
| 135 object ControlContext { | 271 object ControlContext { |
| 136 base | 272 base |
| 137 extends | 273 extends |
| 138 } | 274 } |
| 139 | 275 |
| 140 SharedContext o-- DocumentContext | 276 RootContext o-- DocumentContext |
| 141 SharedContext o-- ControlContext | 277 RootContext o-- ControlContext |
| 142 | 278 |
| 143 Document -- DocumentContext | 279 Document -- DocumentContext |
| 144 Control - ControlContext | 280 Control - ControlContext |
| 145 | 281 |
| 146 Loader . SharedContext: <<creates>> | 282 Loader . RootContext: <<creates>> |
| 147 Loader . Document: <<creates>> | 283 Loader . Document: <<creates>> |
| 148 Loader -up- Registry | 284 Loader -up- Registry |
| 149 | 285 |
| 150 @enduml | 286 @enduml |
| 151 | 287 |
