ASP4

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


2012-02-07    1.079
  - Errors output to the stderr are now derived directly from $@ not from any
    parsed version of it.

2012-02-02    1.078
  - Fixed installation problem that came up in v1.075 
    (compilation root was missing leading forward slash on non-windows systems).

2012-02-01    1.077
  - Loath to add a mime-types-all-knowing dependency, we have a small list of
    common mime-types (html, css, js, etc).
  - Added mime for html and svg.

2012-02-01    1.076
  - Now, the 'content-type' header is set correctly for ASP4::UserAgent responses.
  - Works correctly under ASP4::PSGI (images, css, javascript all show up).

2012-02-01    1.075
  - Now, works on Windows!
  - eric.hayes++

2012-01-30    1.074
  - Explicit calls to $Session->save() are no longer necessary.

2012-01-23    1.073

META.yml  view on Meta::CPAN

--- #YAML:1.0
name:               ASP4
version:            1.087
abstract:           Fast, Simple, Scalable Web Development
author:
    - John Drago <jdrago_999@yahoo.com>
license:            artistic
distribution_type:  module
test_requires:
    Test::More:           0
    Test::Memory::Cycle:  0
    Time::HiRes:          0
    HTML::Form:           0
requires:
    common::sense:            0
    Data::Properties::YAML:   0
    Cwd:                      0
    Digest::MD5:              0

README.markdown  view on Meta::CPAN

      message => 'this is a test message'
    );

To send an HTML email do the following:

    use MIME::Base64;
    $Server->Mail(
      from                        => 'foo@bar.com',
      to                          => 'bar@foo.com',
      subject                     => 'Hello, world!',
      'content-type'              => 'text/html',
      'content-transfer-encoding' => 'base64',
      message => encode_base64(<<"HTML")
    <html>
    <body>
      <p>This is an html email.</p>
      <p>You can see that <b>this text is bold</b>.</p>
    </body>
    </html>
    HTML
    );

README.markdown  view on Meta::CPAN

      my $errors = $Session->{validation_errors} || { };
      $::err = sub {
        my $field = shift;
        my $error = $errors->{$field} or return;
        %><span class="field_error"><%= $Server->HTMLEncode( $error ) %></span><%
      };
    %>
    <form id="register_form" method="post" action="/handlers/myapp.register">
      <p>
        <label>Email:</label>
        <input type="text" name="email" value="<%= $Server->HTMLEncode( $Form->{email} ) %>" />
        <% $::err->("email"); %>
      </p>
      <p>
        <label>Password:</label>
        <input type="password" name="password" />
        <% $::err->("password"); %>
      </p>
      <p>
        <label>Confirm Password:</label>
        <input type="password" name="password2" />
        <% $::err->("password2"); %>
      </p>
      <p>
        <input type="submit" value="Register Now" />
      </p>
    </form>
    </asp:Content>

The form submits to the URL `/handlers/app.register` which means `handlers/app/register.pm`

File: `handlers/app/register.pm`

    package app::register;
    

README.markdown  view on Meta::CPAN

      foreach my $user ( @users ) {
    %>
            <option value="<%= $user->id %>"><%= $Server->HTMLEncode( $user->email ) %></option>
    <%
      }# end foreach()
    %>
          </select>
        </p>
        <p>
          <label>Subject:</label>
          <input type="text" name="subject" maxlength="100" />
        </p>
        <p>
          <label>Message:</label><br/>
          <textarea name="body"></textarea>
        </p>
        <p>
          <input type="submit" value="Send Message" />
        </p>
      </form>
    </div>
    </asp:Content>

The form submits to `/handlers/app.send` which maps to `handlers/app/send.pm`

File: `handlers/app/send.pm`

    package app::send;

inc/Module/Install/Metadata.pm  view on Meta::CPAN

	$ISCORE  = 1;
	@ISA     = qw{Module::Install::Base};
}

my @scalar_keys = qw{
	name
	module_name
	abstract
	author
	version
	distribution_type
	tests
	installdirs
};

my @tuple_keys = qw{
	configure_requires
	build_requires
	requires
	recommends
	bundles

inc/Module/Install/Metadata.pm  view on Meta::CPAN

	while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
		$self->feature( $name, @$mods );
	}
	return $self->{values}{features}
		? @{ $self->{values}{features} }
		: ();
}

sub no_index {
	my $self = shift;
	my $type = shift;
	push @{ $self->{values}{no_index}{$type} }, @_ if $type;
	return $self->{values}{no_index};
}

sub read {
	my $self = shift;
	$self->include_deps( 'YAML::Tiny', 0 );

	require YAML::Tiny;
	my $data = YAML::Tiny::LoadFile('META.yml');

lib/ASP4.pm  view on Meta::CPAN

    message => 'this is a test message'
  );

To send an HTML email do the following:

  use MIME::Base64;
  $Server->Mail(
    from                        => 'foo@bar.com',
    to                          => 'bar@foo.com',
    subject                     => 'Hello, world!',
    'content-type'              => 'text/html',
    'content-transfer-encoding' => 'base64',
    message => encode_base64(<<"HTML")
  <html>
  <body>
    <p>This is an html email.</p>
    <p>You can see that <b>this text is bold</b>.</p>
  </body>
  </html>
  HTML
  );

lib/ASP4.pm  view on Meta::CPAN

    my $errors = $Session->{validation_errors} || { };
    $::err = sub {
      my $field = shift;
      my $error = $errors->{$field} or return;
      %><span class="field_error"><%= $Server->HTMLEncode( $error ) %></span><%
    };
  %>
  <form id="register_form" method="post" action="/handlers/myapp.register">
    <p>
      <label>Email:</label>
      <input type="text" name="email" value="<%= $Server->HTMLEncode( $Form->{email} ) %>" />
      <% $::err->("email"); %>
    </p>
    <p>
      <label>Password:</label>
      <input type="password" name="password" />
      <% $::err->("password"); %>
    </p>
    <p>
      <label>Confirm Password:</label>
      <input type="password" name="password2" />
      <% $::err->("password2"); %>
    </p>
    <p>
      <input type="submit" value="Register Now" />
    </p>
  </form>
  </asp:Content>

The form submits to the URL C</handlers/app.register> which means C<handlers/app/register.pm>

File: C<handlers/app/register.pm>

  package app::register;
  

lib/ASP4.pm  view on Meta::CPAN

    foreach my $user ( @users ) {
  %>
          <option value="<%= $user->id %>"><%= $Server->HTMLEncode( $user->email ) %></option>
  <%
    }# end foreach()
  %>
        </select>
      </p>
      <p>
        <label>Subject:</label>
        <input type="text" name="subject" maxlength="100" />
      </p>
      <p>
        <label>Message:</label><br/>
        <textarea name="body"></textarea>
      </p>
      <p>
        <input type="submit" value="Send Message" />
      </p>
    </form>
  </div>
  </asp:Content>

The form submits to C</handlers/app.send> which maps to C<handlers/app/send.pm>

File: C<handlers/app/send.pm>

  package app::send;

lib/ASP4/ErrorHandler.pm  view on Meta::CPAN



sub send_error
{
  my ($s, $error) = @_;
  
  $Server->Mail(
    To                          => $Config->errors->mail_errors_to,
    From                        => $Config->errors->mail_errors_from,
    Subject                     => "ASP4: Error in @{[ $ENV{HTTP_HOST} ]}@{[ $ENV{REQUEST_URI} ]}",
    'content-type'              => 'text/html',
    'content-transfer-encoding' => 'base64',
    Message                     => encode_base64( $s->error_html($error) ),
    smtp                        => $Config->errors->smtp_server,
  );
}# end send_error()


sub error_html
{
  my ($s, $error) = @_;
  
  my $msg = <<"ERROR";
<!DOCTYPE html>
<html>
<head><title>500 Server Error</title>
<meta charset="utf-8" />
<style type="text/css">
HTML,BODY {
  background-color: #FFFFFF;
}
HTML,BODY,P,DIV {
  font-family: Arial, Helvetica, Sans-Serif;
}
HTML,BODY,P,PRE,DIV {
  font-size: 12px;
}
H1 {

lib/ASP4/FileUpload.pm  view on Meta::CPAN

=head2 FileSize

The size of the uploaded file in bytes.

=head2 FileHandle

Returns a filehandle (open for reading) pointing to the uploaded file.

=head2 ContentType

The C<content-type> header supplied by the browser for the uploaded file.

=head2 FileContents

The contents of the uploaded file.

=head1 PUBLIC METHODS

=head2 SaveAs( $path )

Writes the contents of the uploaded file to C<$path>.  Will throw an exception if

lib/ASP4/HTTPContext.pm  view on Meta::CPAN

sub session   { shift->{session} }
sub config    { shift->{config} }
sub stash     { shift->{stash} }

# More advanced:
sub is_subrequest { shift->{is_subrequest} }
sub cgi         { shift->{cgi} }
sub r           { shift->{r} }
sub handler     { shift->{handler} }
sub headers_out { shift->{headers_out} }
sub content_type  { my $s = shift; $s->r->content_type( @_ ) }
sub status        { my $s = shift; $s->r->status( @_ ) }
sub did_send_headers  { shift->{did_send_headers} }
sub did_end {
  my $s = shift;
  @_ ? $s->{did_end} = shift : $s->{did_end};
}

sub rprint {
  my ($s,$str) = @_;
  $s->buffer->add( $str );

lib/ASP4/Mock/RequestRec.pm  view on Meta::CPAN

use ASP4::ConfigLoader;
use Scalar::Util 'weaken';


sub new
{
  my ($class, %args) = @_;
  
  my $s = bless {
    status        => 200,
    content_type  => 'text/plain',
    buffer        => '',
    document_root => ASP4::ConfigLoader->load()->web->www_root,
    headers_in    => { },
    headers_out   => { },
    uri           => $args{uri} || $ENV{REQUEST_URI},
    args          => $args{args} || $ENV{QUERY_STRING},
    pnotes        => { },
    method        => $args{method},
    pool          => ASP4::Mock::Pool->new(),
    connection    => ASP4::Mock::Connection->new(),

lib/ASP4/Mock/RequestRec.pm  view on Meta::CPAN

sub connection      { shift->{connection} }
sub headers_out     { shift->{headers_out} }
sub headers_in      { shift->{headers_in} }
sub err_headers_out { shift->{err_headers_out} }

sub buffer          { shift->{buffer} } # Not documented:


# Public methods:
sub print { my ($s,$str) = @_; $s->{buffer} .= $str; }
sub content_type
{
  my ($s, $type) = @_;
  return $s->headers_out->{'content-type'} unless $type;
  $s->headers_out->{'content-type'} = $type;
}# end content_type()

sub rflush { }

1;# return true:

=pod

=head1 NAME

ASP4::Mock::RequestRec - Mimic an Apache2::RequestRec object

lib/ASP4/Mock/RequestRec.pm  view on Meta::CPAN

Returns a hashref representing the outgoing headers.

=head2 err_headers_out( )

Returns a hashref representing the outgoing headers.

=head2 status( [$new_status] )

Sets or gets the status code for the response.  200 for "OK", 301 for "Moved" - 404 for "not found" etc.

=head2 content_type( [$new_content_type] )

Sets or gets the mime-header for the outgoing response.  Default is C<text/plain>.

=head1 PUBLIC METHODS

=head2 print( $str )

Adds C<$str> to the outgoing response buffer.

=head2 rflush( )

lib/ASP4/ModPerl.pm  view on Meta::CPAN

{
  my ($class, $r) = @_;
  
  $ENV{DOCUMENT_ROOT}   = $r->document_root;
  $ENV{REMOTE_ADDR}     = $r->connection->get_remote_host();
  $ENV{HTTP_HOST}       = $r->hostname;
  
  my $context = ASP4::HTTPContext->new();
  $r->pool->cleanup_register(sub { $context->DESTROY });
  
  if( ($r->headers_in->{'content-type'}||'') =~ m/multipart\/form\-data/ )
  {
    $context->{r} = $r;
    if( $@ )
    {
      warn $@;
      $r->status( 500 );
      return $r->status;
    }# end if()
    
    my $handler_class = eval {

lib/ASP4/Request.pm  view on Meta::CPAN

is the same as:

  $ENV{HTTP_HOST}

=head2 FileUpload( $fieldname )

Returns a L<ASP4::FileUpload> object that corresponds to the fieldname specified.

So...if your form has this:

  <input type="file" name="my_uploaded_file" />

Then you would get to it like this:

  my $upload = $Request->FileUpload('my_uploaded-file');

=head2 Header( $name )

Returns the value of an incoming http request header by the given name.

=head1 BUGS

lib/ASP4/Response.pm  view on Meta::CPAN

use HTTP::Date qw( time2str );
use ASP4::HTTPContext;
use ASP4::Mock::RequestRec;


sub new
{
  my $s = bless {
    _status           => 200,
    _expires          => 0,
    _content_type     => 'text/html',
    _expires_absolute => time2str( time() ),
  }, shift;
  $s->Status( $s->Status );
  $s->Expires( $s->Expires );
  $s->ContentType( $s->ContentType );
  
  return $s;
}# end new()

sub context { ASP4::HTTPContext->current }


sub ContentType
{
  my $s = shift;
  
  if( @_ )
  {
    my $type = shift;
    $s->{_content_type} = $type;
    $s->context->r->content_type( $type );
    $s->SetHeader( 'content-type' => $type );
  }
  else
  {
    return $s->{_content_type};
  }# end if()
}# end ContentType()


sub Expires
{
  my $s = shift;
  if( my $value = shift )
  {
    my $time;
    if( my ($num,$type) = $value =~ m/^(\-?\d+)([MHD])$/ )
    {
      my $expires;
      if( $type eq 'M' ) {
        # Minutes:
        $expires = time() + ( $num * 60 );
      }
      elsif( $type eq 'H' ) {
        # Hours:
        $expires = time() + ( $num * 60 * 60 );
      }
      elsif( $type eq 'D' ) {
        # Days:
        $expires = time() + ( $num * 60 * 60 * 24 );
      }# end if()
      $time = $expires;
    }
    else
    {
      $time = $value;
    }# end if()
    

lib/ASP4/Response.pm  view on Meta::CPAN

sub End
{
  my $s = shift;
  
  if( $s->Status =~ m{^2} )
  {
    $s->Flush;
  }
  else
  {
    delete $s->context->headers_out->{'content-type'};
  }# end if()
  
  # Would be nice to somehow stop all execution:
  $s->context->did_end( 1 );
}# end End()


sub Flush
{
  my $s = shift;

lib/ASP4/Response.pm  view on Meta::CPAN

  $args{path}   ||= '/';
  my @parts = ( );
  push @parts, $s->context->server->URLEncode($args{name}) . '=' . $s->context->server->URLEncode($args{value});
  unless( $args{domain} eq '*' )
  {
    push @parts, 'domain=' . $s->context->server->URLEncode($args{domain});
  }# end unless()
  push @parts, 'path=' . $args{path};
  if( $args{expires} )
  {
    if( my ($num,$type) = $args{expires} =~ m/^(\-?\d+)([MHD])$/ )
    {
      my $expires;
      if( $type eq 'M' ) {
        # Minutes:
        $expires = time() + ( $num * 60 );
      }
      elsif( $type eq 'H' ) {
        # Hours:
        $expires = time() + ( $num * 60 * 60 );
      }
      elsif( $type eq 'D' ) {
        # Days:
        $expires = time() + ( $num * 60 * 60 * 24 );
      }# end if()
      push @parts, 'expires=' . time2str( $expires );
    }
    else
    {
      push @parts, 'expires=' . time2str( $args{expires} );
    }# end if()
  }# end if()

lib/ASP4/Response.pm  view on Meta::CPAN

  
  # Read-only:
  my $expires_on = $Response->ExpiresAbsolute;

=head1 DESCRIPTION

The C<$Response> object offers a unified interface to send content back to the client.

=head1 PROPERTIES

=head2 ContentType( [$type] )

Sets or gets the C<content-type> header for the response.  Examples are C<text/html>, C<image/gif>, C<text/csv>, etc.

=head2 Status( [$status] )

Sets or gets the C<Status> header for the response.  See L<http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html> for details.

B<NOTE:> Only the numeric part is necessary - eg: 200, 301, 404, etc.

=head2 Headers()

Returns the L<HTTP::Headers> object that will be used for the outgoing response.

lib/ASP4/Server.pm  view on Meta::CPAN

  
  # Email someone:
  $Server->Mail(
    To      => 'jim@bob.com',
    From    => 'Joe Jangles <joe@jangles.net>',
    Subject => 'Test Email',
    Message => "Hello There!",
  );
  
  # Avoid XSS:
  <input type="text" name="foo" value="<%= $Server->HTMLEncode( $Form->{foo} ) %>" />
  
  # Proper URLs:
  <a href="foo.asp?bar=<%= $Server->URLEncode($Form->{bar}) %>">Click</a>

=head1 DESCRIPTION

The C<$Server> object provides some utility methods that don't really fit anywhere
else, but are still important.

=head1 PUBLIC METHODS

lib/ASP4/SimpleCGI.pm  view on Meta::CPAN

      }
      else
      {
        $params{$k} = $v;
      }# end if()
    }# end foreach()
  }# end if()
  
  if( $args{body} )
  {
    my $body = HTTP::Body->new( $args{content_type}, $args{content_length} );
    $body->add( $args{body} );
    
    # Parse form values:
    my $form_info = $body->param || { };
    if( keys(%$form_info) )
    {
      foreach( keys(%$form_info) )
      {
        $params{$_} = $form_info->{$_};
      }# end foreach()

lib/ASP4/SimpleCGI.pm  view on Meta::CPAN


=head1 NAME

ASP4::SimpleCGI - Basic CGI functionality

=head1 SYNOPSIS

  use ASP4::SimpleCGI;
  
  my $cgi = ASP4::SimpleCGI->new(
    content_type    => 'multipart/form-data',
    content_length  => 1200,
    querystring     => 'mode=create&uploadID=234234',
    body            => ...
  );
  
  my $val = $cgi->param('mode');
  foreach my $key ( $cgi->param )
  {
    print $key . ' --> ' . $cgi->param( $key ) . "\n";
  }# end foreach()

lib/ASP4/SimpleCGI.pm  view on Meta::CPAN

in the API enironment.

C<ASP4::SimpleCGI> uses L<HTTP::Body> under the hood.

=head1 PUBLIC METHODS

=head2 new( %args )

Returns a new C<ASP4::SimpleCGI> object.

C<%args> can contain C<content_type>, C<content_length>, C<querystring> and C<body>.

=head2 param( [$key] )

If C<$key> is given, returns the value of the form or querystring parameter by that name.

If C<$key> is not given, returns a list of all parameter names.

=head2 escape( $str )

Returns a URL-encoded version of C<$str>.

lib/ASP4/StaticHandler.pm  view on Meta::CPAN

    $Response->Status( 404 );
    $Response->End;
    return 404;
  }# end unless()
  open my $ifh, '<', $file
    or die "Cannot open '$file' for reading: $!";
  local $/;
  $Response->SetHeader('content-length' => (stat($file))[7] );
  
  my ($ext) = $file =~ m{\.([^\.]+)$};
  my %types = (
    swf   => 'application/x-shockwave-flash',
    xml   => 'text/xml',
    jpg   => 'image/jpeg',
    jpeg  => 'image/jpeg',
    png   => 'image/png',
    bmp   => 'image/bmp',
    gif   => 'image/gif',
    json  => 'application/x-json',
    css   => 'text/css',
    pdf   => 'application/x-pdf',
    js    => 'text/javascript',
    svg   => 'image/svg+xml',
    html  => 'text/html',
  );
  my $type = $types{lc($ext)} || 'application/octet-stream';
  $Response->ContentType( $type );
  
  my ($filename) = $file =~ m{([^/]+)$};
  my $disp = lc($type) eq 'pdf' ? 'attachment' : 'inline';
  $Response->SetHeader('content-disposition' => qq($disp; filename="$filename"; yay=yay;));
  $Response->Write( scalar(<$ifh>) );
  close($ifh);
}# end run()

1;# return true:

lib/ASP4/UserAgent.pm  view on Meta::CPAN

  chdir( $s->{cwd} );
  
  my $temp_referrer = $ENV{HTTP_REFERER};
  my $req = $form->click;
  my $referer = $ENV{HTTP_REFERER};
  %ENV = (
    %{ $s->{env} },
    HTTP_REFERER    => $referer || '',
    DOCUMENT_ROOT   => $s->config->web->www_root,
    REQUEST_METHOD  => uc( $req->method ),
    CONTENT_TYPE    => $form->enctype ? $form->enctype : 'application/x-www-form-urlencoded',
    HTTP_COOKIE     => $s->http_cookie,
    REQUEST_URI     => $form->action,
  );
  my $cgi = $s->_setup_cgi( $req );
  my ($uri_no_args, $querystring) = split /\?/, $req->uri;
  my $r = ASP4::Mock::RequestRec->new( uri => $uri_no_args, args => $querystring );
  my $current_is_subrequest = $ASP4::HTTPContext::_instance ? $ASP4::HTTPContext::_instance->{is_subrequest} ? 1 : 0 : 0;
  $s->{context} = ASP4::HTTPContext->new( is_subrequest => $current_is_subrequest ? 1 : 0 );
  return do {
    local $ASP4::HTTPContext::_instance = $s->context;

lib/ASP4/UserAgent.pm  view on Meta::CPAN

  
  # Cookies:
  $req->header( 'Cookie' => $ENV{HTTP_COOKIE} = $s->http_cookie );
  
  if( $ENV{REQUEST_METHOD} =~ m/^post$/i )
  { 
    # Set up the basic params:
    return ASP4::SimpleCGI->new(
      querystring     => $ENV{QUERY_STRING},
      body            => $req->content,
      content_type    => $req->headers->{'content-type'},
      content_length  => $req->headers->{'content-length'},
    );
  }
  else
  {
    # Simple 'GET' request:
    return ASP4::SimpleCGI->new( querystring => $ENV{QUERY_STRING} );
  }# end if()
}# end _setup_cgi()

t/010-basic/090-everything.t  view on Meta::CPAN

use ASP4::API;

my $api = ASP4::API->new;

ok( my $res = $api->ua->get('/everything/step01.asp'), "Got res");

ok(
  $res = $api->ua->get('/handlers/dev.headers'), "Got headers res again"
);
is(
  $res->header('content-type') => 'text/x-test'
);
is(
  $res->header('content-length') => 3000
);
is(
  $res->content => "X"x3000
);

# static:
{

t/htdocs/index.asp  view on Meta::CPAN

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>ASP4 Installed</title>
  <style type="text/css">
  HTML, BODY {
    margin: 0px;
    padding: 0px;
    border: 0px;
    font-size: 14px;
    font-family: Verdana, Arial, Sans-Serif;
    background-color: #333333;
    color: #000000;
    width: 100%;
    height: 100%;

t/htdocs/useragent/simple-form.asp  view on Meta::CPAN

<html>
<body>
<form name="form1" action="/useragent/simple-form.asp" method="post">
  Color: <input type="text" name="color" value="<%= $Server->HTMLEncode( $Form->{color} ) %>" ><br>
  Pet's Name: <input type="text" name="pet_name" value="<%= $Server->HTMLEncode( $Form->{pet_name} ) %>"><br>
  <br>
  <input type="submit" value="Submit" >
</form>
</body>
</html>

t/htdocs/useragent/upload-form.asp  view on Meta::CPAN

%>
<form name="form1">
<%= $file->FileName %> -- <%= $file->FileExtension %> -- <%= $file->FileSize %><br/>
<textarea name="file_contents"><%= $file->FileContents %></textarea>
</form>
<%
  }
  else
  {
%>
<form name="form1" action="/useragent/upload-form.asp" method="post" enctype="multipart/form-data">
  <input type="file" name="filename">
  <br>
  <input type="submit" value="Submit" >
</form>
<%
  }# end if()
%>
</body>
</html>



( run in 0.805 second using v1.01-cache-2.11-cpan-e1769b4cff6 )