=head1 NAME

Squatting::Cookbook - Web Development Techniques for Squatting

=head1 INTRODUCTION

Squatting exists because I fell in love with Camping's API, and I couldn't bear
the thought of building another site using some other API.  When I decided that
the next site I wanted to build would be implemented in Perl, I had no choice
but to port Camping from Ruby to Perl, and that's how Squatting was born.

My hope is that other Perl programmers will be able to appreciate how concise
this API is, and I hope they'll see just how far a little bit of code can go.


=head2 The Anatomy of a Squatting Application

For many of the examples to follow, a Squatting app called "App" will be used.
First, let's take a look at how the packages are laid out.

  +-----------------------------------------------------------------+
  | App                                                             |
  |   init()              one-time initialization                   |
  |   service()           run on every HTTP request                 |
  |                                                                 |
  |   %CONFIG             app configuration goes here               |
  |                                                                 |
  | +-------------------------------------------------------------+ |
  | | App::Controllers                                            | |
  | |   @C                list of controller objects goes here    | |
  | |                                                             | |
  | |   C()               utility function for making controllers | |
  | |   R()               a URL generation function               | |
  | |                                                             | |
  | +-------------------------------------------------------------+ |
  | +-------------------------------------------------------------+ |
  | | App::Views                                                  | |
  | |   @V                list of view objects goes here          | |
  | |                                                             | |
  | |   V()               utility function for making views       | |
  | |   R()               a URL generation function               | |
  | |                                                             | |
  | +-------------------------------------------------------------+ |
  +-----------------------------------------------------------------+

Most Squatting apps will be composed of at least 3 packages.

=over 2

=item First you need a package for the App itself.

B<App> will inherit from L<Squatting>.

Override the C<init> method if you want to perform some initialization upon start up.

Override the C<service> method if you want to do something before or after an HTTP request.

Put your app config in %CONFIG.

=item Then you need a package to hold the controller objects.

B<App::Controllers> should C<use Squatting ':controllers'> and populate @C
with controller objects.

  package App::Controllers;
  use strict;
  use warnings;
  use Squatting ':controllers';
  our @C = (
    C(
      Home => [ '/' ],
      get => sub { }
    ),
    C(
      Contact => [ '/contact' ],
      get => sub { },
      post => sub { },
    ),
  );

=item Finally you should have a package for holding the view objects.

B<App::Views> should C<use Squatting ':views'> and populate @V
with view objects.

  package App::Views;
  use strict;
  use warnings;
  use Squatting ':views';
  our @V = (
    V(
      "default",
      layout  => sub { },
      home    => sub { },
      contact => sub { },
    ),
  );

=back

Keep in mind that the packages for controllers and views are not classes.  They
are never instantiated.  Think of them more as namespaces, because that's the
way Squatting interprets them.  Their main purpose is to be containers for
objects.

B<It might help to think of a Squatting app as a data structure.>  A reductionist
might say that an app is just an object that contains a list of controller and
view objects, and he wouldn't be that far off from the truth.  This design
decision is what allowed Squatting to embed itself just about anywhere.  Being
a piece of data means that you're free to move around, and this is what gave
this framework a lot of unintended power.


=head1 PROGRAMMING TECHNIQUES

=head2 COMET

The easiest way to add COMET support to a Squatting app is to install
L<Stardust> via CPAN and mount it.  Stardust is a Squatting app that implements
a simple COMET server that's intended to run alongside any old regular web app.
It provides a RESTful API so that even people who don't use Perl can use it as
their COMET server and pass realtime messages around.  However, if you're using
Stardust with a Squatting app, you get to bypass the RESTful API and send
messages into the system directly.

B<Installation>:

  sudo cpan Stardust

B<Documentation>:

  perldoc Stardust
  stardust.pl --help
  stardust.pl --demo

If you want to understand how it works, take a look at the code.  It's a very
small Squatting app that only has a few controllers, and it'll acquaint you with
the wonders of L<Coro> and L<AnyEvent>.

B<Source>:

L<http://github.com/beppu/stardust/tree/master>

=head2 How to Set Up Sessions

(I could actually use a little coding help here.  If someone could take the time to
write a plugin called L<Squatting::With::Apache::Session> that would be great.)

=head3 Continuity and Process Memory

Pure Continuity apps typically don't use persistent session storage, because
they can use lexically scoped variables instead.  However, Squatting apps are
RESTful and stateless by default, so you can't count on the lexical scope of a
controller to stick around between requests.  Luckily, package variables *will*
stick around, so that's what we'll use to implement persistent sessions.

  package App;
  our %state;
  sub service {
    my ($app, $c, @args) = @_;
    my $cr  = $c->cr;
    my $sid = $cr->{session_id};
    if (defined $sid) {
      $c->state = $state{$sid} ||= {};
    }
    $app->next::method($c, @args);
  }

Here, we override service() in the main module of our app so that $c->state will
provide a hashref whose values will persist between requests.

Note that instead of writing C<$app-E<gt>SUPER::service>, we have to write
C<$app-E<gt>next::method>, because Squatting is a sublcass of
L<Class::C3::Componentised>.


=head3 When Squatting::On::Catalyst

When squatting on top of Catalyst, the Catalyst session becomes
C<$self-E<gt>state> in Squatting.  The session storage code in Catalyst is very
mature, so it is highly recommended that all the session setup be done on the
Catalyst side.


=head3 Sessions From Scratch

The challenge is to find a way to assign unique session ids to each visitor and
use that session id as a key into a persistent store.  TMTOWTDI


=head2 How to Use Various Templating Systems With Squatting

=head3 HTML::AsSubs

I like L<HTML::AsSubs> for the following reasons:

=over 4

=item * It works as advertised.

=item * The implementation is really small.

=item * It seems to be widely deployed (even though no one uses it).

=item * And generating HTML with code eliminates the need to install template files.

=back

The documentation is up-front about some of the module's shortcomings which I
appreciate.  However, the docs go a bit too far and recommend that this module
not even be used!  It says that there are "cleaner" alternatives, but when I
looked at them, I came straight back to HTML::AsSubs.

I think the module works just fine, and I'd like to show you how I use it.

=head4 Addressing HTML::AsSubs Shortcomings (Alleged and Otherwise)

=over 4

=item The exported link() function overrides the builtin link() function.

Noted.  You shouldn't be calling the builtin C<link()> in view code anyway, so
it's not a big deal.

=item The exported tr() function must be called using &tr(...) syntax.

This is because it clashes with the builtin tr/../../ operator.
I can live with this.

=item Problem: exports so damned much.  (from the code comments)

The funny thing is, it's actually not exporting enough.  It's missing subs for
the C<span>, C<thead>, and C<tbody> tags.

  sub span  { HTML::AsSubs::_elem('span',  @_) }
  sub thead { HTML::AsSubs::_elem('thead', @_) }
  sub tbody { HTML::AsSubs::_elem('tbody', @_) }

If there are any other missing tags, you know what to do.

=back

There's one more pseudo-tag that I like to add for practical reasons.

  sub x { map { HTML::Element->new('~literal', text => $_) } @_ }

Normally, HTML::AsSubs will entity escape all the text that you give it.
However, there are many times when you legitimately don't want text to be
entity escaped, so that's what C<x()> is for.

=head4 An Example View That Uses HTML::AsSubs

  package App::Views;
  use strict;
  use warnings;
  use Squatting ':views';
  use HTML::AsSubs;

  sub span  { HTML::AsSubs::_elem('span', @_) }
  sub thead { HTML::AsSubs::_elem('thead', @_) }
  sub tbody { HTML::AsSubs::_elem('tbody', @_) }
  sub x     { map { HTML::Element->new('~literal', text => $_) } @_ }

  our @V = (
    V(
      'html',
      layout => sub {
        my ($self, $v, $content) = @_;
        html(
          head(
            title( $v->{title} ),
            style(x( $self->_css )),
          ),
          body(
            x( $content )
          )
        )->as_HTML;
      },
      _css => sub {qq|
        body {
          background : #000;
          color      : #f5deb3;
        }
      |},
      home => sub {
        my ($self, $v) = @_;
        h1( $v->{message} )->as_HTML;
      },
    ),
  );
  1;

Again, the nicest part about generating HTML from code is that you don't have
to worry about installing template files.  The templates are in memory as perl
expressions.  When building web apps that are designed to be embedded, this is
a really nice feature to have as it makes deployment that much easier.

If HTML::AsSubs is a bit too low tech for you, there are more modern
expressions of the code-to-html idea on CPAN.  For example,
L<Template::Declare> and L<HTML::Tiny> may be worth looking into.  I'm happy
with L<HTML::AsSubs>, though.


=head3 Tenjin

Tenjin is the fastest templating system that no one outside of Japan seems to
know about.  It's really unfortunate that this module isn't on CPAN, but
hopefully this will be rectified in the near future.  Until then, you can
download it from L<http://www.kuwata-lab.com/tenjin/>.

=head4 An Example View That Uses Tenjin

First, make sure your template_path is configurable for deployment purposes.

  package App;
  our %CONFIG = (
    template_path => './www'
  );

And here is the actual view:

  package App::Views;
  use strict;
  use warnings;
  no  warnings 'once';
  use Squatting ':views';
  use Tenjin;

  # make functions defined in this package available to templates
  use base 'Tenjin::Context';
  eval $Tenjin::Context::defun;
  $Tenjin::CONTEXT_CLASS = 'App::Views';

  our @V = (
    V(
      'tenjin',
      tenjin => Tenjin::Engine->new({
        path => [ $App::CONFIG{template_path} ], postfix => '.html'
      }),
      layout => sub {
        my ($self, $v, $content) = @_;
        my $tenjin = $self->{tenjin};
        $v->{content} = $content;
        $tenjin->render(":layout", $v);
      },
      _ => sub {
        my ($self, $v) = @_;
        my $tenjin = $self->{tenjin};
        $v->{self} = $self;
        $tenjin->render(":$self->{template}", $v);
      }
    ),
  );
  1;

That's all there is too it.  Views for other file-based templating systems will
follow a similar pattern where the special C<_> template is used to map method
names to filenames.

=head3 Template Toolkit

L<Template Toolkit|Template> is probably the most popular templating system in
use by the Perl community as of this writing.  This is one way you could implement
a view for it:

  package App::Views;
  use strict;
  use warnings;
  use Squatting ':views';
  use Template;

  our @V = (
    V(
      'html',
      tt => Template->new($App::CONFIG{tt_config}),

      layout => sub {
        my ($self, $v, $body) = @_;
        my $tt = $self->{tt};
        $v->{body} = $body;
        my $output;
        $tt->process('layout' . $App::CONFIG{tt_postfix}, $v, \$output);
        return $output;
      },

      _ => sub {
        my ($self, $v) = @_;
        my $tt = $self->{tt};
        $v->{R} = \&R;
        my $output;
        $tt->process($self->{template} . $App::CONFIG{tt_postfix}, $v, \$output);
        return $output;
      },
    ),
  );
  1;

Credit for this example goes to draegtun.
L<http://draegtun.wordpress.com/2008/10/21/using-template-toolkit-with-squatting/>

=head3 HTML::Mason

TODO

=head3 HTML::Template

L<HTML::Template> is one of the strictest templating systems around.  There is
very little processing you can do from within the templates, so you're really
forced to do all your data manipulation B<BEFORE> the templating system sees
it.  Some people like this hard separation, and if you're one of them, here is
how you'd make use of HTML::Template from within Squatting.

  package App::Views;
  use strict;
  use warnings;
  use Squatting ':views';
  use HTML::Template::Pro;

  our @V = (
    V(
      'html',
      layout => sub {
        my ($self, $v, $content) = @_;
        my $root = $App::CONFIG{root};
        my $t = HTML::Template::Pro->new(filename => "$root/layout.html");
        $v->{content} = $content;
        $t->param(%$v);
        $t->output;
      },
      _ => sub {
        my ($self, $v) = @_;
        my $root = $App::CONFIG{root};
        my $template = $self->{template};
        my $t = HTML::Template::Pro->new(filename => "$root/$template.html");
        $t->param(%$v);
        $t->output;
      },
    )
  );
  1;

=head3 XML::Atom

TODO - This is not a templating system, but it's useful to know how to generate
well-formed Atom feeds, so I'm going to include it in this section as well.

Views are not just for HTML....


=head2 How to Take Advantage of Having Multiple Views

In the documentation for the L<Squatting> module, it said that multiple views
per app were supported, and that it was "kinda like Catalyst (but not quite)".
L<Catalyst> also supports multiple views per app, so there are certain techniques
that both frameworks can implement.

=head3 Kinda Like Catalyst -- Views as Data Formats


=head3 But Not Quite -- Multiple Views of the Same Format == Themes


=head2 How to Internationalize and Localize Squatting Apps

The longer you wait to internationalize a web application, the harder the task
becomes due to the ever increasing number of strings being used.  Thus, if you
have any ambition of catering to an international audience, it would be wise to
internationalize your application right from the beginning when the task is at
its easiest.

=head3 Using Subdomains to Determine Language Preference

First, we need a high-level strategy for determining what language to present
to the user.  Wikipedia's approach of using 2-letter language codes in their
subdomains is my favorite way of doing this.  (For example, the English version
of Wikipedia is at L<http://en.wikipedia.org/> and the Korean version of
Wikipedia is at L<http://ko.wikipedia.org/>.)

I like this approach for a number of reasons.

=over 4

=item Visitors have control over what language to use.

=item The URLs look nice while remaining easy to manage.

=item It's search engine friendly.

=back

To make our Squatting apps aware of what subdomain was requested, the
C<service()> method can be overridden as follows:

  package App;
  use strict;
  use warnings;
  use base 'Squatting';

  use App::L10N;
  use I18N::LangTags::List;

  sub translation_function {
    my ($c) = @_;
    my @h   = split(/\./ => $c->env->{HTTP_HOST});
    my $lang_tag = I18N::LangTags::List::name($h[0]) || 'en';
    my $l10n     = App::L10N->get_handle($lang_tag);
    sub { $l10n->maketext(@_) if @_ };
  }

  sub service {
    my ($app, $c, @args) = @_;
    $c->v->{tr} = translation_function($c);
    $app->next::method($c, @args);
  }

The important code is in C<translation_function($c)>.

=head3 Creating Localization Classes with Locale::Maketext


=head3 Handling UTF-8 Input Correctly




=head2 How to be an OpenID Consumer

TODO - go into much more detail and clean up the code.

helper function for making a Net::OpenID::Consumer object

  sub csr {
    my ($self) = @_;
    return Net::OpenID::Consumer->new(
      ua    => LWPx::ParanoidAgent->new,
      cache => Cache::File->new(cache_root => '/tmp/openid-consumer-cache'),
      args  => $self->input,
      consumer_secret => '...',
      required_root   => 'http://work:4234/'
    );
  }

Login controller; form is provided somewhere else; POST is the entry point;
GET is where the sequence finishes.

  C(
    Login => [ '/login' ],
    get => sub {
      my ($self) = @_;
      my $csr = csr($self);
      $self->headers->{'Content-Type'} = 'text/plain';
      if (my $setup_url = $csr->user_setup_url) {
        # redirect/link/popup user to $setup_url
        $self->redirect($setup_url);
        return;
      } elsif ($csr->user_cancel) {
        # restore web app state to prior to check_url
        return "user_cancel";
      } elsif (my $vident = $csr->verified_identity) {
         my $verified_url = $vident->url;
         return "verified_url $verified_url !";
      } else {
         return "Error validating identity: " . $csr->err;
      }
    },
    post => sub {
      my ($self) = @_;
      my $input = $self->input;
      my $csr = csr($self);
      my $claimed_identity = $csr->claimed_identity($input->{openid});
      my $check_url = $claimed_identity->check_url(
        return_to  => "http://work:4234/login",
        trust_root => "http://work:4234/",
      );
      $self->redirect($check_url);
    },
  ),

=head2 How to be an OpenID Provider



=head2 How to Compose Multiple Squatting Apps Into One App

B<*BEFORE*> you C<init> the app, you can mount other apps like this:

  App->mount('OtherApp0' => '/prefix0');
  App->mount('OtherApp1' => '/prefix1');
  App->mount('OtherApp2' => '/prefix2');

Once you're done mounting, run init:

  App->init();

Finally, if you need to C<relocate>, do that after you finish mounting.
Remember:  the order is always C<mount>, C<init>, and C<relocate>.  Also,
remember that an app can only be mounted once, and C<relocate> should only be
called once.

=head2 How to Embed a Squatting App Into Other Frameworks

In order to embed a Squatting app into an app written in another
framework, we need to be able to do the following things.

=over 4

=item get incoming CGI parameters

=item get incoming HTTP request headers

=item get incoming HTTP method

=item set outgoing HTTP status

=item set outgoing HTTP response headers

=item set outgoing content

=back

If we can do all these things, Squatting can make itself at home.
Here are some concrete examples to get you started.

=head3 Catalyst

To embed a Squatting app into a Catalyst app, you can add code like
this to your C<Root> controller.

  use App 'On::Catalyst';
  App->init;
  App->relocate('/somewhere');
  sub somewhere : Local { App->catalyze($_[1]) }

If you want the Squatting app to be completely in charge, you don't
even have to relocate() -- just redefine the default() method like
this:

  use App 'On::Catalyst';
  App->init;
  sub default : Private { App->catalyze($_[1]) }

=head3 HTTP::Engine

Running an app on top of L<HTTP::Engine> is accomplished by using the
L<Squatting::On::HTTP::Engine> module like this:

  use App 'On::HTTP::Engine';
  App->init;
  App->http_engine(
    interface => 'ServerSimple',
    args      => {
      host => 'localhost',
      port => 2222,
    }
  )->run;

Squatting::On::HTTP::Engine supports many other interfaces such as
FCGI and ModPerl (for Apache 2.2 only), so please consult the docs for
this module if this method of deployment interests you.

=head3 Mojo

TODO

=head3 Jifty

TODO

=head3 Raw mod_perl1

TODO

=head3 Raw mod_perl2

TODO

=head3 HTML::Mason

TODO:  Implement a dhandler that embeds a Squatting app

=head3 CGI

To run a Squatting app in a CGI environment, a script like the following has to
be written.

  use App 'On::CGI';
  my $q = CGI->new;
  App->init;
  App->relocate('/cgi-bin/app.cgi');
  App->cgi($q);

The key to doing this right is to C<relocate> the app correctly.
The path that you relocate to should be the same as the C<REQUEST_PATH>
for the script.  For example, if the URL you use to get to the script
is F<http://localhost/cgi-bin/app.cgi>, then you should relocate to
F</cgi-bin/app.cgi>.


=head2 How to Replace a Squatting App's Layout

Now that you've embedded or composed some Squatting apps together, the
next thing you'll want to do is make the whole system of sites look
consistent.  To do this, you'll usually get the App's first view object
and replace its layout method with your own.

  my $view = $App::Views::V[0];
  $view->{layout} = sub {
    my ($self, $v, $content) = @_;
    #
    # Make the layout look however you want
    # using any templating system you want
    # ( or none at all ),
    # and return a string that wraps $content
    #
  };

=head1 DEPLOYMENT TECHNIQUES

=head2 Let Squatting+Continuity Own Port 80

This is the simplest thing you could possibly do, but it's also somewhat
limiting.

=head2 Reverse Proxying to Squatting+Continuity w/ nginx

TODO

=head2 Reverse Proxying to Squatting+Continuity w/ Apache 2.2

TODO

=head2 Reverse Proxying to Squatting+Continuity w/ Perlbal

TODO

=head2 Piggy-Backing on Top of Other Frameworks

If you've embedded a Squatting app into another application, the rules and
conventions governing the other application's framework take precedence.
Follow their deployment guidelines, and you should be fine.


=head1 SCALING TECHNIQUES

This section is for those who wish to scale Squatting apps that are using a
Continuity foundation.  If any part of your site is RESTless and stateful, and
you've suddenly got a lot of traffic to your site, this section is for you.

=head2 Session Affinity with Multiple Instances

TODO

=for later
L<http://onsmalltalk.com/programming/smalltalk/seaside/scaling-seaside-more-advanced-load-balancing-and-publishing/>

=head2 Linux and OpenSSI

TODO

=head2 DragonFlyBSD Single Image Cluster

This is currently science fiction.


=cut

# Local Variables: ***
# mode: cperl ***
# indent-tabs-mode: nil ***
# cperl-close-paren-offset: -2 ***
# cperl-continued-statement-offset: 2 ***
# cperl-indent-level: 2 ***
# cperl-indent-parens-as-block: t ***
# cperl-tab-always-indent: nil ***
# End: ***
# vim:tabstop=8 softtabstop=2 shiftwidth=2 shiftround expandtab
