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 )