1package Squatting::Controller;
2sub new{bless{name=>$_[1],urls=>$_[2],@_[3..$#_]}=>$_[0]}
3sub clone{bless{%{$_[0]},@_[1..$#_]}=>ref($_[0])}
4for my$m qw(name urls cr env input cookies state v status headers log view app){
5*{$m}=sub:lvalue{$_[0]->{$m}}}
6for my$m qw(get post head put delete options trace connect){
7*{$m}=sub{$_[0]->{$m}->(@_)}}sub param{my($self,$k,@v)=@_;
8if(defined $k){if(@v){$self->input->{$k}=((@v>1)?\@v:$v[0]);
9}else{$self->input->{$k}}
10}else{keys%{$self->input}}}
11sub render{my($self,$template,$vn)=@_;my$view;$vn||=$self->view;
12my$app=$self->app;if(defined($vn)){$view=${$app."::Views::V"}{$vn};
13}else{$view=${$app."::Views::V"}[0]}
14$view->headers=$self->headers;$view->$template($self->v)}
15sub redirect{my($self,$l,$s)=@_;$self->headers->{Location}=$l||'/';
16$self->status=$s||302}my$not_found=sub{$_[0]->status=404;
17$_[0]->env->{REQUEST_PATH}." not found."};
18our$r404=Squatting::Controller->new(R404=>[],
19get=>$not_found,post=>$not_found,app=>'Squatting');
20package Squatting;
21use base"Class::C3::Componentised";use List::Util"first";use URI::Escape;
22use Carp;our$VERSION='0.60';sub import{my$m=shift;my$p=(caller)[0];my$app=$p;
23$app=~s/::Controllers$//;$app=~s/::Views$//;if(UNIVERSAL::isa($app,'Squatting')
24){*{$p."::R"}=sub{my($controller,@args)=@_;my$input;if(@args && ref($args[-1])
25eq'HASH'){$input=pop(@args)}my$c=${$app."::Controllers::C"}{$controller};
26croak"$controller controller not found"unless$c;my$arity=@args;
27my$path=first{my@m=/\(.*?\)/g;$arity==@m}@{$c->urls};
28croak"couldn't find a matching URL path" unless $path;
29while($path=~/\(.*?\)/){
30$path=~s{\(.*?\)}{uri_escape(+shift(@args),"^A-Za-z0-9\-_.!~*’()/")}e}
31if($input){$path.="?".join('&'=>map{my$k=$_;ref($input->{$_})eq'ARRAY'
32?map{"$k=".uri_escape($_)}@{$input->{$_}}:"$_=".uri_escape($input->{$_})
33}keys %$input)}$path};
34*{$app."::D"}=sub{my$url=uri_unescape($_[0]);
35my$C=\@{$app.'::Controllers::C'};my($c,@regex_captures);for$c(@$C){
36for(@{$c->urls}){if(@regex_captures=($url=~qr{^$_$})){
37pop @regex_captures if($#+==0);return($c,\@regex_captures)}}}
38($Squatting::Controller::r404,[])}unless exists ${$app."::"}{D}}
39my@c;for(@_){if($_ eq':controllers'){*{$p."::C"}=sub{
40Squatting::Controller->new(@_,app=>$app)};
41}elsif($_ eq':views'){*{$p."::V"}=sub{Squatting::View->new(@_)};
42}elsif(/::/){push @c,$_}}$m->load_components(@c)if@c}
43sub component_base_class{__PACKAGE__}sub mount{my($app,$other,$prefix)=@_;
44push @{$app."::O"},$other;push @{$app."::Controllers::C"},map{
45my$urls=$_->urls;$_->urls=[map{$prefix.$_}@$urls];$_;
46}@{$other."::Controllers::C"}}
47sub relocate{my($app,$prefix)=@_;for(@{$app."::Controllers::C"}){
48my$urls=$_->urls;$_->urls=[map{$prefix.$_}@$urls]}}
49sub init{$_->init for(@{$_[0]."::O"});%{$_[0]."::Controllers::C"}=
50map{$_->name=>$_}@{$_[0]."::Controllers::C"};
51%{$_[0]."::Views::V"}=map{$_->name=>$_}@{$_[0]."::Views::V"}}
52sub service{my($app,$c,@args)=grep{defined}@_;my$method=lc
53$c->env->{REQUEST_METHOD};my$content;eval{$content=$c->$method(@args)};
54warn"EXCEPTION: $@"if($@);my$cookies=$c->cookies;$c->headers->{'Set-Cookie'}=
55join("; ",map{CGI::Cookie->new(-name=>$_,%{$cookies->{$_}})}
56grep{ref$cookies->{$_}eq'HASH'}keys %$cookies)if(%$cookies);$content}
57package Squatting::View;sub new{
58my$class=shift;my$name=shift;bless{name=>$name,@_}=>$class}
59sub name:lvalue{$_[0]->{name}};sub headers:lvalue{$_[0]->{headers}}
60sub _render{my($self,$template,$vars,$alt)=@_;$self->{template}=$template;
61if(exists$self->{layout}&&($template!~/^_/)){$template=$alt if defined$alt;
62$self->{layout}($self,$vars,$self->{$template}($self,$vars));
63}else{$template=$alt if defined $alt;$self->{$template}($self,$vars)}}
64sub AUTOLOAD{my($self,$vars)=@_;my$template=$AUTOLOAD;
65$template=~s/.*://;if(exists$self->{$template}&&ref($self->{$template})eq
66'CODE'){$self->_render($template,$vars)}elsif(exists$self->{_}){
67$self->_render($template,$vars,'_')}else{die(
68"$template cannot be rendered.")}};sub DESTROY{};1;
69