blib/lib/Template/Parser/RemoteInclude.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 13 | 15 | 86.6 |
branch | n/a | ||
condition | n/a | ||
subroutine | 5 | 5 | 100.0 |
pod | n/a | ||
total | 18 | 20 | 90.0 |
line | stmt | bran | cond | sub | pod | time | code | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | package Template::Parser::RemoteInclude; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
2 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3 | 1 | 1 | 63067 | use strict; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 37 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
4 | 1 | 1 | 7 | use warnings; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 46 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
5 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
6 | our $VERSION = '0.01'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
7 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
8 | 1 | 1 | 889 | use namespace::autoclean; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 23406 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
9 | 1 | 1 | 1666 | use AnyEvent; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 6529 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | 38 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
10 | 1 | 1 | 444 | use AnyEvent::Curl::Multi; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
11 | use HTTP::Request; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
12 | use Scalar::Util qw(refaddr); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
13 | use Try::Tiny; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
14 | use Sub::Install; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
15 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
16 | use base 'Template::Parser'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
17 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
18 | # парсер ничего не знает о переменных, которые будут проинициализированы в шаблоне, т.к. он его ещё разбирает, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
19 | # но, вот получить хэш переменных - вполне необходимо, дабы упростить использование модуля | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
20 | $Template::Parser::RemoteInclude::old_tt_process = \&Template::process; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
21 | Sub::Install::reinstall_sub({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
22 | code => sub { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
23 | my $providers = $_[0]->service->context->{ LOAD_TEMPLATES }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
24 | for (@$providers) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
25 | next unless $_; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
26 | if (UNIVERSAL::isa($_->{PARSER},'Template::Parser::RemoteInclude')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
27 | $_->{PARSER}->{__stash} = $_[2] || {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
28 | last; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
29 | }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
30 | } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
31 | return $Template::Parser::RemoteInclude::old_tt_process->(@_); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
32 | }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33 | into => "Template", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
34 | as => "process", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
35 | }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
36 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
37 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
38 | =head1 NAME | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
39 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
40 | Template::Parser::RemoteInclude - call remote template-server inside your template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
41 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
42 | =head1 DESCRIPTION | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
43 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
44 | You can write your own html aggregator for block build pages. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
45 | However, this module allows you to make remote calls directly from the template. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
46 | This is very useful when your project have a template server. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
47 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
48 | This module allows you to make any http-requests from template. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
49 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
50 | Depends on L |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
51 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
52 | L |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
53 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
54 | Use and enjoy! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
55 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
56 | =head1 NOTE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
57 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
58 | =over 4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
59 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
60 | =item * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
61 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
62 | Directive C |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
63 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
64 | =item * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
65 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
66 | Parser does not know anything about L |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
67 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
68 | =item * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
69 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
70 | Content of the response can be as a simple html or a new template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
71 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
72 | =item * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
73 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
74 | Contents of the response is recursively scanned for directives C |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
75 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
76 | =item * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
77 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
78 | The best option when your template-server is located on the localhost | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
79 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
80 | =back | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
81 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
82 | =head1 SYNOPSIS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
83 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
84 | create C object with C [% name = "Boris" %][% RINCLUDE "http://example.com/.../another" %]' 138 # and 139 # http://example.com/.../another => 140 # 'And goodbye, [% name %]!' 141 142 # example 143 my $tmpl = "[% RINCLUDE GET 'http://example.com/get/template/hello_world' %]"; 144 $tt->process(\$tmpl,{name => 'User'}); 145 146 # returned 147 Hello, User! And goodbye, Boris! 148 149 more power example 150 151 use Template; 152 use Template::Parser::RemoteInclude; 153 154 my $tt = Template->new( 155 INCLUDE_PATH => '....', 156 ...., 157 PARSER => Template::Parser::RemoteInclude->new( 158 'Template::Parser' => { 159 ...., 160 }, 161 'AnyEvent::Curl::Multi' => { 162 max_concurrency => 10, 163 ...., 164 } 165 ), 166 WRAPPER => 'dummy.tt2' 167 ); 168 169 # where 'dummy.tt2' 170 # [% IF CSS %] 171 # [% FOREACH c = CSS %] 172 # css = [% c %] 173 # [% END %] 174 # [% END %] 175 # ==== 176 # [% content %] 177 # ==== 178 179 # http://example.com/get/template/hello_world => 180 # "[% CSS.push('http://example.com/file.css') %]\nHello, [% name %]!\n" 181 182 my $tmpl = "[% SET CSS = [] %][% RINCLUDE GET 'http://example.com/get/template/hello_world' %]"; 183 $tt->process(\$tmpl,{name => 'User'}); 184 185 # output: 186 # css = http://example.com/file.css 187 # 188 # ==== 189 # 190 # Hello, User! 191 # 192 # ==== 193 194 =head1 METHODS 195 196 =head2 new('Template::Parser' => %param1, 'AnyEvent::Curl::Multi' => %param2) 197 198 Simple constructor 199 200 =cut 201 sub new { 202 my ($class, %param) = @_; 203 204 my $self = $class->SUPER::new($param{'Template::Parser'}); 205 $self->{iparam} = $param{'AnyEvent::Curl::Multi'} || {}; 206 207 return $self; 208 } 209 210 sub _parse { 211 my ($self, $tokens, $info) = @_; 212 $self->{ _ERROR } = ''; 213 214 $self->{aecm} = AnyEvent::Curl::Multi->new(%{$self->{iparam}}); 215 216 # выгребем все id элементов массива с RINCLUDE и url в качесвте первого аргумента 217 my @ids_rinclude = (); 218 for (0..$#$tokens) { 219 if ( 220 UNIVERSAL::isa($tokens->[$_],'ARRAY') and 221 UNIVERSAL::isa($tokens->[$_]->[2],'ARRAY') and 222 $tokens->[$_]->[2]->[1] and 223 not ref $tokens->[$_]->[2]->[1] and 224 $tokens->[$_]->[2]->[1] eq 'RINCLUDE' 225 ) { 226 push @ids_rinclude, $_; 227 } 228 } 229 230 # хэш-связка: id элемента в массиве -> ссылка в памяти 231 my $ids_rinclude = {}; 232 # наполним хэш: ссылка в памяти -> объект запроса 233 my %requests = map { 234 my $req = $self->_make_request($tokens->[$_]); 235 return unless $req; 236 my $addr = refaddr($req); 237 $ids_rinclude->{$_} = $addr; 238 ($addr => $req); 239 } @ids_rinclude; 240 241 # зарегистрируем запросы в Curl::Multi 242 my @handler_cm = map {$self->{aecm}->request($_)} values %requests; 243 244 # колбэчимся и в колбэке переопределяем значения в %requests 245 $self->{aecm}->reg_cb(response => sub { 246 my ($client, $request, $response, $stats) = @_; 247 $requests{refaddr($request)} = $response->content; 248 #$requests{refaddr($request)} = "[% CSS.push('http://example.com/file.css') %]\nHello, [% name %]!\n"; 249 }); 250 251 $self->{aecm}->reg_cb(error => sub { 252 my ($client, $request, $errmsg, $stats) = @_; 253 $self->debug("error returned RINCLUDE for url: ".$request->uri." - $errmsg") if $self->{ DEBUG }; 254 $self->error("RINCLUDE for url: ".$request->uri." - $errmsg"); 255 #$requests{refaddr($request)} = $errmsg; 256 }); 257 258 # поднимаем событие обхода для Curl::Multi 259 $self->{timer_w} = AE::timer(0, 0, sub { $self->{aecm}->_perform }) if (@handler_cm and not $self->{timer_w}); 260 261 # погнали (see AnyEvent::Curl::Multi) 262 for my $crawler (@handler_cm) { 263 try { 264 $crawler->cv->recv; 265 } catch { 266 $self->debug("error returned RINCLUDE for url: ".$crawler->{req}->uri." - $_") if $self->{ DEBUG }; 267 $self->error("RINCLUDE for url: ".$crawler->{req}->uri." - $_"); 268 #$requests{refaddr($crawler->{req})} = $_; 269 }; 270 }; 271 272 return if $self->error; 273 274 # # replace tokens RINCLUDE to simple value 275 # for (@ids_rinclude) { 276 # $tokens->[$_] = [ 277 # "'".$ids_rinclude->{$_}."'", # unic name - addr 278 # 1, 279 # $self->split_text($requests{$ids_rinclude->{$_}}) 280 # ]; 281 # } 282 283 # extend tokens RINCLUDE to new array values from request 284 for (@ids_rinclude) { 285 my $parse_upload = $self->split_text($requests{$ids_rinclude->{$_}}); 286 my $added_len = $#$parse_upload; 287 splice(@$tokens, $_, 1, @$parse_upload); 288 $_ += $added_len for @ids_rinclude; 289 } 290 291 my $cli = delete $self->{aecm}; 292 undef $cli; 293 294 # методично, как тузик тряпку, продожаем обработку токенов, пока не исчерпаем все RINCLUDE, если они пришли в контенте ответов 295 if (@ids_rinclude) { 296 return $self->_parse($tokens, $info); 297 } else { 298 delete $self->{__stash}; 299 return $self->SUPER::_parse($tokens, $info); 300 } 301 } 302 303 sub _strip { 304 my $text = shift; 305 return $text unless $text; 306 $text =~ s/(^$1|$1$)//g if $text =~ /^(['"])/; 307 return $text; 308 } 309 310 sub _make_request { 311 my $self = shift; 312 my $token = shift; 313 return unless (UNIVERSAL::isa($token,'ARRAY') and UNIVERSAL::isa($token->[2],'ARRAY')); 314 my @token = @{$token->[2]}; 315 316 # skip RINCLUDE 317 splice(@token,0,2); 318 319 my $ret = HTTP::Request->new(); 320 my $is_header = 0; my @headers = (); 321 while (@token) { 322 my $type = shift @token || ''; 323 my $val = shift @token || ''; 324 $val = _strip($val) || ''; 325 326 next if ($type eq 'ASSIGN' or $type eq 'COMMA'); 327 328 if ($type eq 'IDENT') { 329 $type = 'LITERAL'; 330 $val = $self->{__stash}->{$val}; 331 if (UNIVERSAL::isa($val, 'HTTP::Request')) { 332 $self->debug("uri found: ".$val->uri) if $self->{ DEBUG }; 333 return $val; 334 }; 335 }; 336 337 if ($val eq 'GET' or $val eq 'POST' or $val eq 'PUT' or $val eq 'DELETE') { 338 $ret->method($val); 339 } elsif ($type eq '[') { 340 $is_header = 1; 341 } elsif ($type eq ']') { 342 $is_header = 0; 343 while (@headers) {$ret->header(splice(@headers,0,2))} 344 } elsif ($type eq 'LITERAL' and $is_header) { 345 push @headers, UNIVERSAL::isa($val, 'ARRAY') ? @$val : $val; 346 } elsif ($type eq 'LITERAL' and not $is_header) { 347 if ($ret->uri) { 348 $ret->content($val); 349 } else { 350 $ret->uri($val); 351 $self->debug("uri found: ".$ret->uri) if $self->{ DEBUG }; 352 }; 353 } else { 354 # skip unknown 355 next; 356 } 357 } 358 359 return $ret; 360 } 361 362 =head1 SEE ALSO 363 364 L
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=head1 AUTHOR |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
mr.Rico |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=cut |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
1; |
373
|
|
|
|
|
|
|
__END__ |
|