Apache-JAF
view release on metacpan or search on metacpan
lib/Apache/JAF.pm view on Meta::CPAN
package Apache::JAF;
use 5.6.0;
use strict;
use Template ();
use Template::Provider;
use Template::Parser;
use Template::Document;
our @ISA = qw( Template::Provider );
use Apache ();
use Apache::Util ();
use Apache::JAF::Util ();
use JAF::Util ();
use Apache::Request ();
use Apache::Constants qw( :common REDIRECT );
use Apache::File ();
use Data::Dumper qw( Dumper );
use File::Find ();
our $WIN32 = $^O =~ /win32/i;
our $RX = $WIN32 ? qr/\:(?!(?:\/|\\))/ : qr/\:/;
our $VERSION = do { my @r = (q$Revision: 0.21 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
# Constructor
################################################################################
sub new {
my ($ref, $r) = @_;
my $self = {};
#
# use as template provider
#
if (ref($r) eq 'HASH') {
$self = $ref->SUPER::new($r);
bless ($self, $ref);
return $self;
}
#
# use as framework
#
$r = Apache::Request->instance($r);
bless ($self, $ref);
# r - request (filter-aware)
$self->{filter} = lc $r->dir_config('Filter') eq 'on';
$self->{r} = $self->{filter} ? $r->filter_register() : $r;
my $prefix = $r->dir_config('Apache_JAF_Prefix') || 0;
# prefix - path|number of subdirs that must be removed from uri
$prefix = ($prefix =~ /^\/(.*)$/) ? scalar(my @tmp = split '/', $1) : int($prefix);
# uri - reference to array that contains uri plitted by '/'
my @uri = split '/', $self->{r}->uri;
shift @uri if $prefix;
splice @uri, 0, ($prefix || 1);
if (@uri) {
$uri[-1] =~ s/\.html?$//i;
my $i = 0;
while ($i < @uri) {
splice @uri, $i, 2 if $uri[$i] =~ /^\w+:$/ && !$uri[$i+1];
$i++;
}
}
$self->{uri} = \@uri;
# res - result hash, that passed to the template
$self->{res} = {};
# for complex-name-handlers change '_' in handler name to '/' to provide
# real document tree in Templates folder
$self->{expand_path} = 1;
# Level of warnings that will be written to the server's error_log
# every next level includes all options from previous
# 0: critical errors only
# 1: request processing line
# 2: client request
# 3: response headers
# 4: template variables
# 9: loading additional handlers
# 10: processed template
$self->{debug_level} = $self->{r}->dir_config('Apache_JAF_Debug') || 0;
# Default response status and content-type
$self->{status} = NOT_FOUND;
$self->{type} = 'text/html';
# Default template and includes extensions
$self->{template_ext} = '.html';
$self->{include_ext} = '.inc';
$self->{default_include} = 'default';
# pre- and post-process templates (without extensions)
lib/Apache/JAF.pm view on Meta::CPAN
################################################################################
sub handler ($$) {
my ($self, $r) = @_;
my $time;
eval "use Time::HiRes ()";
$time = Time::HiRes::time() unless $@;
if (-f $r->document_root() . $r->uri() && -r _) {
$r->filename($r->document_root() . $r->uri());
$r->warn('Static file request: ' . $r->filename());
return DECLINED;
}
$self = $self->new($r) unless ref($self);
unless ($self) {
$self->warn(0, "Can't create handler object!");
return SERVER_ERROR;
}
my $result;
$self->{status} = $self->site_handler();
$result = $self->process_template() if $self->{status} == OK && $self->{type} =~ /^text/ && !$self->{r}->header_only;
if ($self->{status} == OK) {
$self->{r}->send_http_header($self->{type});
return $self->{status} if $self->{r}->header_only;
if ($self->{type} =~ /^text/) {
#
# Apache::Filter->print() must(?) be patched for printing referenced scalars
#
$self->{r}->print($self->{filter} ? $$result : $result);
} else {
#
# if handler set $self->{type} other than text/(html|plain)
# then data must be send to the client by on_send_..._data method
#
my $method = "on_send_${\($self->{handler})}_data";
$self->$method(@{$self->{uri}}) if $self->can($method);
}
}
$self->warn(3, 'Response headers: ' . Dumper {($self->{status} == OK) ? $self->{r}->headers_out() : $self->{r}->err_headers_out()});
$self->warn(1, sprintf 'Request processed in %0.3f sec', Time::HiRes::time() - $time) if $time;
my $status = $self->{status};
undef $result;
undef $self;
return $status
}
# Global Apache::JAF handler. If you want some stuff before (and|or) after
# running handler you must override it like that:
#
# sub site_handler {
# my $self = shift;
#
# [before stuff goes here]
#
# $self->{status} = $self->SUPER::site_handler(@_);
#
# [after stuff goes here]
#
# return $self->{status}
# }
################################################################################
sub site_handler {
my ($self) = @_;
my ($method, $last_modified, $cache, $mtime);
foreach (($method, $last_modified, $cache) = map { $_ . $self->{handler} } qw(do_ last_modified_ cache_)) {
$_ =~ tr{/}{_} if $self->{expand_path};
}
$self->warn(1, "Handler method: $method");
$mtime = $self->last_modified(@{$self->{uri}});
$mtime = $self->$last_modified(@{$self->{uri}}) if $self->can($last_modified);
if ($mtime) {
$self->{r}->update_mtime($mtime);
$self->{r}->set_last_modified;
$self->{status} = $self->{r}->meets_conditions;
return $self->{status} unless $self->{status} == OK;
}
if ($self->can($method)) {
#
# process template with handler
#
$self->warn(1, "Can do $method: Y");
my $cstat = $self->cache(@{$self->{uri}});
$cstat = $self->$cache(@{$self->{uri}}) if $self->can($cache);
if ($cstat) {
$self->{status} = $cstat;
} else {
$self->{status} = $self->$method(@{$self->{uri}})
}
$self->{handler} =~ tr{_}{/} if $self->{expand_path} && $self->{type} =~ /^text/;
$self->warn(1, 'Content-type: ' . $self->{type});
} else {
#
# process template without handler (defaults variables only, header and footer)
#
$self->warn(1, "Can do $method: N");
$self->{status} = OK unless $self->{status} == SERVER_ERROR;
}
return $self->{status};
}
### Additional utility methods for getting params
sub param {
my ($self, $p) = @_;
my @params = map { $_ = JAF::Util::trim($_); length > 0 ? $_ : undef} ($self->{r}->param($p));
return $params[0];
}
sub upload_fh {
lib/Apache/JAF.pm view on Meta::CPAN
sub default_messages {
my ($self, $modeller) = @_;
%{$self->{cookies}} = Apache::Cookie->fetch() unless $self->{cookies};
if ($self->{status} == REDIRECT) {
my $messages = $modeller->messages();
if ($messages) {
Apache::Cookie->new($self->{r},
-name => 'messages',
-path => '/',
-value => Data::Dumper::Dumper $messages)->bake();
}
} elsif ($self->{status} == OK && $self->{type} =~ /^text/ && !$self->{r}->header_only) {
my $VAR1;
if (exists $self->{cookies}{messages} && eval $self->{cookies}{messages}->value) {
$self->{res}{messages} = $VAR1;
Apache::Cookie->new($self->{r},
-name => $self->{res}{messages} ? 'messages' : 'error',
-path => '/',
-value => '')->bake();
} else {
$self->{res}{messages} = $modeller->messages();
}
}
}
=head1 NAME
Apache::JAF -- mod_perl and Template-Toolkit web applications framework
=head1 SYNOPSIS
=over 4
=item controller -- a mod_perl module that drives your application
package Apache::JAF::MyJAF;
use strict;
use JAF::MyJAF; # optional
# loading mini-handlers & templates during compilation time
use Apache::JAF (
handlers => '/examples/site/modules/Apache/JAF/MyJAF/pages/', # 'auto' if you want to use suggested file structure
templates => '/examples/site/templates/' # the same comment
);
our @ISA = qw(Apache::JAF);
# determine handler to call
sub setup_handler {
my ($self) = @_;
# the page handler for each URI of sample site is 'do_index'
# you should swap left and right ||-parts for real application
my $handler = 'index' || shift @{$self->{uri}};
return $handler;
}
sub site_handler {
my ($self) = @_;
# common stuff before handler is called
$self->{m} = JAF::MyJAF->new(); # create modeller -- if needed
$self->SUPER::site_handler();
# common stuff after handler is called
return $self->{status}
}
1;
=item page handler -- controller's method that makes one (or more) pages
sub do_index {
my ($self) = @_;
# page handler must fill $self->{res} hash that process with template
$self->{res}{test} = __PACKAGE__ . 'test';
# and return Apache constant according it's logic
return OK;
}
=item modeller -- a module that encapsulates application business-logic
package JAF::MyJAF;
use strict;
use DBI;
use base qw( JAF );
sub new {
my ($class, $self) = @_;
$self->{dbh} = DBI->connect(...);
return bless $self, $class;
}
1;
=item Apache configuration (F<httpd.conf>)
DocumentRoot /examples/site/data
<Location />
<Perl>
use lib qw(/examples/site/modules);
use Apache::JAF::MyJAF;
</Perl>
SetHandler perl-script
PerlHandler Apache::JAF::MyJAF
PerlSetVar Apache_JAF_Templates /examples/site/templates
# optional or can be specified in Apache::JAF descendant (default value is used in example)
PerlSetVar Apache_JAF_Modules /examples/site/modules/Apache/JAF/MyJAF/pages
# optional or can be specified in Apache::JAF descendant (default value is used in example)
PerlSetVar Apache_JAF_Compiled /tmp
</Location>
=back
=head1 DESCRIPTION
=head2 Introduction
Apache::JAF is designed for creation web applications based on MVC (Model-View-Controller)
concept.
=over 4
=item *
I<Modeller> is JAF descendant
lib/Apache/JAF.pm view on Meta::CPAN
=back
=head2 Request processing pipeline
The C<Apache::JAF::handler> intercepts every request for specified location, and
process it's own way:
=over 4
=item 1
If requested file exists then nothing happens. The handle declines request with C<DECLINE>.
=item 2
Otherwise the instance of Apache::JAF's descendant is created and C<setup_handler> method is called.
You B<must override> this method and return determined handler's name. Usually it's the first part of
URI or just C<index>. Also handlers from C<Apache_JAF_Modules> folder is loaded into package's
namespace if C<$self-E<gt>{debug_level}> E<gt> 0 or handlers were not loaded during module
compilation.
=item 3
Then goes C<site_handler> calling. If you have common tasks for each handler you can
override it. C<site_handler> calls your own handler. It's name is returned by C<setup_handler>.
Usually this "mini-handler" is I<very> simple. It have to be implemented as package method with
C<do_I<E<lt>handler nameE<gt>>> name. You have to fill C<$self-E<gt>{res}> hash with
result and return Apache constant according to handler's logic (C<OK>, C<NOT_FOUND>,
C<FORBIDDEN> and so on). The sample is shown in L<"SYNOPSIS">.
=item 4
If the previous step fulfills correctly, and C<$self-E<gt>{type}> property is C<text/*> then
result of processing template returns to client. If type of result is not
like text, one more method is needed to implement: C<on_send_I<E<lt>handeler nameE<gt>>_data>.
It must return binary data to client. This way you may create handlers for
dynamic generation of images, M$ Excel workbooks and any other type of data.
=back
=head2 Apache::JAF methods
=over 4
=item setup_handler
This method you must override in your Apache::JAF descendant. You must return handler's
name (that will be called as I<do_E<lt>handler nameE<gt>> method later) from it depending
on URI requested by user. You may set site-wide properties such as I<debug_level>, I<header>
or I<footer>, templates and includes extensions and so on. If handler name depends on
application logic implemented in modeller then you have to create modeller in this method
and store it in I<m> property for later use.
The primary I<setup_handler> is shown in L<"SYNOPSIS">.
=item site_handler
You can override this method to provide common tasks for each of your page-handlers. For
example you may create instance of modeller class, provide some custom
authorization/authentication or sessions handling and so on. You must call
C<$selfE<gt>SUPER::site_handler> and return C<$self-E<gt>{status}> from it.
=back
=head2 Apache::JAF properties
=over 4
=item r
Current C<Apache::Request> object.
=item filter
Using C<Apache::Filter> flag.
=item uri
Reference to the array of current URI (splitted by slash). Usually you need to modify
it in L<"setup_handler"> method to determine page's handler name. Remained array will be passed
to the page-handler method as a list of parameters.
=item res
Hash reference that holds page-handler results.
=item expand_path
Boolean flag for complex-name-handlers changes '_' to '/' in handler's name.
It provides real-like document tree in the templates folder.
=item debug_level
Look at B<Apache_JAF_Debug> in L<"CONFIGURATION"> section.
=item status
Default handler status is C<NOT_FOUND>.
=item type
Default content-type is C<text/html>. You can call C<$self-E<gt>download_type()>
for set unexisting MIME-type to force browser download content instead of viewing it.
=item template_ext, include_ext
Default template extension is C<.html>.
Default include template extension is C<.inc>.
=item default_include
Site-wide include template. Default value is... C<default>.
=item header, footer
Site-wide pre- and post-include templates. Defalut values are C<header> and C<footer>.
I<Note:>You must undef this properies if you want create page-template without it. For
example for page in pop-up window (C<disable_header>, C<disable_footer>, and
C<disable_header_footer> methods).
=item templates
( run in 0.737 second using v1.01-cache-2.11-cpan-5735350b133 )