=head1 NAME

Catalyst::Manual::Cookbook - Cooking with Catalyst

=head1 DESCRIPTION

Yummy code like your mum used to bake!

=head1 RECIPES

=head2 Force debug screen

You can force Catalyst to display the debug screen at the end of the request by
placing a die() call in the _end action.

    __PACKAGE__->action(
        '!end' => sub {
            my ( $self, $c ) = @_;
            die "testing";
        }
    );

If you're tired of removing and adding this all the time, you
can easily add a condition. for example:

  die "Testing" if $c->param->{dump_info};

=head2 Disable statistics

Just add this line to your application class if you don't want those nifty
statistics in your debug messages.

    sub Catalyst::Log::info { }

=head2 Scaffolding

Scaffolding is very simple with Catalyst.
Just use Catalyst::Model::CDBI::CRUD as baseclass.

    # lib/MyApp/Model/CDBI.pm
    package MyApp::Model::CDBI;

    use strict;
    use base 'Catalyst::Model::CDBI::CRUD';

    __PACKAGE__->config(
        dsn           => 'dbi:SQLite:/tmp/myapp.db',
        relationships => 1
    );

    1;

    # lib/MyApp.pm
    package MyApp;

    use Catalyst 'FormValidator';

    __PACKAGE__->config(
        name => 'My Application',
        root => '/home/joeuser/myapp/root'
    );

    __PACKAGE__->action(
        'table' => sub {
            my ( $self, $c ) = @_;
            $c->form( optional => [ MyApp::Model::CDBI::Table->columns ] );
            $c->forward('MyApp::Model::CDBI::Table');
        }
    );

    1;

Modify the $c->form() parameters to match your needs, and don't forget to copy
the templates. ;)

=head2 Serving static files and CSS as text/css

If you want to serve static content (like images, txt or CSS) via Catalyst,
then all you need is the plugin Catalyst::Plugin::Static as well as a small
regex to set the MIME type for CSS to text/css.

    # lib/MyApp.pm
    package MyApp;

    use strict;
    use Catalyst qw/-Debug Static/;
    
    __PACKAGE__->action(

        '!default' => sub {
            my ( $self, $c ) = @_;
	    $c->serve_static;
	},
	    
        '/^.*\.css$/' => sub {
            my ( $self, $c ) = @_;
            $c->serve_static('text/css');
        },
    );

=head2 Uploads with Catalyst

To implement uploads in Catalyst you need to have a HTML form similiar to
this:

    <form action="/upload" method="post" enctype="multipart/form-data">
      <input type="hidden" name="form_submit" value="yes">
      <input type="file" name="my_file">
      <input type="submit" value="Send">
    </form>

It's very important not to forget enctype="multipart/form-data" in form, 
if it's not there, uploads just don't work.

Catalyst Controller module 'upload' action:

    MyApp->action(
    
        'upload' => sub {
            my ($self, $c) = @_;
            if ($c->req->parameters->{form_submit} eq 'yes') {
                my $filename = $c->req->parameters->{my_file};
                if ($filename) {
                    my $fh = $c->req->uploads->{$filename}->{fh};
                    open(NEW_FILE, ">/tmp/$filename") or die
                        "Can't open file for writing: $!";
                    while ($fh->read(my $buf, 32768)) {
                        print NEW_FILE $buf;
                    }
                    close(NEW_FILE);
                }
            }
            $c->stash->{template} = 'upload_form.tt';
            $c->forward('MyApp::V::View');
        },
    );

If you want to upload bigger files than 1MB, then just add to your Controller 
module:

    $CGI::Simple::POST_MAX = 1048576000;

=head2 Authentication with Catalyst::Plugin::Authentication::CDBI

There are (at least) two ways to implement authentication with this plugin:
1) only checking username and password
2) checking username, password and the roles the user has

For both variants you'll need the following code in your MyApp package:

    use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;

    MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
                                       user_field => 'email',
                                       password_field => 'password' });

'user_class' is a Class::DBI class for your users table.
'user_field' tells which field is used for username lookup (might be 
email, first name, surname etc).
'password_field' is, well, password field in your table and by default 
password is stored in plain text. Authentication::CDBI looks for 'user' 
and 'password' fields in table, if they're not defined in the config.

In PostgreSQL users table might be something like:

CREATE TABLE users (
  user_id   serial,
  name      varchar(100),
  surname   varchar(100),
  password  varchar(100),
  email     varchar(100),
  primary key(user_id)
);

We'll discuss the first variant for now:
1. user:password login / auth without roles

To log in a user you might use a action like this:

    '?login' => sub {
        my ($self, $c) = @_;
        if ($c->req->params->{username}) {
            $c->session_login($c->req->params->{username}, 
	                      $c->req->params->{password} );
            if ($c->req->{user}) {
                $c->forward('?restricted_area');
            }
        }
    },

$c->req->params->{username} and $c->req->params->{password} are html 
form parameters from a login form. If login succeeds, then $c->req->{user} 
contains the username of the authenticated user.

If you want to remember the users login status inbetween further requests, 
then just use the $c->session_login method, Catalyst will create a session 
id, session cookie and automatically append session id to all urls. So 
all you have to do, is just check $c->req->{user} where needed.

To log out user, just call $c->session_logout.

Now lets take a look at the second variant:
2. user:password login / auth with roles

To use roles you need to add to MyApp->config in the 'authentication' 
section following parameters:

    role_class      => 'MyApp::M::MyApp::Roles',
    user_role_class => 'MyApp::M::MyApp::UserRoles',
    user_role_user_field => 'user_id',
    user_role_role_field => 'role_id',

Corresponding tables in PostgreSQL could look like this:

CREATE TABLE roles (
  role_id  serial,
  name     varchar(100),
  primary key(role_id)
);

CREATE TABLE user_roles (
  user_role_id  serial,
  user_id       int,
  role_id       int,
  primary key(user_role_id),
  foreign key(user_id) references users(user_id),
  foreign key(role_id) references roles(role_id)
);

The 'roles' table is a list of role names and the 'user_role' table is used for
the user -> role lookup.

Now if a logged in user wants to see a location which is allowed only for 
people with 'admin' role then in you controller you can check it with:

    '?add' => sub {
        my ($self, $c) = @_;
        if ($c->roles(qw/admin/)) {
            $c->req->output("Your account has the role 'admin.'");
        } else {
            $c->req->output("You're not allowed to be here");
        }
    },

One thing you might need is to forward non-authenticated users to login 
form, if they try to access restricted areas. If you want to do this 
controller-wide (if you have one controller for admin section) then it's 
best to add user check to '!begin' action:

    '!begin' => sub {
        my ($self, $c) = @_;
        unless ($c->req->{user}) {
            $c->req->action(undef);  ## notice this!!
            $c->forward('?login');
        }
    },

Pay attention to $c->req->action(undef). This is needed, because of the 
way $c->forward works - forward to login gets called, but after that 
Catalyst executes anyway the action defined in the uri (eg. if you tried to 
watch /add, then first '!begin' forwards to '?login', but after that 
anyway '?add' is executed). So $c->req->action(undef) undefines any 
actions that were to be called and forwards user where we want him/her 
to be.

And this is all you need to do, isn't Catalyst wonderful?


=head2 How to use Catalyst without mod_perl

Catalyst applications give optimum performance when run under mod_perl.
However sometimes mod_perl is not an option, and running under CGI is just too
slow.  There are two alternatives to mod_perl that give reasonable
performance: FastCGI and PersistentPerl.

B<Using FastCGI>

To quote from L<http://www.fastcgi.com/>: "FastCGI is a language independent,
scalable, extension to CGI that provides high performance without the
limitations of specific server APIs."  Web server support is provided for
Apache in the form of C<mod_fastcgi> and there is Perl support in the C<FCGI>
module.  To convert a CGI Catalyst application to FastCGI one needs to
initialize an C<FCGI::Request> object and loop while the C<Accept> method
returns zero.  The following code shows how it is done - and it also works as
a normal, single-shot CGI script.

    #!/usr/bin/perl
    use strict;
    use FCGI;
    use MyApp;

    my $request = FCGI::Request();
    while ($request->Accept() >= 0) {
        my $output;
	{ 
	    local(*STDOUT);
	    open( STDOUT, '>', \$output );
	    MyApp->run;
	}
	$output =~ s!^HTTP/\d+.\d+ \d\d\d.*?\n!!s;
	print $output;
    }

Any initialization code should be included outside the request-accept loop.

There is one little complication, which is that C<MyApp->run> outputs a
complete HTTP response including the status line (e.g.: "C<HTTP/1.1 200>").
FastCGI just wants a set of headers, so the sample code captures the output
and  drops the first line if it is an HTTP status line (note: this may change).

The Apache C<mod_fastcgi> module is provided by a number of Linux distros and
is straightforward to compile for most Unix-like systems.  The module provides
a FastCGI Process Manager, which manages FastCGI scripts.  You configure your
script as a FastCGI script with the following Apache configuration directives:

    <Location /fcgi-bin>
       AddHandler fastcgi-script fcgi
    </Location>

or:

    <Location /fcgi-bin>
       SetHandler fastcgi-script
       Action fastcgi-script /path/to/fcgi-bin/fcgi-script
    </Location>

C<mod_fastcgi> provides a number of options for controlling the FastCGI
scripts spawned; it also allows scripts to be run to handle the
authentication, authorization and access check phases.

For more information see the FastCGI documentation, the C<FCGI> module and
L<http://www.fastcgi.com/>.


B<PersistentPerl>

PersistentPerl (previously known as C<CGI::SpeedyCGI>) is a persistent Perl
interpreter.  After the script is initially run, instead of exiting, the perl
interpreter is kept running. During subsequent runs, this interpreter is used
to handle new executions instead of starting a new perl interpreter each
time. A very fast frontend program contacts the persistent Perl process, which
is usually already running, to do the work and return the results.
PersistentPerl can be used to speed up perl CGI scripts.  It also provides an
Apache module so that scripts can be run without the overhead of doing a
fork/exec for each request.

The code for PersistentPerl is simpler than for FastCGI; rather than waiting
in an accept loop the script runs to completion, however variables are not
reinitialized on subsequent runs but maintain their values from the previous
run.


    #!/usr/bin/perperl
    use strict;
    use vars qw($output $initialized);
    use PersistentPerl;
    use MyApp;

    if (!$initialized++) {
        # initialization code - set up database, etc
        if ($PersistentPerl::i_am_per_perl) {
            # PP-specific initialization code
        }
    }
    { 
	local(*STDOUT);
	open( STDOUT, '>', \$output );
	MyApp->run;
    }
    $output =~ s!^HTTP/\d+.\d+ \d\d\d.*?\n!!s;
    print $output;

For more information see the C<PersistentPerl> documentation.


=head1 AUTHOR

Sebastian Riedel, C<sri@oook.de>
Danijel Milicevic C<me@danijel.de>
Viljo Marrandi C<vilts@yahoo.com>

=head1 COPYRIGHT

This program is free software, you can redistribute it and/or modify it under
the same terms as Perl itself.
