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