<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>WebNano - A minimalistic PSGI based web framework.</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rev="made" href="mailto:zby@dorotka-pc.(none)" />
</head>

<body style="background-color: white">


<!-- INDEX BEGIN -->
<div name="index">
<p><a name="__index__"></a></p>

<ul>

        <li><a href="#name">NAME</a></li>
        <li><a href="#version">VERSION</a></li>
        <li><a href="#synopsis">SYNOPSIS</a></li>
        <li><a href="#description">DESCRIPTION</a></li>
        <ul>

                <li><a href="#controller_object_live_in_the_request_scope__new_controller_per_request_">Controller object live in the request scope (new controller per request)</a></li>
                <li><a href="#things_that_you_can_do_with_webnano_even_though_it_does_not_actively_support_them">Things that you can do with WebNano even though it does not actively support them</a></li>
                <ul>

                        <li><a href="#streamming">Streamming</a></li>
                        <li><a href="#authentication">Authentication</a></li>
                        <li><a href="#authorization">Authorization</a></li>
                </ul>

        </ul>

        <li><a href="#attributes_and_methods">ATTRIBUTES and METHODS</a></li>
        <ul>

                <li><a href="#psgi_callback">psgi_callback</a></li>
                <li><a href="#controller_search_path">controller_search_path</a></li>
                <li><a href="#handle">handle</a></li>
                <li><a href="#renderer">renderer</a></li>
        </ul>

        <li><a href="#diagnostics">DIAGNOSTICS</a></li>
        <li><a href="#dependencies">DEPENDENCIES</a></li>
        <li><a href="#incompatibilities">INCOMPATIBILITIES</a></li>
        <li><a href="#bugs_and_limitations">BUGS AND LIMITATIONS</a></li>
        <li><a href="#author">AUTHOR</a></li>
        <li><a href="#copyright_and_license">COPYRIGHT AND LICENSE</a></li>
</ul>

<hr name="index" />
</div>
<!-- INDEX END -->

<p>
</p>
<hr />
<h1><a name="name">NAME</a></h1>
<p>WebNano - A minimalistic PSGI based web framework.</p>
<p>
</p>
<hr />
<h1><a name="version">VERSION</a></h1>
<p>version 0.001</p>
<p>
</p>
<hr />
<h1><a name="synopsis">SYNOPSIS</a></h1>
<p>A minimal WebNano application can be an
app.psgi file like this:</p>
<pre>
    {
        package MyApp;
        use base 'WebNano';
    }
    
    {
        package MyApp::Controller;
        use base 'WebNano::Controller';
        
        sub index_action {
            my $self = shift;
            return 'This is my home';
        }
    }
    
    my $app = MyApp-&gt;new();
    $app-&gt;psgi_callback;</pre>
<p>You can then run it with <code>plackup</code> (see <a href="http://search.cpan.org/dist/Plack/scripts/plackup">http://search.cpan.org/dist/Plack/scripts/plackup</a>).
A more practical approach is to split this into three different files.</p>
<p>
</p>
<hr />
<h1><a name="description">DESCRIPTION</a></h1>
<p>Every WebNano application has at least three parts - the application
class, at least one controller class and the
<em>app.psgi</em> file (or
something else that uses <a href="http://search.cpan.org/dist/Plack/lib/Plack/Runner.pm">http://search.cpan.org/dist/Plack/lib/Plack/Runner.pm</a>
run the app).</p>
<p>The application object is instantiated only once and is used to hold all the
other constand data objects - like connection to the database, a template
renderer object (if it is too heavy to be created per request) and generally
stuff that is too heavy to be rebuild with each request.  In contrast the
controller objects are recreated for each request a new.</p>
<p>The dispatching implemented by WebNano is simple mapping of HTTP request paths into method
calls as in the following examples:</p>
<pre>
    '/page' -&gt; 'MyApp::Controller-&gt;page_action()'
    '/Some/Very/long/path' -&gt; 'MyApp::Controller::Some::Very-&gt;long_action( 'path' )</pre>
<p>Additionally if the last part of the path is empty then <code>index</code> is added to it - so <code>/</code> is
mapped to <code>index_action</code> and <code>/SomeController/</code> is mapped to
<code>MyApp::SomeController-&gt;index_action</code>.</p>
<p>If someone does not like the <code>_action</code> postfixes then he can use the
<code>url_map</code> controller attribute which works like the <code>run_modes</code> attribute in
<code>CGI::Application</code> - that is provides a map for method dispatching:</p>
<pre>
    $self-&gt;url_map( { 'mapped url' =&gt; 'mapped_url' } );</pre>
<p>or a list of approved methods to be dispached by name:</p>
<pre>
    $self-&gt;url_map( [ 'safe_method' ] );</pre>
<p>More advanced dispatching is done by overriding the <code>local_dispatch</code> method in
the Controller class:</p>
<pre>
    around 'local_dispatch' =&gt; sub {
        my( $orig, $self, $path) = @_;
        my( $id, $method, @args ) = split qr{/}, $path;
        $method ||= 'view';
        if( $id &amp;&amp; $id =~ /^\d+$/ &amp;&amp; $self-&gt;is_record_method( $method ) ){
            my $rs = $self-&gt;app-&gt;schema-&gt;resultset( 'Dvd' );
            my $record = $rs-&gt;find( $id );
            if( ! $record ) {
                my $res = $self-&gt;req-&gt;new_response(404);
                $res-&gt;content_type('text/plain');
                $res-&gt;body( 'No record with id: ' . $id );
                return $res;
            }
            return $self-&gt;$method( $record, @args );
        }
        return $self-&gt;$orig( $path );
    };</pre>
<p>This one checks if the first part of the path is a number - if it is it uses
it to look for a Dvd object by primary key.  If it cannot find such a Dvd then
it returns a 404. If it finds that dvd it then redispatches by the next path
part and passes that dvd object as the first parameter to that method call.
Note the need to check if the called method is an allowed one.
If the first part of the url is not a number - then the request is dispatched in
the normal way.</p>
<p>The design goal numer one here is to provide basic functionality that should cover most 
of use cases and a easy way to override it and extend. In general it is easy
to write your own dispatcher that work for your limited use case - and here
you just need to do that, you can override the dispatching only for a
particular controller and you don't need to warry about the general cases.</p>
<p>The example in <em class="file">extensions/WebNano-Controller-DSL/</em> shows how to create a DSL
for dispatching (ala Dancer):</p>
<pre>
    get '/some_address' =&gt; sub { 'This is some_address in web_dispatch table' };</pre>
<p>
</p>
<h2><a name="controller_object_live_in_the_request_scope__new_controller_per_request_">Controller object live in the request scope (new controller per request)</a></h2>
<p>If you need to build a heavy
structure used in the controller you can always build it as the
application attribute and use it in the controller as it has access to
the application object, but since all the work of controllers is done
in the request scope (i.e. creating the request) - then it makes sense
that the whole object lives in that scope.  This is the same as
Tatsumaki handlers (and controllers in Rails, Django and probably
other frameworks) - but different from Catalyst.</p>
<p>
</p>
<h2><a name="things_that_you_can_do_with_webnano_even_though_it_does_not_actively_support_them">Things that you can do with WebNano even though it does not actively support them</a></h2>
<p>There is a tendency in other frameworks to add interfaces to any other CPAN
library. With WebNano I want to keep it small, both in code and in it's
interface, and avoid adding new WebNano interfaces to things that can be used
directly, but instead I try to make that direct usage as simple as possible.</p>
<p>In particular a WebNano script is a PSGI application and you can use all the Plack
tools with it.  
For example to use sessions you can add following line to your app.psgi file:</p>
<pre>
    enable 'session'</pre>
<p>Read
<a href="http://search.cpan.org/dist/Plack-Middleware-Session/lib/Plack/Middleware/Session.pm">http://search.cpan.org/dist/Plack-Middleware-Session/lib/Plack/Middleware/Session.pm</a>
about the additional options that you can enable here.  See also
<a href="http://search.cpan.org/dist/Plack/lib/Plack/Builder.pm">http://search.cpan.org/dist/Plack/lib/Plack/Builder.pm</a>
to read about the sweetened syntax you can use in your app.psgi file
and  <a href="http://search.cpan.org/search?query=Plack+Middleware&mode=all">http://search.cpan.org/search</a>
to find out what other Plack::Middleware packages are available.</p>
<p>The same goes for MVC. WebNano does not have any methods or attributes for
models, not because I don't structure my web application using the 'web MVC'
pattern - but rather because I don't see any universal attribute or method of
the possible models.  Users are free to add their own methods.  For example most
of my code uses <a href="http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class.pm">http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class.pm</a> 
- and I add these lines to my application:</p>
<pre>
    has schema =&gt; ( is =&gt; 'ro', isa =&gt; 'DBIx::Class::Schema', lazy_build =&gt; 1 );
    
    sub _build_schema {
       my $self = shift;
       my $config = $self-&gt;config-&gt;{schema};
       return DvdDatabase::DBSchema-&gt;connect( $config-&gt;{dbi_dsn},
    $config-&gt;{user}, $config-&gt;{pass}, $config-&gt;{dbi_params} );
    }</pre>
<p>then I use it with <code>$self-&gt;app-&gt;schema</code> in the controller objects.</p>
<p>As to Views - I've added some support for two templating engines for WebNano,
but this is only because I wanted to experiment with 'template inheritance'.  If
you don't want to use 'template inheritance' you can use Template::Tookit
directly in your controller actions or you can use directly any templating
engine in your controller actions - like 
<code>$self-&gt;app-&gt;my_templating-&gt;process('template_name' )</code> 
or even <code>$self-&gt;my_templating-&gt;process( ... )</code> as long as it
returns a string.</p>
<p>
</p>
<h3><a name="streamming">Streamming</a></h3>
<p>You can use the original <a href="http://search.cpan.org/dist/PSGI/PSGI.pod#Delayed_Reponse_and_Streaming_Body">http://search.cpan.org/dist/PSGI/PSGI.pod#Delayed_Reponse_and_Streaming_Body</a>
The streaming_action method in <em class="file">t/lib/MyApp/Controller.pm</em> can be used as an example.</p>
<p>
</p>
<h3><a name="authentication">Authentication</a></h3>
<p>Example code in the application class:</p>
<pre>
    around handle =&gt; sub {
        my $orig = shift;
        my $self = shift;
        my $env  = shift;
        if( $env-&gt;{'psgix.session'}{user_id} ){
            $env-&gt;{user} = $self-&gt;schema-&gt;resultset( 'User' )-&gt;find( $env-&gt;{'psgix.session'}{user_id} );
        }
        else{
            my $req = Plack::Request-&gt;new( $env );
            if( $req-&gt;param( 'username' ) &amp;&amp; $req-&gt;param( 'password' ) ){
                my $user = $self-&gt;schema-&gt;resultset( 'User' )-&gt;search( { username =&gt; $req-&gt;param( 'username' ) } )-&gt;first;
                if( $user-&gt;check_password( $req-&gt;param( 'password' ) ) ){
                    $env-&gt;{user} = $user;
                    $env-&gt;{'psgix.session'}{user_id} = $user-&gt;id;
                }
            }
        }
        $self-&gt;$orig( $env, @_ );
    };</pre>
<p>
</p>
<h3><a name="authorization">Authorization</a></h3>
<p>Example:</p>
<pre>
    around 'local_dispatch' =&gt; sub {
        my $orig = shift;
        my $self = shift;
        if( !$self-&gt;env-&gt;{user} ){
            return $self-&gt;render( template =&gt; 'login_required.tt' );
        }
        $self-&gt;$orig( @_ );
    };</pre>
<p><code>local_dispatch</code> is called before the controll is passed to child controllers,
so if you put that into the <code>MyApp::Controller::Admin</code> controller - then both
all local actions and actions in child controllers (for example
<code>MyApp::Controller::Admin::User</code>) would be guarded agains unauthorized usage.</p>
<p>
</p>
<hr />
<h1><a name="attributes_and_methods">ATTRIBUTES and METHODS</a></h1>
<p>
</p>
<h2><a name="psgi_callback">psgi_callback</a></h2>
<p>This is a method which returns a subroutine reference suitable for PSGI.
The returned subrourine ref is a closure over the application object.</p>
<p>
</p>
<h2><a name="controller_search_path">controller_search_path</a></h2>
<p>Experimental.</p>
<p>
</p>
<h2><a name="handle">handle</a></h2>
<p>Application method that acts as the PSGI callback - takes environment
as input and returns the response.</p>
<p>
</p>
<h2><a name="renderer">renderer</a></h2>
<p>Nearly every web application uses some templating engine - this is the
attribute to keep the templating engine object.  It is not mandatory that you
follow this rule.</p>
<p>
</p>
<hr />
<h1><a name="diagnostics">DIAGNOSTICS</a></h1>
<dl>
<dt><strong><a name="error_message_here_perhaps_with_s_placeholders" class="item"><code>Error message here, perhaps with %s placeholders</code></a></strong></dt>

<dd>
<p>[Description of error here]</p>
</dd>
<dt><strong><a name="another_error_message_here" class="item"><code>Another error message here</code></a></strong></dt>

<dd>
<p>[Description of error here]</p>
<p>[Et cetera, et cetera]</p>
</dd>
</dl>
<p>
</p>
<hr />
<h1><a name="dependencies">DEPENDENCIES</a></h1>
<p>See Makefile.PL</p>
<p>
</p>
<hr />
<h1><a name="incompatibilities">INCOMPATIBILITIES</a></h1>
<p>None reported.</p>
<p>
</p>
<hr />
<h1><a name="bugs_and_limitations">BUGS AND LIMITATIONS</a></h1>
<p>No bugs have been reported.</p>
<p>Please report any bugs or feature requests to
<code>bug-webnano@rt.cpan.org</code>, or through the web interface at
<a href="http://rt.cpan.org">http://rt.cpan.org</a>.</p>
<p>
</p>
<hr />
<h1><a name="author">AUTHOR</a></h1>
<p>Zbigniew Lukasiak &lt;<a href="mailto:zby@cpan.org">zby@cpan.org</a>&gt;</p>
<p>
</p>
<hr />
<h1><a name="copyright_and_license">COPYRIGHT AND LICENSE</a></h1>
<p>This software is copyright (c) 2010 by Zbigniew Lukasiak &lt;<a href="mailto:zby@cpan.org">zby@cpan.org</a>&gt;.</p>
<p>This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.</p>

</body>

</html>