X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FWeb%2FSimple%2FApplication.pm;h=e11eef71eead64b746279de0475dd272cd4f4a7b;hb=795c4698606dcaac5f660d9c759cda2474038eae;hp=de514c8dd4d8eec2e26372c44a694842ecfb698c;hpb=81a5b03ea99065c6dfff6a3ccd99241dd03826c6;p=catagits%2FWeb-Simple.git diff --git a/lib/Web/Simple/Application.pm b/lib/Web/Simple/Application.pm index de514c8..e11eef7 100644 --- a/lib/Web/Simple/Application.pm +++ b/lib/Web/Simple/Application.pm @@ -25,7 +25,7 @@ use warnings FATAL => 'all'; sub dispatch { my ($self, $env, @args) = @_; - my $next = $self->next; + my $next = $self->_has_match ? $self->next : undef; if (my ($env_delta, @match) = $self->_match_against($env)) { if (my ($result) = $self->_execute_with(@args, @match)) { if ($self->_is_dispatcher($result)) { @@ -36,9 +36,18 @@ use warnings FATAL => 'all'; } } } + return () unless $next; return $next->dispatch($env, @args); } + sub call { + @_ > 1 + ? $_[0]->{call} = $_[1] + : shift->{call} + } + + sub _has_match { $_[0]->{match} } + sub _match_against { return ({}, $_[1]) unless $_[0]->{match}; $_[0]->{match}->($_[1]); @@ -55,6 +64,21 @@ sub new { bless({ config => $config }, $class); } +sub _setup_default_config { + my $class = shift; + { + no strict 'refs'; + if (${"${class}::_default_config"}{CODE}) { + $class->_cannot_call_twice('_setup_default_config', 'default_config'); + } + } + my @defaults = (@_, $class->_default_config); + { + no strict 'refs'; + *{"${class}::_default_config"} = sub { @defaults }; + } +} + sub _default_config { () } sub config { @@ -66,7 +90,9 @@ sub _construct_response_filter { $_[0]->_build_dispatcher({ call => sub { my ($d, $self, $env) = (shift, shift, shift); - $self->_run_with_self($code, $d->next->dispatch($env, $self, @_)); + my @next = $d->next->dispatch($env, $self, @_); + return unless @next; + $self->_run_with_self($code, @next); }, }); } @@ -77,43 +103,95 @@ sub _construct_redispatch { call => sub { shift; my ($self, $env) = @_; - $self->handle_request({ %{$env}, PATH_INFO => $new_path }) + $self->_dispatch({ %{$env}, PATH_INFO => $new_path }) } }) } -sub _dispatch_parser { +sub _build_dispatch_parser { require Web::Simple::DispatchParser; return Web::Simple::DispatchParser->new; } -sub _setup_dispatchables { - my ($class, $dispatch_subs) = @_; - my $parser = $class->_dispatch_parser; - my @dispatchables; - my ($root, $last); - foreach my $dispatch_sub (@$dispatch_subs) { - my $proto = prototype $dispatch_sub; - my $matcher = ( - defined($proto) - ? $parser->parse_dispatch_specification($proto) - : undef - ); - my $new = $class->_build_dispatcher({ - match => $matcher, - call => sub { shift; - shift->_run_with_self($dispatch_sub, @_) - }, - }); - $root ||= $new; - $last = $last ? $last->next($new) : $new; - push @dispatchables, [ $matcher, $dispatch_sub ]; +sub _cannot_call_twice { + my ($class, $method, $sub) = @_; + my $error = "Cannot call ${method} twice for ${class}"; + if ($sub) { + $error .= " - did you call Web::Simple's ${sub} export twice?"; } - $last->next($class->_build_final_dispatcher); + die $error; +} + +sub _setup_dispatcher { + my ($class, $dispatch_specs) = @_; { no strict 'refs'; - *{"${class}::_dispatch_root"} = sub { $root }; + if (${"${class}::_dispatcher"}{CODE}) { + $class->_cannot_call_twice('_setup_dispatcher', 'dispatch'); + } + } + my $chain = $class->_build_dispatch_chain( + [ @$dispatch_specs, $class->_build_final_dispatcher ] + ); + { + no strict 'refs'; + *{"${class}::_dispatcher"} = sub { $chain }; + } +} + +sub _construct_subdispatch { + my ($class, $dispatch_spec) = @_; + my $disp = $class->_build_dispatcher_from_spec($dispatch_spec); + my $call = $disp->call; + $disp->call(sub { + my @res = $call->(@_); + return unless @res; + my $chain = $class->_build_dispatch_chain(@res); + return $class->_build_dispatcher({ + call => sub { + my ($d, $self, $env) = (shift, shift, shift); + return $chain->dispatch($env, $self, @_); + } + }); + }); + return $class->_build_dispatcher({ + call => sub { + my ($d, $self, $env) = (shift, shift, shift); + my @sub = $disp->dispatch($env, $self, @_); + return @sub if @sub; + return unless (my $next = $d->next); + return $next->dispatch($env, $self, @_); + }, + }); +} + +sub _build_dispatcher_from_spec { + my ($class, $spec) = @_; + return $spec unless ref($spec) eq 'CODE'; + my $proto = prototype $spec; + my $parser = $class->_build_dispatch_parser; + my $matcher = ( + defined($proto) + ? $parser->parse_dispatch_specification($proto) + : undef + ); + return $class->_build_dispatcher({ + match => $matcher, + call => sub { shift; + shift->_run_with_self($spec, @_) + }, + }); +} + +sub _build_dispatch_chain { + my ($class, $dispatch_specs) = @_; + my ($root, $last); + foreach my $dispatch_spec (@$dispatch_specs) { + my $new = $class->_build_dispatcher_from_spec($dispatch_spec); + $root ||= $new; + $last = $last ? $last->next($new) : $new; } + return $root; } sub _build_dispatcher { @@ -131,9 +209,9 @@ sub _build_final_dispatcher { }) } -sub handle_request { +sub _dispatch { my ($self, $env) = @_; - $self->_dispatch_root->dispatch($env, $self); + $self->_dispatcher->dispatch($env, $self); } sub _run_with_self { @@ -154,13 +232,13 @@ sub run_if_script { sub _run_cgi { my $self = shift; require Web::Simple::HackedPlack; - Plack::Server::CGI->run(sub { $self->handle_request(@_) }); + Plack::Server::CGI->run(sub { $self->_dispatch(@_) }); } sub run { my $self = shift; if ($ENV{GATEWAY_INTERFACE}) { - $self->_run_cgi; + return $self->_run_cgi; } my $path = shift(@ARGV) or die "No path passed - use $0 / for root";