File Coverage

blib/lib/Web/App.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Web::App;
2             # $Id: App.pm,v 1.36 2009/03/23 00:44:49 apla Exp $
3              
4             our $VERSION = '1.21';
5              
6 1     1   865 use Class::Easy;
  1         2  
  1         11  
7 1     1   1560 use Data::Dumper;
  1         9634  
  1         121  
8              
9 1     1   1123 use IO::Easy;
  1         3523  
  1         5  
10              
11 1     1   51721 use Web::App::Config;
  0            
  0            
12              
13             use Web::App::Request;
14             use Web::App::Response;
15              
16             use Web::App::Session;
17              
18             has 'root';
19             has 'config';
20             has 'int';
21             has 'session';
22             has 'request';
23             has 'response';
24              
25             has 'project', is => 'rw';
26              
27             our $app = {};
28              
29             1;
30              
31             sub new {
32             my $class = shift;
33             my $params = {@_};
34            
35             bless $app, $class;
36            
37             debug "process initialization";
38            
39             my $t = timer ('project init');
40            
41             my $config_file;
42            
43             if ($params->{project}) {
44             my $project = $params->{project};
45             die "can't use package $project"
46             unless try_to_use ($project);
47            
48             # modules always in lib for Web::App
49             $app->{root} = $project->root;
50             $app->{project} = $project;
51             # !!! dirty xml hack
52             $config_file = $project->root->append ('etc', $project->id . '-web-app.xml')
53             unless -f $config_file;
54             } else {
55             $app->{root} = IO::Easy->new ($params->{'root'});
56             $config_file = $params->{'config'} || 'etc/config.xml';
57             }
58            
59             $t->lap ('config loading');
60            
61             # Анализирует входящий запрос, производит общую абстрактную
62             # обработку запроса
63            
64             debug "creating Web::App object in $app->{root}";
65            
66             debug 'loading configuration';
67            
68             my $config = Web::App::Config->get ($app, $config_file);
69            
70             $app->{config} = $config;
71            
72             $t->lap ('modules loading');
73            
74             $config->init_modules;
75            
76             $t->end;
77            
78             return $app;
79             }
80              
81             # accessors here
82              
83             sub home {
84             shift->{root};
85             }
86              
87             sub app {
88             $app;
89             }
90              
91             sub receive_request {
92             my $self = shift;
93            
94             # initialization
95             my $request = $self->{request} = Web::App::Request->new ($app);
96             my $response = $self->{response} = Web::App::Response->new;
97            
98             $request->handle ($self);
99            
100             $response->{data}->{request} = $request; # for presentation
101            
102             my $screen = $self->request->screen;
103            
104             # TODO CHANGE DESCRIPTION
105             # we don't init session because some session
106             # internals must be preloaded
107            
108             my $session = Web::App::Session->detect;
109            
110             }
111              
112             sub handler {
113             my $self = shift;
114            
115             my $r = Web::App::Request->new ($app);
116            
117             return $r;
118             }
119              
120             sub var {
121             my $self = shift;
122             return $self->response->data;
123             }
124              
125             sub expand_params {
126             my $self = shift;
127             my $params = shift;
128            
129             my $session = $self->session;
130             my $request = $self->request;
131             my $form = $request->params;
132            
133             my $dirs = {
134             'data-dir' => $self->root . '/var/db/sharedwork',
135             'root' => $self->root,
136             'path_info' => $request->path_info,
137             'session_id' => $session->id,
138             'screen_id' => $request->screen->id,
139             'dir_info' => $request->dir_info,
140             'file_name' => $request->file_name,
141             'file_extension' => $request->file_extension,
142             'base_uri' => $request->base_uri,
143             'var' => $self->var,
144             'form' => {map {
145             $_ => $form->{$_}->[0]
146             } grep {! /CGI\:\:Minimal/} keys %$form},
147             };
148            
149             my $counter = 1;
150             foreach my $match (@{$request->screen_matches}) {
151             $dirs->{$counter} = $match;
152             $counter++;
153             }
154            
155             if (defined ref $params and ref $params eq 'HASH') {
156             foreach my $key (keys %$params) {
157             #supports xslt notation: {$aaa}
158             # 3-letters
159             my $val = $params->{$key};
160             my $pos = index ($val, '{$');
161             while ($pos > -1) {
162             my $end = index ($val, '}', $pos);
163             my $str = substr ($val, $pos + 2, $end - $pos - 2);
164            
165             # warn "found replacement: key => $key, requires => \$$str\n";
166            
167             my $fix;
168             if (index ($str, '/') > -1) { # treat as path
169             # warn join ', ', keys %{$self->var};
170             $fix = Web::App::Config::path_to_val ($dirs, $str);
171             } else { # scalar
172             $fix = $dirs->{$str};
173             }
174            
175             # warn "value for replace is: $fix\n";
176            
177             if ($pos == 0 and $end == (length ($val) - 1)) {
178             $val = $fix;
179             } else {
180             substr ($val, $pos, $end - $pos + 1, $fix);
181             }
182             $pos = index ($val, '{$', $end);
183             }
184             $params->{$key} = $val;
185             # warn ("key is: $key, param is: $1");
186             }
187             } else { # what this?
188             $params =~ s/(?:\$\{|\{\$)([\w\-_0-9]+)\}/$dirs->{$1}/g;
189             return $params;
190             }
191            
192             }
193              
194              
195             sub process_request {
196             my $self = shift;
197              
198             my $request = $self->request;
199             my $response = $self->response;
200              
201             my $screen = $self->request->screen;
202            
203             # adding processors from config for current screen into request
204             my $processors = [];
205             if ($request->data_available) {
206             $processors = $screen->process_calls;
207             } else {
208             $processors = $screen->init_calls;
209             }
210            
211             push @{$request->processors}, @$processors
212             if defined $processors;
213            
214             $request->presentation ($screen->{'presentation'});
215            
216             while (my $processor = $request->next_processor) {
217            
218             last unless defined $processor;
219            
220             my $processor_params = {%$processor}; # copy
221            
222             my $result_place = delete $processor_params->{place};
223            
224             $self->expand_params ($processor_params);
225            
226             my $processor_call = $processor_params->{sub};
227              
228             debug "launch '$processor_call'";
229            
230             my ($pack, $method) = split '->', $processor_call;
231             if ($pack =~ /^\$([^:]+)$/) {
232             $pack = $app->$1;
233             }
234            
235             my $result = eval {
236             $pack->$method ($self, $processor_params);
237             };
238            
239             if (defined $result and $result) {
240            
241             die "you must supply place for results"
242             unless $result_place;
243            
244             die "you can't override $result_place"
245             if exists $app->var->{$result_place};
246            
247             $app->var->{$result_place} = $result;
248             }
249            
250             # eval "$processor_call (\$self, \$processor_params)";
251             critical "after '$processor_call' launch: $@"
252             if $@;
253             }
254            
255             debug "processors finished";
256            
257             my $location = $self->{'redirect-to'};
258            
259             # !!! need to be replaced for correct headers output.
260              
261             debug Dumper $response->data
262             if $Class::Easy::DEBUGIMMEDIATELY;
263            
264             if ($location) {
265             if ($Class::Easy::DEBUGIMMEDIATELY) {
266             # print "Location: $location\n\n";
267             debug "actual headers are below";
268             } else {
269             $self->response->headers->header ('Location' => $location);
270             }
271             }
272              
273              
274             }
275              
276             sub handle_request ($$) {
277             my $class = shift;
278             my $r = shift;
279            
280             my $self = $class->app;
281            
282             delete $self->{'redirect-to'};
283            
284             my $t = timer ('request retrieval');
285            
286             $self->receive_request;
287            
288             $t->lap ('accessors');
289            
290             my $request = $self->request;
291             my $screen = $request->screen;
292            
293             my $session = $self->session;
294            
295             $t->lap ('authentication');
296            
297             if ($screen->authenticated ($session)) {
298            
299             $t->lap ('processors work');
300            
301             $self->process_request;
302            
303             } else {
304              
305             debug "screen not authenticated";
306             $self->clear_process_queue;
307             $self->set_presentation_screen ('login');
308              
309             }
310            
311             $t->lap ('presentation');
312            
313             my $content;
314             my $status;
315              
316             my $can_set_status = $request->can ('set_status');
317              
318             if ($self->redirected) {
319             $request->set_status (302)
320             if $can_set_status;
321              
322             $self->send_headers;
323              
324             } else {
325             $request->set_status (200)
326             if $can_set_status;
327              
328             $self->prepare_presenter;
329             $content = $self->run_presenter;
330            
331             $self->send_headers;
332            
333             $request->send_content ($content);
334             }
335              
336             $t->end;
337             debug "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< request finished";
338             $t->total;
339            
340             $request->done_status;
341             }
342              
343             sub prepare_presenter {
344             my $app = shift;
345            
346             # maybe processor changed presentation
347             my $presentation = $app->request->presentation;
348            
349             my $presenter = $app->config->presenters->{$presentation->{'type'}};
350            
351             $app->response->presenter ($presenter);
352            
353             $presenter->headers;
354              
355             }
356              
357             sub debug_log { # TODO: more optimal way without copying
358             my $self = shift;
359            
360             my $result = $Web::App::LOG;
361            
362             $Web::App::LOG = '';
363            
364             my $presentation = $self->request->presentation;
365             my $presentation_type = $presentation->{'type'};
366            
367             # we must prettify log for html
368            
369             my $presenters = $self->config->presenters;
370             my $presenter;
371            
372             if ($presentation_type) {
373             $presenter = $presenters->{$presentation_type};
374             } else {
375             if ($self->response->headers->content_type =~ /text\/html/) {
376             $presenter = $presenters->{'xslt'};
377             warn "we hacked into xslt";
378             }
379             }
380              
381             if ($presenter and $presenter->can ('wrap_log')) {
382             return $presenter->wrap_log ($result);
383             } else {
384             return $result;
385             }
386              
387             }
388              
389             sub send_headers {
390             my $app = shift;
391            
392             my $request = $app->request;
393            
394             return if $request->headers_sent;
395            
396             my $headers = $app->response->headers;
397            
398             $request->send_headers ($headers);
399             debug "headers are: ", $headers->as_string;
400            
401             $request->headers_sent (1);
402             }
403              
404             sub set_presentation {
405             my $self = shift;
406             my $presentation = shift;
407            
408             $self->request->presentation ($presentation);
409             }
410              
411             sub set_presentation_screen {
412             my $self = shift;
413             my $screen_name = shift;
414            
415             my $screen = $self->config->screen ($screen_name)->{'?'};
416            
417             $self->request->presentation ($screen->{'presentation'});
418            
419             $self->request->screen ($screen);
420            
421             }
422              
423             sub clear_process_queue {
424             my $self = shift;
425            
426             debug 'requested for clearing processor queue, processed';
427            
428             $self->request->processors ([]);
429             }
430              
431             sub redirect_to_screen {
432             my $self = shift;
433             my $screen = shift;
434            
435             my $request = $self->request;
436            
437             return unless $request->type eq 'CGI';
438            
439             # TODO CRITICAL: fix for proto (https) and port
440            
441             my $base_uri = $request->base_uri;
442             my $host = $request->host;
443             if ($self->project and exists $self->project->config->{'hostname'}) {
444             $host = $self->project->config->{'hostname'};
445             }
446              
447             if ( $request->{'session-id'} ) {
448             my $session_id = $request->{'session-id'};
449             $self->{'redirect-to'} = "http://$host$base_uri/$session_id\@$screen";
450            
451             } else {
452             $self->{'redirect-to'} = "http://$host$base_uri/$screen";
453             }
454             }
455              
456             sub redirect {
457             my $self = shift;
458             my $url = shift;
459            
460             debug "requested redirect to uri: $url";
461              
462             my $request = $self->request;
463            
464             $self->{'redirect-to'} = $url;
465             }
466              
467             sub redirected {
468             my $self = shift;
469            
470             my $status = 0;
471             $status = 1 if exists $self->{'redirect-to'} and $self->{'redirect-to'} ne '';
472             return $status;
473             }
474              
475             sub run_presenter {
476             my $self = shift;
477            
478             my $presentation = $self->request->presentation;
479              
480             debug "presenter: " . $presentation->{'type'}
481             . (defined $presentation->{'file'} ? " in " . $presentation->{'file'} : '');
482              
483             my $presenter = $self->response->presenter;
484            
485             critical "maybe you want to register presenter, because i nothing knows about '$presentation->{type}'"
486             unless defined $presenter;
487            
488             my $data = $self->response->data;
489            
490             my $content;
491             eval {
492             $content = $presenter->process ($self, $data, %$presentation);
493             };
494            
495             debug $@ if $@;
496            
497             return $content;
498             }
499              
500             1;