File Coverage

blib/lib/Apache/ContentHandler.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Apache::ContentHandler;
2              
3             =head1 NAME
4              
5             Apache::ContentHandler - mod_perl extension for uniform application
6             generation.
7              
8             =head1 SYNOPSIS
9              
10             use Apache::ContentHandler;
11              
12             @ISA = 'Apache::ContentHandler';
13              
14             sub handler {
15             my $r = shift;
16             my $algometer = new Apache::Algometer($r);
17             my $result = $algometer->run;
18             return $result;
19             }
20              
21             sub _init {
22             my $self = shift || die 'need $self';
23             $self->SUPER::_init(@_);
24              
25             # overrides
26             $self->{title} = 'Project Algometer';
27             $self->{subtitle} = "Version $VERSION";
28             $self->{default_action} = 'hello';
29             # other variable definitions
30             }
31              
32             sub hello {
33             return '

Hello World

';
34             }
35              
36             =head1 DESCRIPTION
37              
38             Apache::ContentHandler is a generic framework for creating mod_perl
39             based applications. It provides a basic event mechanism and a
40             subclassable framework for customizing actions.
41              
42             The synopsis shows a very simple example of what it can do. In this
43             case, we set the default_action to 'hello', which is automatically
44             executed. Hello in this case outputs a simple paragraph. Nothing big,
45             but it is very simple. Note that this app runs as-is in both CGI and
46             mod_perl.
47              
48             =head2 Rapid Prototyping
49              
50             This does not demonstrate the real power of ContentHandler. The real
51             power comes from rapid prototyping. For example, if we modifed the
52             example above to read:
53              
54             sub hello {
55             my $self = shift || die 'need $self';
56             my $s = '';
57             $s .= '\Make\ something.';
58             return $s;
59             }
60              
61             Then the page will output a url for the application that includes
62             "action=make" as a url parameter. This will tell ContentHandler to run
63             the method make when executed. But, 'make' does not exist at this
64             time. That is ok, because ContentHandler will deal with it by putting
65             a standard page up explaining that that feature is not yet
66             implemented. This allows you to quickly prototype one page, and move
67             on to the rest of the functionality one piece at a time.
68              
69             I have used this style with clients on several different projects and
70             they were all extremely happy to get something tangible in a very
71             short period of time, usually 5 minutes to get the first page up and
72             running with skeletal functionality. From there, it is a very
73             interactive process with the client driving on one machine and
74             commenting, and me coding away at another machine as they talk.
75              
76             =head1 PUBLIC METHODS
77              
78             =over 4
79              
80             =cut
81              
82 1     1   747 use strict;
  1         2  
  1         35  
83 1     1   5 use vars qw($VERSION);
  1         2  
  1         47  
84 1     1   1425 use Apache::Constants qw(:response);
  0            
  0            
85             use Mail::Mailer;
86              
87             use CGI qw(:html2 :html3 :form param url *table);
88             local $^W = 1;
89              
90             $VERSION = '1.3.3';
91              
92             =item * $ch = Apache::ContentHandler->new
93              
94             Creates a new ContentHandler. You should not override this, override
95             _init instead.
96              
97             =cut
98              
99             sub new {
100              
101             my $proto = shift;
102             my $class = ref($proto) || $proto;
103             my $self = {};
104              
105             bless $self, $class;
106             $self->_init(@_);
107              
108             return $self;
109             }
110              
111             =item * $hc->run
112              
113             The main application structure. Provides for a standard header, body,
114             and footer. You probably do not want to override this, override the
115             individual methods instead.
116              
117             =cut
118              
119             sub run {
120             my $self = shift || die 'need $self';
121              
122             my $work = $self->work;
123             my $html = join("\n",
124             start_html(-Title=>$self->{title}
125             . ($self->{subtitle}
126             ? ": $self->{subtitle}" : ''),
127             -BGCOLOR=>'white'),
128             $self->header,
129             $work,
130             $self->footer,
131             end_html,
132             );
133              
134             if ($self->{redirect}) {
135             print $self->{cgi}->redirect(-uri=>$self->{redirect});
136             return REDIRECT;
137             } elsif (! $self->{noprint}) {
138             if ($self->{mod_perl}) {
139             my $request = $self->{request};
140             $request->content_type('text/html');
141             $request->no_cache(1);
142             $request->send_http_header;
143             return OK if $request->header_only;
144             print $html;
145             return OK;
146             } else {
147             print $self->{cgi}->header;
148             print $html;
149             }
150             } else {
151             return $work;
152             }
153             }
154              
155             ############################################################
156             # Standard CGI Functions:
157              
158             =back
159              
160             =head1 PROTECTED METHODS
161              
162             =over 4
163              
164             =item * _init
165              
166             Private: called by new. Override to put your application specific
167             variables here.
168              
169             =cut
170              
171             sub _init {
172             my $self = shift || die 'need $self';
173             my $request = shift;
174              
175             $self->{mod_perl} = exists $ENV{"MOD_PERL"};
176              
177             if ($self->{mod_perl}) {
178             $self->{request} = $request;
179             }
180              
181             $self->{cgi} = new CGI; # used in various places regardless of mod_perl
182              
183             $self->{url} = ($self->{mod_perl}
184             ? $request->uri
185             : $self->url(-absolute=>1));
186              
187             $self->{title} = 'Untitled Application';
188             $self->{subtitle} = '';
189             $self->{action} = $self->arg('action');
190             $self->{default_action} = 'does_not_exist';
191             $self->{debug} = $self->arg('debug') || 0;
192             $self->{error} = {};
193             $self->{redirect} = '';
194             $self->{noprint} = 0;
195              
196             $self->{error_email} = 'root';
197             $self->{dbi_driver} = '';
198             $self->{dbi_user} = '';
199             $self->{dbi_password} = '';
200             }
201              
202             =item * $val = $self->arg($key)
203              
204             Returns a CGI/mod_perl parameter for the key $key.
205              
206             =cut
207              
208             sub arg {
209             my $self = shift;
210             my $key = shift;
211              
212             if ($self->{mod_perl}) {
213             my %args = $self->{request}->args;
214             return $args{$key};
215             } else {
216             return param($key);
217             }
218             }
219              
220             =item * @keys = $self->args
221              
222             Returns a list of all of the mod_perl/cgi parameters.
223              
224             =cut
225              
226             sub args {
227             my $self = shift;
228              
229             if ($self->{mod_perl}) {
230             my %args = $self->{request}->args;
231             return keys %args;
232             } else {
233             return param();
234             }
235             }
236              
237             =item * $s = $hc->header
238              
239             Returns a string containing the preheader, an HTML title, and a
240             postheader. You probably do not want to override this unless you want
241             a different type of title.
242              
243             =cut
244              
245             sub header {
246             my $self = shift || die 'need $self';
247              
248             return join(
249             '',
250             $self->preheader(),
251             h1($self->{title}),
252             ($self->{subtitle} ? "$self->{subtitle}
\n" : ''),
253             $self->postheader(),
254             );
255             }
256              
257             =item * $s = $hc->work
258              
259             Runs a method corresponding to the $action parameter, or the default
260             action, and returns the content as the body of the document. If the
261             $action does not exist, then it puts up a page stating that. This
262             makes rapid prototyping very easy and quick.
263              
264             =cut
265              
266             sub work {
267             my $self = shift || die 'need $self';
268              
269             unless (defined $self->{action}) {
270             $self->{action} = $self->{default_action};
271             }
272              
273             my $action = $self->{action};
274             my $method = $self->can($action);
275             my $result = '';
276              
277             if (defined $method) {
278             no strict 'refs';
279             $result .= join(
280             '',
281             $self->prework(),
282             $method->($self),
283             $self->postwork(),
284             $self->errors,
285             );
286             use strict 'refs';
287             } else {
288             $result .= h1('Page Not Implemented');
289             $result .= p('The application encountered a request for a page that is not yet implemented or understood and was unable to complete your request.');
290             $result .= p('The error is automatically logged and an email report is being sent.');
291             }
292              
293             return $result;
294             }
295              
296             =item * $s = $hc->footer
297              
298             Returns a string containing the prefooter, and postfooter. This used
299             to have a standard footer as well, but I found it annoying.
300              
301             =cut
302              
303             sub footer {
304             my $self = shift || die 'need $self';
305             return join(
306             '',
307             $self->prefooter(),
308             $self->postfooter(),
309             );
310             }
311              
312             =item * $s = $hc->errors
313              
314             Returns a dictionary list detailing the contents of the error hash, if
315             any.
316              
317             =cut
318              
319             sub errors {
320             my $result = '';
321             my $self = shift || die 'need $self';
322              
323             if (%{$self->{error}}) {
324             $result .= join("\n",
325             h1('Errors:'),
326             '
',
327             map(dt($_) . dd($self->{error}{$_}),
328             sort keys %{$self->{error}}),
329             '',
330             );
331             }
332             return $result;
333             }
334              
335             ############################################################
336             # Application Specific Hooks:
337              
338             =item * $s = $hc->preheader
339              
340             Returns the contents of the preheader. Override to add something
341             before the title.
342              
343             =cut
344              
345             sub preheader {
346             return ''
347             }
348              
349             =item * $s = $hc->postheader
350              
351             Returns the contents of the postheader. Override to add something
352             after the title.
353              
354             =cut
355              
356             sub postheader {
357             return '';
358             }
359              
360             =item * $s = $hc->prework
361              
362             Returns the contents of the prework. Override to add something
363             before the body.
364              
365             =cut
366              
367             sub prework {
368             return '';
369             }
370              
371             =item * $s = $hc->postwork
372              
373             Returns the contents of the postwork. Override to add something
374             after the body.
375              
376             =cut
377              
378             sub postwork {
379             return '';
380             }
381              
382             =item * $s = $hc->prefooter
383              
384             Returns the contents of the prefooter. Override to add something
385             before the footer.
386              
387             =cut
388              
389             sub prefooter {
390             return '';
391             }
392              
393             =item * $s = $hc->postfooter
394              
395             Returns the contents of the postfooter. Override to add something
396             after the footer.
397              
398             =cut
399              
400             sub postfooter {
401             return '';
402             }
403              
404             ############################################################
405             # Utility/Accessor/Helper Methods
406              
407             =item * $s = $hc->reportError
408              
409             Sends an email to the addresses listed in error_email, detailing an
410             error with as much debugging content as possible. Used for fatal
411             conditions.
412              
413             =cut
414              
415             sub reportError {
416             my $self = shift;
417              
418             my $mailer = new Mail::Mailer;
419             $mailer->open({
420             'To' => $self->{error_email},
421             'Subject' => "Error in " . $self->{url},
422             });
423              
424             print $mailer join ("\n",
425             "Error:",
426             ($self->{mod_perl}
427             ? '$url = ' . $self->{url} . '?' . $self->{request}->args
428             : $self->{cgi}->self_url),
429             @_);
430              
431             $mailer->close;
432             }
433              
434             =item * $s = $hc->dbi
435              
436             Returns a DBI connection. Override _init and add values for
437             dbi_driver, dbi_user, and dbi_password to make this connection.
438              
439             =cut
440              
441             sub dbi {
442             my $self = shift;
443              
444             unless (defined $self->{dbi}) {
445             $self->{dbi} = DBI->connect($self->{dbi_driver},
446             $self->{dbi_user},
447             $self->{dbi_password});
448              
449             if ($self->{dbi}) {
450             $self->{dbi}->do('SET DateStyle = \'ISO\'') ||
451             print '

', $DBI::errstr, "

\n";
452             } else {
453             print '

', $DBI::errstr, "

\n";
454             }
455             }
456              
457             return $self->{dbi};
458             }
459              
460             =item * $s = $hc->sqlToTable
461              
462             Returns an HTML representation of a SQL statement in table form.
463              
464             =cut
465              
466             sub sqlToTable {
467             my $self = shift;
468             my $sql = shift;
469              
470             my $result = '';
471             my $dbi = $self->dbi();
472              
473             my $sth = $dbi->prepare($sql);
474             if ( !defined $sth ) {
475             die "Cannot prepare statement: $DBI::errstr\n";
476             }
477             $sth->execute();
478              
479             my $head = $sth->{NAME};
480              
481             $result .= "\n"; \n"; \n";
482             $result .= "
\n";
483             $result .= join(" ", @$head);
484             $result .= "
485              
486             my @row;
487             while (@row = $sth->fetchrow) {
488             $result .= "
\n";
489             $result .= join(" ", @row);
490             $result .= "
491             }
492             $result .= "
\n";
493              
494             $sth->finish;
495             return $result;
496             }
497              
498             =item * $s = $hc->sqlToArrays
499              
500             Returns an array representing a SQL query.
501              
502             =cut
503              
504             sub sqlToArrays {
505             my $self = shift;
506             my $sql = shift;
507             my $result = [];
508             my $dbi = $self->dbi();
509              
510             my $sth = $dbi->prepare($sql);
511             die "Cannot prepare statement: $DBI::errstr\n"
512             unless ( defined $sth );
513             $sth->execute();
514              
515             while (my @row = $sth->fetchrow) {
516             push @{$result}, [@row];
517             }
518              
519             $sth->finish;
520             return $result;
521             }
522              
523             =item * $s = $hc->sqlToHashes
524              
525             Returns a hash representing a SQL query.
526              
527             =cut
528              
529             sub sqlToHashes {
530             my $self = shift;
531             my $sql = shift;
532             my $result = [];
533             my $dbi = $self->dbi();
534              
535             $self->{debug_str} = $sql;
536              
537             my $sth = $dbi->prepare($sql);
538             die "Cannot prepare statement: $DBI::errstr\n"
539             unless ( defined $sth );
540             $sth->execute();
541              
542             my $head = $sth->{NAME};
543             my $size = scalar @{$head} - 1;
544              
545             while (my @row = $sth->fetchrow) {
546             my $data = {};
547              
548             map { $data->{$head->[$_]} = $row[$_] } 0 .. $size;
549              
550             push @{$result}, $data;
551             }
552              
553             $sth->finish;
554             return $result;
555             }
556              
557             =item * $s = $hc->query1
558              
559             Returns a single value from a SQL query. The query must return a
560             single column and row (ie SELECT name FROM users WHERE id=42).
561              
562             =cut
563              
564             sub query1 {
565             my $self = shift;
566             my $sql = shift || return -1;
567              
568             my $sth = $self->dbi->prepare($sql);
569             if ( !defined $sth ) {
570             die "Cannot prepare statement: $DBI::errstr\n";
571             }
572             $sth->execute;
573              
574             my @row = $sth->fetchrow();
575              
576             $sth->finish;
577              
578             return scalar(@row) == 1? $row[0] : @row;
579             }
580              
581             1;
582              
583             __END__