r12983@zaphod: kd | 2008-04-28 18:10:27 +1000
Matt S Trout [Mon, 1 Sep 2008 13:06:33 +0000 (13:06 +0000)]
lighttpd non-root doc fix

POD: IO::FileHandle => IO::Handle (RT #35690)
Fix grammar on welcome page (RT #33236)
Fix for Path('0') handling (RT #29334)
fix path tests for win32 compat
Workaround for Win32 and c3_mro.t (RT #26452, tested by Kenichi Ishigaki)
query param encoding patch
adding RT ticket #
test for multiple chained attrs
just use perl_version() to specify the min. required version of perl
Provide backwards compatability methods in Catalyst::Stats
Prepare for release
Fix regression for relative uri_for arguments after a forward() introduced in 5.7013 (Peter Karman)
missing file
Fix regression for "sub foo : Path {}" in the root controller which was introduced when attempting to allow "0" as a Path.
r13155@zaphod:  kd | 2008-05-23 19:31:34 +1000
more yaml expurgation

r13167@zaphod:  kd | 2008-05-24 08:57:42 +1000
fixing up some merge fun

Fixed changes
tidy up Changes a wee bit.
Get some of the optional_* tests working from dirs with spaces (RT #26455)
fix up test to match release.
Fix Catalyst::Utils::home() when application .pm is in the current dir (RT #34437)
Added the ability to remove parameters in req->uri_with() by passing in an undef value (RT #34782)
remove a confusing and duplicate bit of documentation
Fix for LocalRegex when used in the Root controller
Update HTTP::Body dep so that the uploadtmp config value will work (RT #22540)
remove 0-length query string components so warnings aren't thrown (RT #36428)
merge compres branch
merge go branch.
Prepare for 5.7099_01
Add go to changes
Fix warning for dev versions
rescue :PathPrefix from an old branch
nuked Catalyst::Build
authors cleanup
missed one
Clarify inner package behavior of companent instantation in the POD. (no 1 of 2 doc patches I promised mst)
Updated Catalyst::Test docs to mention the use of HTTP::Request objects (Rafael Kitover)
Prepare 5.7100
removing some Dumpers
prepare for devel release
doc typo fix (rafl)
tiny doc update
Fix regression for regexp fallback in model(), view() and controller()
more fixes to regexp fallback, ugh.
Added the supplied argument to the regexp fallback warning for easier debugging
Ensure ACCEPT_CONTEXT is called for results from component()
add a test for ACCEPT_CONTEXT from regex fallback in view()
Prepare for _03 dev release
Added warnign that path_to() should be called after MyApp->setup, which sets $c->config->{home}

Undoing my commit since the issue had been fixed in Catalyst::Devel 1.08 (#37869)

Add chained go tests
TODO tests added for :ChainedParent and :Chained('../action') atrs
Switch syntax example for model to use create=static

Add pt translation of error msg
tidy up the name of module in pod
Fix some Win32 test failures
go() cannot dispatch to anon. actions, fix code and tests to match.
pod fix
merge rafl's test
DispatchType/Chained:
A chain of equal actions but less captures should win over one with more captures.
Less captures is taken to mean it did so less ambiguously, and therefore wins the fight.
A chain of more actions will lose to one of less actions even if it had less captures, though.
i.e.: Actions beat Captures, but Captures decide betwixt Actions.

r13745@harold:  kd | 2008-08-20 19:24:42 +0100
improvements to redirect doc

Move :Chained sugar out of DispatchType::Chained.
Fix tests for ../action chaining.

rootdef is an endaction; the tests expect two args for chained_rel.
Implement :Chained('../action').
Implement :ChainedParent.
UnTODO :Chained('../action') tests.
Test :Chained('../../action').
Implement relative chaining over more than one level.
UnTODO tests for relative chaining over multiple levels.
Add a test controller for :Chained('../../action').

Forgot to add it in r8276.
Changelogging.
r18460@agaton (orig r7637):  zarquon | 2008-04-28 09:22:19 +0100
r18802@agaton (orig r7710):  bricas | 2008-05-07 14:05:16 +0100
r18803@agaton (orig r7711):  bricas | 2008-05-07 14:23:04 +0100
r18806@agaton (orig r7714):  bricas | 2008-05-07 18:42:22 +0100
r18807@agaton (orig r7715):  bricas | 2008-05-07 20:23:05 +0100
r18808@agaton (orig r7716):  bricas | 2008-05-07 21:10:08 +0100
r18823@agaton (orig r7731):  marcus | 2008-05-09 18:04:23 +0100
r18824@agaton (orig r7732):  bricas | 2008-05-09 19:47:05 +0100
r18828@agaton (orig r7736):  marcus | 2008-05-11 10:08:43 +0100
r18883@agaton (orig r7748):  bricas | 2008-05-14 14:42:12 +0100
r18884@agaton (orig r7749):  bricas | 2008-05-14 14:42:50 +0100
r19082@agaton (orig r7758):  marcus | 2008-05-16 19:16:16 +0100
r19100@agaton (orig r7776):  bricas | 2008-05-23 15:54:22 +0100
r19101@agaton (orig r7777):  bricas | 2008-05-23 15:58:07 +0100
r19102@agaton (orig r7778):  bricas | 2008-05-23 17:54:30 +0100
r19106@agaton (orig r7782):  zarquon | 2008-05-23 23:55:26 +0100
r19108@agaton (orig r7784):  zarquon | 2008-05-23 23:57:59 +0100
r19115@agaton (orig r7790):  marcus | 2008-05-25 16:44:47 +0100
r19119@agaton (orig r7794):  bricas | 2008-05-26 02:28:49 +0100
r19207@agaton (orig r7809):  bricas | 2008-05-27 01:40:53 +0100
r19208@agaton (orig r7810):  bricas | 2008-05-27 02:43:13 +0100
r19209@agaton (orig r7811):  bricas | 2008-05-27 03:42:11 +0100
r19473@agaton (orig r7857):  bricas | 2008-05-29 14:01:03 +0100
r20113@agaton (orig r7936):  bricas | 2008-06-20 19:14:11 +0100
r20115@agaton (orig r7938):  bricas | 2008-06-23 14:38:24 +0100
r20172@agaton (orig r7995):  bricas | 2008-06-23 23:01:06 +0100
r20173@agaton (orig r7996):  bricas | 2008-06-24 01:14:21 +0100
r20226@agaton (orig r8000):  marcus | 2008-06-25 20:08:09 +0100
r20228@agaton (orig r8002):  marcus | 2008-06-25 21:16:15 +0100
r20229@agaton (orig r8003):  marcus | 2008-06-25 21:38:36 +0100
r20232@agaton (orig r8006):  marcus | 2008-06-26 07:48:13 +0100
r20237@agaton (orig r8011):  marcus | 2008-06-27 10:19:04 +0100
r20281@agaton (orig r8012):  bricas | 2008-06-27 13:06:54 +0100
r20374@agaton (orig r8053):  bricas | 2008-06-30 13:19:10 +0100
r20375@agaton (orig r8054):  bricas | 2008-06-30 15:58:05 +0100
r20378@agaton (orig r8057):  bricas | 2008-06-30 17:25:16 +0100
r20710@agaton (orig r8106):  t0m | 2008-07-12 13:02:32 +0100
r20718@agaton (orig r8114):  bricas | 2008-07-14 19:20:11 +0100
r20835@agaton (orig r8124):  marcus | 2008-07-16 18:13:20 +0100
r20836@agaton (orig r8125):  bricas | 2008-07-16 18:23:53 +0100
r20837@agaton (orig r8126):  marcus | 2008-07-16 18:32:35 +0100
r20839@agaton (orig r8128):  bricas | 2008-07-17 01:37:48 +0100
r20845@agaton (orig r8134):  bricas | 2008-07-17 16:33:07 +0100
r20846@agaton (orig r8135):  bricas | 2008-07-18 01:01:14 +0100
r20847@agaton (orig r8136):  bricas | 2008-07-18 02:21:07 +0100
r20858@agaton (orig r8138):  bricas | 2008-07-18 12:23:28 +0100
r20860@agaton (orig r8140):  bricas | 2008-07-18 13:29:44 +0100
r20861@agaton (orig r8141):  bricas | 2008-07-18 14:20:02 +0100
r21095@agaton (orig r8143):  marcus | 2008-07-20 09:11:37 +0100
r21101@agaton (orig r8149):  dandv | 2008-07-22 22:26:55 +0100
r21103@agaton (orig r8151):  dandv | 2008-07-23 11:56:29 +0100
r21113@agaton (orig r8161):  marcus | 2008-07-25 07:56:31 +0100
r21254@agaton (orig r8175):  ash | 2008-08-01 17:58:25 +0100
r21391@agaton (orig r8185):  castaway | 2008-08-05 21:21:25 +0100
r21400@agaton (orig r8194):  marcus | 2008-08-06 21:19:47 +0100
r21401@agaton (orig r8195):  bricas | 2008-08-07 12:51:10 +0100
r21404@agaton (orig r8196):  bricas | 2008-08-07 17:31:31 +0100
r21405@agaton (orig r8197):  bricas | 2008-08-08 15:08:12 +0100
r21439@agaton (orig r8199):  bricas | 2008-08-08 20:35:45 +0100
r21598@agaton (orig r8231):  matthewt | 2008-08-18 13:02:54 +0100
r23166@agaton (orig r8232):  gbjk | 2008-08-18 20:03:08 +0100
r23169@agaton (orig r8235):  zarquon | 2008-08-20 19:25:20 +0100
r23356@agaton (orig r8271):  rafl | 2008-08-24 20:31:44 +0100
r23357@agaton (orig r8272):  rafl | 2008-08-24 20:31:53 +0100
r23358@agaton (orig r8273):  rafl | 2008-08-24 20:32:00 +0100
r23359@agaton (orig r8274):  rafl | 2008-08-24 20:32:08 +0100
r23360@agaton (orig r8275):  rafl | 2008-08-24 20:32:14 +0100
r23361@agaton (orig r8276):  rafl | 2008-08-24 20:32:21 +0100
r23362@agaton (orig r8277):  rafl | 2008-08-24 20:32:27 +0100
r23363@agaton (orig r8278):  rafl | 2008-08-24 20:32:34 +0100
r23367@agaton (orig r8282):  rafl | 2008-08-25 18:28:56 +0100
r23380@agaton (orig r8295):  rafl | 2008-08-26 16:20:04 +0100

70 files changed:
Changes
Makefile.PL
lib/Catalyst.pm
lib/Catalyst/Action.pm
lib/Catalyst/ActionChain.pm
lib/Catalyst/ActionContainer.pm
lib/Catalyst/AttrContainer.pm
lib/Catalyst/Base.pm
lib/Catalyst/Build.pm [deleted file]
lib/Catalyst/Component.pm
lib/Catalyst/Controller.pm
lib/Catalyst/DispatchType.pm
lib/Catalyst/DispatchType/Chained.pm
lib/Catalyst/DispatchType/Default.pm
lib/Catalyst/DispatchType/Index.pm
lib/Catalyst/DispatchType/Path.pm
lib/Catalyst/DispatchType/Regex.pm
lib/Catalyst/Dispatcher.pm
lib/Catalyst/Engine.pm
lib/Catalyst/Engine/CGI.pm
lib/Catalyst/Engine/FastCGI.pm
lib/Catalyst/Engine/HTTP.pm
lib/Catalyst/Engine/HTTP/Restarter.pm
lib/Catalyst/Engine/HTTP/Restarter/Watcher.pm
lib/Catalyst/Exception.pm
lib/Catalyst/Log.pm
lib/Catalyst/Manual.pm
lib/Catalyst/Model.pm
lib/Catalyst/Request.pm
lib/Catalyst/Request/Upload.pm
lib/Catalyst/Response.pm
lib/Catalyst/Runtime.pm
lib/Catalyst/Stats.pm
lib/Catalyst/Test.pm
lib/Catalyst/Utils.pm
lib/Catalyst/View.pm
script/catalyst.pl
t/c3_mro.t
t/custom_live_path_bug.t [new file with mode: 0644]
t/dead_load_multiple_chained_attributes.t [new file with mode: 0644]
t/lib/TestApp.pm
t/lib/TestApp/Controller/Action/Chained.pm
t/lib/TestApp/Controller/Action/Chained/ParentChain.pm
t/lib/TestApp/Controller/Action/Chained/ParentChain/Relative.pm [new file with mode: 0644]
t/lib/TestApp/Controller/Action/Chained/PathPrefix.pm [new file with mode: 0644]
t/lib/TestApp/Controller/Action/Forward.pm
t/lib/TestApp/Controller/Action/ForwardTo.pm [new file with mode: 0644]
t/lib/TestApp/Controller/Action/Go.pm [new file with mode: 0644]
t/lib/TestApp/Controller/Action/TestRelative.pm
t/lib/TestApp/Controller/Args.pm
t/lib/TestApp/Controller/Engine/Request/URI.pm
t/lib/TestApp/Controller/Root.pm
t/lib/TestAppPathBug.pm [new file with mode: 0644]
t/live_component_controller_action_chained.t
t/live_component_controller_action_forward.t
t/live_component_controller_action_go.t [new file with mode: 0644]
t/live_component_controller_action_path.t
t/live_component_controller_action_regexp.t
t/live_engine_request_parameters.t
t/live_engine_request_uploads.t
t/live_engine_request_uri.t
t/live_priorities.t
t/optional_http-server-restart.t
t/optional_http-server.t
t/something/script/foo/bar/for_dist [new file with mode: 0644]
t/unit_core_component.t
t/unit_core_mvc.t
t/unit_core_uri_for.t
t/unit_stats.t
t/unit_utils_subdir.t

diff --git a/Changes b/Changes
index 8903baa..0d2e92d 100644 (file)
--- a/Changes
+++ b/Changes
@@ -5,12 +5,62 @@
         - Added test for action stringify
         - Added test for component instances getting $self->{value} from config.
 
-5.7013
+5.7XXXXXX XXXX
+        - Fix some Win32 test failures
+        - Add pt translation of error message (wreis)
+        - Make :Chained('../action') work (Florian Ragwitz)
+
+5.7099_03 2008-07-20 10:10:00
+        - Fix regressions for regexp fallback in model(), view() and controller()
+        - Added the supplied argument to the regexp fallback warning for easier
+          debugging
+        - Ensure ACCEPT_CONTEXT is called for results from component()
+
+5.7099_02 2008-07-16 19:10:00
+        - Added PathPrefix attribute
+        - Removed Catalyst::Build; we've long since moved to Module::Install
+        - Updated Catalyst::Test docs to mention the use of HTTP::Request
+          objects (Rafael Kitover)
+
+5.7099_01 2008-06-25 22:36:00
+        - Refactored component resolution (component(), models(), model(), et al). We now
+          throw warnings for two reasons:
+          1) model() or view() was called with no arguments, and two results are returned
+             -- set default_(model|view), current_(model|view) or current_(model|view)_instance
+             instead
+          2) you call a component resolution method with a string, and it resorts to a regexp 
+             fallback wherein a result is returned -- if you really want to search, call the
+             method with a regex as the argument
+        - remove 0-length query string components so warnings aren't thrown (RT #36428)
+        - Update HTTP::Body dep so that the uploadtmp config value will work (RT #22540)
+        - Fix for LocalRegex when used in the Root controller
+        - Get some of the optional_* tests working from dirs with spaces (RT #26455)
+        - Fix Catalyst::Utils::home() when application .pm is in the current dir (RT #34437)
+        - Added the ability to remove parameters in req->uri_with() by passing in
+          an undef value (RT #34782)
+        - Added $c->go, to do an internal redispatch to another action, while retaining the
+          contents of the stash
+
+5.7014  2008-05-25 15:26:00
+        - Addition of .conf in restart regex in Catalyst::Engine::HTTP::Restarter::Watcher
+        - Fix regression for relative uri_for arguments after a forward()   
+          introduced in 5.7013 (Peter Karman)  
+        - Fix regression for "sub foo : Path {}" in the root controller which 
+          was introduced when attempting to allow "0" as a Path.
+
+5.7013  2008-05-16 18:20:00
+        - Provide backwards compatability methods in Catalyst::Stats
         - Fix subdirs for scripts that run in subdirs more than one level deep.
         - Added test and updated docs for handling the Authorization header
           under mod_fastcgi/mod_cgi.
         - Fixed bug in HTTP engine where the connection was not closed properly if the
           client disconnected before sending any headers. (Ton Voon)
+        - POD fix, IO::FileHandle => IO::Handle (RT #35690)
+        - Fix grammar on welcome page (RT #33236)
+        - Fix for Path('0') handling (RT #29334)
+        - Workaround for Win32 and c3_mro.t (RT #26452, tested by Kenichi Ishigaki)
+        - Fix for encoding query parameters (edenc)
+        - Fix Chained multiple test (t0m)
 
 5.7012  2007-12-16 23:44:00
         - Fix uri_for()'s and uri_with()'s handling of multibyte chars
@@ -32,7 +82,7 @@
         - Fixed bug in HTTP engine where writes could fail with
           'Resource temporarily unavailable'.
         - Fixed bug where %2b in query parameter is doubly decoded to ' ', instead of '+'
-          (Gavin Henry, Tatsuhiko Miyagawa, Oleg Pronin)
+          (RT #30087, Gavin Henry, Tatsuhiko Miyagawa, Oleg Pronin)
         - Fixed bug where req->base and req->uri would include a port number when running
           in SSL mode.
         - Removed unnecessary sprintf in debug mode that caused warnings on locales where
index 7c59dfb..00470d7 100644 (file)
@@ -1,7 +1,6 @@
 use inc::Module::Install 0.64;
 
-use 5.008001; 
-perl_version '5.8.1';
+perl_version '5.008001';
 
 name 'Catalyst-Runtime';
 all_from 'lib/Catalyst/Runtime.pm';
@@ -16,7 +15,7 @@ requires 'CGI::Simple::Cookie';
 requires 'Data::Dump';
 requires 'File::Modified';
 requires 'HTML::Entities';
-requires 'HTTP::Body'    => '0.9';
+requires 'HTTP::Body'    => '1.04'; # makes uploadtmp work
 requires 'HTTP::Headers' => '1.64';
 requires 'HTTP::Request';
 requires 'HTTP::Response';
index 0f70e5e..8eb5838 100644 (file)
@@ -39,8 +39,6 @@ has request => (is => 'rw', default => sub { $_[0]->request_class->new({}) }, re
 has response => (is => 'rw', default => sub { $_[0]->response_class->new({}) }, required => 1, lazy => 1);
 has namespace => (is => 'rw');
 
-attributes->import( __PACKAGE__, \&namespace, 'lvalue' );
-
 sub depth { scalar @{ shift->stack || [] }; }
 sub comp { shift->component(@_) }
 
@@ -61,6 +59,7 @@ our $COUNT     = 1;
 our $START     = time;
 our $RECURSION = 1000;
 our $DETACH    = "catalyst_detach\n";
+our $GO        = "catalyst_go\n";
 
 #I imagine that very few of these really need to be class variables. if any.
 #maybe we should just make them attributes with a default?
@@ -77,7 +76,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.7013';
+our $VERSION = '5.7099_03';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -117,7 +116,7 @@ documentation and tutorials.
     catalyst.pl MyApp
 
     # add models, views, controllers
-    script/myapp_create.pl model MyDatabase DBIC::Schema create=dynamic dbi:SQLite:/path/to/db
+    script/myapp_create.pl model MyDatabase DBIC::Schema create=static dbi:SQLite:/path/to/db
     script/myapp_create.pl view MyTemplate TT
     script/myapp_create.pl controller Search
 
@@ -347,6 +346,20 @@ When called with no arguments it escapes the processing chain entirely.
 
 sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
 
+=head2 $c->go( $action [, \@arguments ] )
+
+=head2 $c->go( $class, $method, [, \@arguments ] )
+
+Almost the same as C<detach>, but does a full dispatch, instead of just
+calling the new C<$action> / C<$class-E<gt>$method>. This means that C<begin>,
+C<auto> and the method you go to is called, just like a new request.
+
+C<$c-E<gt>stash> is kept unchanged.
+
+=cut
+
+sub go { my $c = shift; $c->dispatcher->go( $c, @_ ) }
+
 =head2 $c->response
 
 =head2 $c->res
@@ -438,87 +451,66 @@ sub clear_errors {
     $c->error(0);
 }
 
+# search components given a name and some prefixes
+sub _comp_search_prefixes {
+    my ( $c, $name, @prefixes ) = @_;
+    my $appclass = ref $c || $c;
+    my $filter   = "^${appclass}::(" . join( '|', @prefixes ) . ')::';
 
-# search via regex
-sub _comp_search {
-    my ( $c, @names ) = @_;
-
-    foreach my $name (@names) {
-        foreach my $component ( keys %{ $c->components } ) {
-            return $c->components->{$component} if $component =~ /$name/i;
-        }
-    }
+    # map the original component name to the sub part that we will search against
+    my %eligible = map { my $n = $_; $n =~ s{^$appclass\::[^:]+::}{}; $_ => $n; }
+        grep { /$filter/ } keys %{ $c->components };
 
-    return undef;
-}
+    # undef for a name will return all
+    return keys %eligible if !defined $name;
 
-# try explicit component names
-sub _comp_explicit {
-    my ( $c, @names ) = @_;
+    my $query  = ref $name ? $name : qr/^$name$/i;
+    my @result = grep { $eligible{$_} =~ m{$query} } keys %eligible;
 
-    foreach my $try (@names) {
-        return $c->components->{$try} if ( exists $c->components->{$try} );
-    }
+    return map { $c->components->{ $_ } } @result if @result;
 
-    return undef;
-}
+    # if we were given a regexp to search against, we're done.
+    return if ref $name;
 
-# like component, but try just these prefixes before regex searching,
-#  and do not try to return "sort keys %{ $c->components }"
-sub _comp_prefixes {
-    my ( $c, $name, @prefixes ) = @_;
+    # regexp fallback
+    $query  = qr/$name/i;
+    @result = map { $c->components->{ $_ } } grep { $eligible{ $_ } =~ m{$query} } keys %eligible;
 
-    my $appclass = ref $c || $c;
+    # no results? try against full names
+    if( !@result ) {
+        @result = map { $c->components->{ $_ } } grep { m{$query} } keys %eligible;
+    }
 
-    my @names = map { "${appclass}::${_}::${name}" } @prefixes;
+    # don't warn if we didn't find any results, it just might not exist
+    if( @result ) {
+        $c->log->warn( qq(Found results for "${name}" using regexp fallback.) );
+        $c->log->warn( 'Relying on the regexp fallback behavior for component resolution is unreliable and unsafe.' );
+        $c->log->warn( 'If you really want to search, pass in a regexp as the argument.' );
+    }
 
-    my $comp = $c->_comp_explicit(@names);
-    return $comp if defined($comp);
-    $comp = $c->_comp_search($name);
-    return $comp;
+    return @result;
 }
 
 # Find possible names for a prefix 
-
 sub _comp_names {
     my ( $c, @prefixes ) = @_;
-
     my $appclass = ref $c || $c;
 
-    my @pre = map { "${appclass}::${_}::" } @prefixes;
-
-    my @names;
-
-    COMPONENT: foreach my $comp ($c->component) {
-        foreach my $p (@pre) {
-            if ($comp =~ s/^$p//) {
-                push(@names, $comp);
-                next COMPONENT;
-            }
-        }
-    }
+    my $filter = "^${appclass}::(" . join( '|', @prefixes ) . ')::';
 
+    my @names = map { s{$filter}{}; $_; } $c->_comp_search_prefixes( undef, @prefixes );
     return @names;
 }
 
-# Return a component if only one matches.
-sub _comp_singular {
-    my ( $c, @prefixes ) = @_;
-
-    my $appclass = ref $c || $c;
-
-    my ( $comp, $rest ) =
-      map { $c->_comp_search("^${appclass}::${_}::") } @prefixes;
-    return $comp unless $rest;
-}
-
 # Filter a component before returning by calling ACCEPT_CONTEXT if available
 sub _filter_component {
     my ( $c, $comp, @args ) = @_;
+
     if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
         return $comp->ACCEPT_CONTEXT( $c, @args );
     }
-    else { return $comp }
+    
+    return $comp;
 }
 
 =head2 COMPONENT ACCESSORS
@@ -532,13 +524,23 @@ Gets a L<Catalyst::Controller> instance by name.
 If the name is omitted, will return the controller for the dispatched
 action.
 
+If you want to search for controllers, pass in a regexp as the argument.
+
+    # find all controllers that start with Foo
+    my @foo_controllers = $c->controller(qr{^Foo});
+
+
 =cut
 
 sub controller {
     my ( $c, $name, @args ) = @_;
-    return $c->_filter_component( $c->_comp_prefixes( $name, qw/Controller C/ ),
-        @args )
-      if ($name);
+
+    if( $name ) {
+        my @result = $c->_comp_search_prefixes( $name, qw/Controller C/ );
+        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
+        return $c->_filter_component( $result[ 0 ], @args );
+    }
+
     return $c->component( $c->action->class );
 }
 
@@ -551,18 +553,27 @@ Gets a L<Catalyst::Model> instance by name.
 Any extra arguments are directly passed to ACCEPT_CONTEXT.
 
 If the name is omitted, it will look for 
- - a model object in $c->stash{current_model_instance}, then
+ - a model object in $c->stash->{current_model_instance}, then
  - a model name in $c->stash->{current_model}, then
  - a config setting 'default_model', or
  - check if there is only one model, and return it if that's the case.
 
+If you want to search for models, pass in a regexp as the argument.
+
+    # find all models that start with Foo
+    my @foo_models = $c->model(qr{^Foo});
+
 =cut
 
 sub model {
     my ( $c, $name, @args ) = @_;
-    return $c->_filter_component( $c->_comp_prefixes( $name, qw/Model M/ ),
-        @args )
-      if $name;
+
+    if( $name ) {
+        my @result = $c->_comp_search_prefixes( $name, qw/Model M/ );
+        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
+        return $c->_filter_component( $result[ 0 ], @args );
+    }
+
     if (ref $c) {
         return $c->stash->{current_model_instance} 
           if $c->stash->{current_model_instance};
@@ -571,19 +582,18 @@ sub model {
     }
     return $c->model( $c->config->{default_model} )
       if $c->config->{default_model};
-    return $c->_filter_component( $c->_comp_singular(qw/Model M/) );
-
-}
-
-=head2 $c->controllers
 
-Returns the available names which can be passed to $c->controller
+    my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/Model M/);
 
-=cut
+    if( $rest ) {
+        $c->log->warn( 'Calling $c->model() will return a random model unless you specify one of:' );
+        $c->log->warn( '* $c->config->{default_model} # the name of the default model to use' );
+        $c->log->warn( '* $c->stash->{current_model} # the name of the model to use for this request' );
+        $c->log->warn( '* $c->stash->{current_model_instance} # the instance of the model to use for this request' );
+        $c->log->warn( 'NB: in version 5.80, the "random" behavior will not work at all.' );
+    }
 
-sub controllers {
-    my ( $c ) = @_;
-    return $c->_comp_names(qw/Controller C/);
+    return $c->_filter_component( $comp );
 }
 
 
@@ -596,18 +606,27 @@ Gets a L<Catalyst::View> instance by name.
 Any extra arguments are directly passed to ACCEPT_CONTEXT.
 
 If the name is omitted, it will look for 
- - a view object in $c->stash{current_view_instance}, then
+ - a view object in $c->stash->{current_view_instance}, then
  - a view name in $c->stash->{current_view}, then
  - a config setting 'default_view', or
  - check if there is only one view, and return it if that's the case.
 
+If you want to search for views, pass in a regexp as the argument.
+
+    # find all views that start with Foo
+    my @foo_views = $c->view(qr{^Foo});
+
 =cut
 
 sub view {
     my ( $c, $name, @args ) = @_;
-    return $c->_filter_component( $c->_comp_prefixes( $name, qw/View V/ ),
-        @args )
-      if $name;
+
+    if( $name ) {
+        my @result = $c->_comp_search_prefixes( $name, qw/View V/ );
+        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
+        return $c->_filter_component( $result[ 0 ], @args );
+    }
+
     if (ref $c) {
         return $c->stash->{current_view_instance} 
           if $c->stash->{current_view_instance};
@@ -616,7 +635,29 @@ sub view {
     }
     return $c->view( $c->config->{default_view} )
       if $c->config->{default_view};
-    return $c->_filter_component( $c->_comp_singular(qw/View V/) );
+
+    my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/View V/);
+
+    if( $rest ) {
+        $c->log->warn( 'Calling $c->view() will return a random view unless you specify one of:' );
+        $c->log->warn( '* $c->config->{default_view} # the name of the default view to use' );
+        $c->log->warn( '* $c->stash->{current_view} # the name of the view to use for this request' );
+        $c->log->warn( '* $c->stash->{current_view_instance} # the instance of the view to use for this request' );
+        $c->log->warn( 'NB: in version 5.80, the "random" behavior will not work at all.' );
+    }
+
+    return $c->_filter_component( $comp );
+}
+
+=head2 $c->controllers
+
+Returns the available names which can be passed to $c->controller
+
+=cut
+
+sub controllers {
+    my ( $c ) = @_;
+    return $c->_comp_names(qw/Controller C/);
 }
 
 =head2 $c->models
@@ -651,35 +692,52 @@ unless you want to get a specific component by full
 class. C<< $c->controller >>, C<< $c->model >>, and C<< $c->view >>
 should be used instead.
 
+If C<$name> is a regexp, a list of components matched against the full
+component name will be returned.
+
 =cut
 
 sub component {
-    my $c = shift;
+    my ( $c, $name, @args ) = @_;
 
-    if (@_) {
+    if( $name ) {
+        my $comps = $c->components;
 
-        my $name = shift;
+        if( !ref $name ) {
+            # is it the exact name?
+            return $c->_filter_component( $comps->{ $name }, @args )
+                       if exists $comps->{ $name };
 
-        my $appclass = ref $c || $c;
+            # perhaps we just omitted "MyApp"?
+            my $composed = ( ref $c || $c ) . "::${name}";
+            return $c->_filter_component( $comps->{ $composed }, @args )
+                       if exists $comps->{ $composed };
 
-        my @names = (
-            $name, "${appclass}::${name}",
-            map { "${appclass}::${_}::${name}" }
-              qw/Model M Controller C View V/
-        );
+            # search all of the models, views and controllers
+            my( $comp ) = $c->_comp_search_prefixes( $name, qw/Model M Controller C View V/ );
+            return $c->_filter_component( $comp, @args ) if $comp;
+        }
+
+        # This is here so $c->comp( '::M::' ) works
+        my $query = ref $name ? $name : qr{$name}i;
 
-        my $comp = $c->_comp_explicit(@names);
-        return $c->_filter_component( $comp, @_ ) if defined($comp);
+        my @result = grep { m{$query} } keys %{ $c->components };
+        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
 
-        $comp = $c->_comp_search($name);
-        return $c->_filter_component( $comp, @_ ) if defined($comp);
+        if( $result[ 0 ] ) {
+            $c->log->warn( qq(Found results for "${name}" using regexp fallback.) );
+            $c->log->warn( 'Relying on the regexp fallback behavior for component resolution' );
+            $c->log->warn( 'is unreliable and unsafe. You have been warned' );
+            return $c->_filter_component( $result[ 0 ], @args );
+        }
+
+        # I would expect to return an empty list here, but that breaks back-compat
     }
 
+    # fallback
     return sort keys %{ $c->components };
 }
 
-
-
 =head2 CLASS DATA AND HELPER CLASSES
 
 =head2 $c->config
@@ -998,10 +1056,10 @@ sub uri_for {
     if (my @keys = keys %$params) {
       # somewhat lifted from URI::_query's query_form
       $query = '?'.join('&', map {
+          my $val = $params->{$_};
           s/([;\/?:@&=+,\$\[\]%])/$URI::Escape::escapes{$1}/go;
           s/ /+/g;
           my $key = $_;
-          my $val = $params->{$_};
           $val = '' unless defined $val;
           (map {
               $_ = "$_";
@@ -1120,7 +1178,7 @@ sub welcome_message {
                  <p>That really depends  on what <b>you</b> want to do.
                     We do, however, provide you with a few starting points.</p>
                  <p>If you want to jump right into web development with Catalyst
-                    you might want want to start with a tutorial.</p>
+                    you might want to start with a tutorial.</p>
 <pre>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst-Manual/lib/Catalyst/Manual/Tutorial.pod">Catalyst::Manual::Tutorial</a></code>
 </pre>
 <p>Afterwards you can go on to check out a more complete look at our features.</p>
@@ -1250,7 +1308,12 @@ sub execute {
     my $last = pop( @{ $c->stack } );
 
     if ( my $error = $@ ) {
-        if ( !ref($error) and $error eq $DETACH ) { die $DETACH if $c->depth > 1 }
+        if ( !ref($error) and $error eq $DETACH ) {
+            die $DETACH if($c->depth > 1);
+        }
+        elsif ( !ref($error) and $error eq $GO ) {
+            die $GO if($c->depth > 0);
+        }
         else {
             unless ( ref $error ) {
                 no warnings 'uninitialized';
@@ -1610,7 +1673,8 @@ sub prepare {
     }
 
     my $method  = $c->req->method  || '';
-    my $path    = $c->req->path    || '/';
+    my $path    = $c->req->path;
+    $path       = '/' unless length $path;
     my $address = $c->req->address || '';
 
     $c->log->debug(qq/"$method" request for "$path" from "$address"/)
@@ -1862,6 +1926,11 @@ search paths, specify a key named C<search_extra> as an array
 reference. Items in the array beginning with C<::> will have the
 application class name prepended to them.
 
+All components found will also have any 
+L<Devel::InnerPackage|inner packages> loaded and set up as components.
+Note, that modules which are B<not> an I<inner package> of the main
+file namespace loaded will not be instantiated as components.
+
 =cut
 
 sub setup_components {
@@ -2366,13 +2435,15 @@ Wiki:
 
 =head2 L<Catalyst::Test> - The test suite.
 
-=head1 CREDITS
+=head1 PROJECT FOUNDER
+
+sri: Sebastian Riedel <sri@cpan.org>
 
-Andy Grundman
+=head1 CONTRIBUTORS
 
-Andy Wardley
+abw: Andy Wardley
 
-Andreas Marienborg
+acme: Leon Brocard <leon@astray.com>
 
 Andrew Bramble
 
@@ -2380,65 +2451,67 @@ Andrew Ford
 
 Andrew Ruthven
 
-Arthur Bergman
+andyg: Andy Grundman <andy@hybridized.org>
 
-Autrijus Tang
+audreyt: Audrey Tang
 
-Brian Cassidy
+bricas: Brian Cassidy <bricas@cpan.org>
 
-Carl Franks
+chansen: Christian Hansen
 
-Christian Hansen
+chicks: Christopher Hicks
 
-Christopher Hicks
+dkubb: Dan Kubb <dan.kubb-cpan@onautopilot.com>
 
-Dan Sully
-
-Danijel Milicevic
+Drew Taylor
 
-David Kamholz
+esskar: Sascha Kiefer
 
-David Naughton
+fireartist: Carl Franks <cfranks@cpan.org>
 
-Drew Taylor
+gabb: Danijel Milicevic
 
 Gary Ashton Jones
 
 Geoff Richards
 
-Jesse Sheidlower
-
-Jesse Vincent
+jcamacho: Juan Camacho
 
 Jody Belka
 
 Johan Lindstrom
 
-Juan Camacho
+jon: Jon Schutz <jjschutz@cpan.org>
 
-Leon Brocard
+marcus: Marcus Ramberg <mramberg@cpan.org>
 
-Marcus Ramberg
+miyagawa: Tatsuhiko Miyagawa <miyagawa@bulknews.net>
 
-Matt S Trout
+mst: Matt S. Trout <mst@shadowcatsystems.co.uk>
 
-Robert Sedlacek
+mugwump: Sam Vilain
 
-Sam Vilain
+naughton: David Naughton
 
-Sascha Kiefer
+ningu: David Kamholz <dkamholz@cpan.org>
 
-Sebastian Willert
+nothingmuch: Yuval Kogman <nothingmuch@woobling.org>
 
-Tatsuhiko Miyagawa
+numa: Dan Sully <daniel@cpan.org>
 
-Ulf Edvinsson
+obra: Jesse Vincent
+
+omega: Andreas Marienborg
 
-Yuval Kogman
+phaylon: Robert Sedlacek <phaylon@dunkelheit.at>
 
-=head1 AUTHOR
+sky: Arthur Bergman
+
+the_jester: Jesse Sheidlower
+
+Ulf Edvinsson
 
-Sebastian Riedel, C<sri@oook.de>
+willert: Sebastian Willert <willert@cpan.org>
 
 =head1 LICENSE
 
index 1a23b16..c3abb5b 100644 (file)
@@ -129,9 +129,9 @@ returns the sub name of this action.
 
 Provided by Moose
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S. Trout
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index d9fb623..1518802 100644 (file)
@@ -71,9 +71,9 @@ Catalyst::ActionChain object representing a chain of these actions
 
 Provided by Moose
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S. Trout
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index f3cb7e0..bb1bbac 100644 (file)
@@ -74,9 +74,9 @@ stringifies to.
 
 Provided by Moose
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S. Trout
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 764c460..921a886 100644 (file)
@@ -23,7 +23,7 @@ sub FETCH_CODE_ATTRIBUTES { $_[0]->_attr_cache->{ $_[1] } || () }
 
 =head1 NAME
 
-Catalyst::AttrContainer
+Catalyst::AttrContainer - Handles code attribute storage and caching
 
 =head1 SYNOPSIS
 
@@ -47,10 +47,9 @@ Attribute function. See attributes(3pm)
 L<Catalyst::Dispatcher>
 L<Catalyst>.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-Marcus Ramberg, C<mramberg@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 42e70db..c368fa8 100644 (file)
@@ -21,11 +21,9 @@ remains here for compability reasons.
 
 L<Catalyst>, L<Catalyst::Controller>.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-Marcus Ramberg, C<mramberg@cpan.org>
-Matt S Trout, C<mst@shadowcatsystems.co.uk>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
diff --git a/lib/Catalyst/Build.pm b/lib/Catalyst/Build.pm
deleted file mode 100644 (file)
index 944d9e8..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-package Catalyst::Build;
-
-use strict;
-use Module::Build;
-use Path::Class;
-use File::Find 'find';
-
-our @ISA;
-eval "require Module::Build";
-die "Please install Module::Build\n" if $@;
-push @ISA, 'Module::Build';
-
-our @ignore =
-  qw/Build Build.PL Changes MANIFEST META.yml Makefile.PL Makefile README
-  _build blib lib script t/;
-
-our $FAKE;
-our $ignore = '^(' . join( '|', @ignore ) . ')$';
-
-=head1 NAME
-
-Catalyst::Build - Module::Build extension for Catalyst
-
-=head1 SYNOPSIS
-
-See L<Catalyst>
-
-=head1 DESCRIPTION
-
-L<Module::Build> extension for Catalyst.
-
-=head1 DEPRECATION NOTICE
-
-This module is deprecated in favor of L<Module::Install::Catalyst>. It's
-only left here for compability with older applications.
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-=cut
-
-sub new {
-    my $class = shift;
-    my $self  = $class->SUPER::new(@_);
-
-    my $app_name = $self->{properties}{module_name};
-    warn <<"EOF";
-
- Note:
-
-    The use of Build.PL for building and distributing Catalyst
-    applications is deprecated in Catalyst 5.58.
-
-    We recommend using the new Module::Install-based Makefile
-    system.  You can generate a new Makefile.PL for your application
-    by running:
-
-        catalyst.pl -force -makefile $app_name
-
-EOF
-
-    return $self;
-}
-
-=item ACTION_install
-
-=cut
-
-sub ACTION_install {
-    my $self = shift;
-    $self->SUPER::ACTION_install;
-    $self->ACTION_install_extras;
-}
-
-=item ACTION_fakeinstall
-
-=cut
-
-sub ACTION_fakeinstall {
-    my $self = shift;
-    $self->SUPER::ACTION_fakeinstall;
-    local $FAKE = 1;
-    $self->ACTION_install_extras;
-}
-
-=item ACTION_install_extras
-
-=cut
-
-sub ACTION_install_extras {
-    my $self    = shift;
-    my $prefix  = $self->{properties}{destdir} || undef;
-    my $sitelib = $self->install_destination('lib');
-    my @path    = defined $prefix ? ( $prefix, $sitelib ) : ($sitelib);
-    my $path    = dir( @path, split( '::', $self->{properties}{module_name} ) );
-    my @files   = $self->_find_extras;
-    print "Installing extras to $path\n";
-    for (@files) {
-        $FAKE
-          ? print "$_ -> $path (FAKE)\n"
-          : $self->copy_if_modified( $_, $path );
-    }
-}
-
-sub _find_extras {
-    my $self = shift;
-    my @all  = glob '*';
-    my @files;
-    for my $file (@all) {
-        next if $file =~ /$ignore/;
-        if ( -d $file ) {
-            find(
-                sub {
-                    return if -d;
-                    push @files, $File::Find::name;
-                },
-                $file
-            );
-        }
-        else { push @files, $file }
-    }
-    return @files;
-}
-
-=back
-
-=head1 AUTHOR
-
-Sebastian Riedel, C<sri@oook.de>
-
-=head1 LICENSE
-
-This library is free software, you can redistribute it and/or modify it under
-the same terms as Perl itself.
-
-=cut
-
-1;
index 1f1fbb5..176769c 100644 (file)
@@ -183,11 +183,9 @@ calling code in the application rather than the component itself.
 
 L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-Marcus Ramberg, C<mramberg@cpan.org>
-Matt S Trout, C<mst@shadowcatsystems.co.uk>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 256b2c3..17b5c52 100644 (file)
@@ -107,7 +107,7 @@ sub _ACTION : Private {
     my ( $self, $c ) = @_;
     if (   ref $c->action
         && $c->action->can('execute')
-        && $c->req->action )
+        && defined $c->req->action )
     {
         $c->action->dispatch( $c );
     }
@@ -315,7 +315,7 @@ sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
 
 sub _parse_Path_attr {
     my ( $self, $c, $name, $value ) = @_;
-    $value ||= '';
+    $value = '' if !defined $value;
     if ( $value =~ m!^/! ) {
         return ( 'Path', $value );
     }
@@ -337,11 +337,52 @@ sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
 sub _parse_LocalRegex_attr {
     my ( $self, $c, $name, $value ) = @_;
     unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
-    return ( 'Regex', '^' . $self->path_prefix($c) . "/${value}" );
+
+    my $prefix = $self->path_prefix( $c );
+    $prefix .= '/' if length( $prefix );
+   
+    return ( 'Regex', "^${prefix}${value}" );
 }
 
 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
 
+sub _parse_Chained_attr {
+    my ($self, $c, $name, $value) = @_;
+
+    if (defined($value) && length($value)) {
+        if ($value eq '.') {
+            $value = '/'.$self->action_namespace($c);
+        } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
+            my @parts = split '/', $self->action_namespace($c);
+            my @levels = split '/', $rel;
+
+            $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
+        } elsif ($value !~ m/^\//) {
+            my $action_ns = $self->action_namespace($c);
+
+            if ($action_ns) {
+                $value = '/'.join('/', $action_ns, $value);
+            } else {
+                $value = '/'.$value; # special case namespace '' (root)
+            }
+        }
+    } else {
+        $value = '/'
+    }
+
+    return Chained => $value;
+}
+
+sub _parse_ChainedParent_attr {
+    my ($self, $c, $name, $value) = @_;
+    return $self->_parse_Chained_attr($c, $name, '../'.$name);
+}
+
+sub _parse_PathPrefix_attr {
+    my $self = shift;
+    return PathPart => $self->path_prefix;
+}
+
 sub _parse_ActionClass_attr {
     my ( $self, $c, $name, $value ) = @_;
     unless ( $value =~ s/^\+// ) {
@@ -413,8 +454,8 @@ overridden from the "namespace" config key.
 
 =head2 $self->path_prefix($c)
 
-Returns the default path prefix for :Local, :LocalRegex and relative
-:Path actions in this component. Defaults to the action_namespace or
+Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
+relative :Path actions in this component. Defaults to the action_namespace or
 can be overridden from the "path" config key.
 
 =head2 $self->create_action(%args)
@@ -430,10 +471,9 @@ Primarily designed for the use of register_actions.
 
 Returns the application instance stored by C<new()>
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@oook.de>
-Marcus Ramberg C<mramberg@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index d763eb6..31b980c 100644 (file)
@@ -58,10 +58,9 @@ arrayref, or undef if unable to do so.
 
 sub uri_for_action { }
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S Trout
-Sebastian Riedel, C<sri@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 2e45b29..cde26e1 100644 (file)
@@ -182,7 +182,14 @@ sub recurse_match {
                 my ($actions, $captures, $action_parts) = $self->recurse_match(
                                              $c, '/'.$action->reverse, \@parts
                                            );
-                if ($actions && (!$best_action || $#$action_parts < $#{$best_action->{parts}})){
+                #    No best action currently
+                # OR The action has less parts
+                # OR The action has equal parts but less captured data (ergo more defined)
+                if ($actions    &&
+                    (!$best_action                                 ||
+                     $#$action_parts < $#{$best_action->{parts}}   ||
+                     ($#$action_parts == $#{$best_action->{parts}} &&
+                      $#$captures < $#{$best_action->{captures}}))){
                     $best_action = {
                         actions => [ $action, @$actions ],
                         captures=> [ @captures, @$captures ],
@@ -233,31 +240,13 @@ sub register {
 
     return 0 unless @chained_attr;
 
-    if (@chained_attr > 2) {
+    if (@chained_attr > 1) {
         Catalyst::Exception->throw(
           "Multiple Chained attributes not supported registering ${action}"
         );
     }
 
-    my $parent = $chained_attr[0];
-
-    if (defined($parent) && length($parent)) {
-        if ($parent eq '.') {
-            $parent = '/'.$action->namespace;
-        } elsif ($parent !~ m/^\//) {
-            if ($action->namespace) {
-                $parent = '/'.join('/', $action->namespace, $parent);
-            } else {
-                $parent = '/'.$parent; # special case namespace '' (root)
-            }
-        }
-    } else {
-        $parent = '/'
-    }
-
-    $action->attributes->{Chained} = [ $parent ];
-
-    my $children = ($self->_children_of->{$parent} ||= {});
+    my $children = ($self->{children_of}{ $chained_attr[0] } ||= {});
 
     my @path_part = @{ $action->attributes->{PathPart} || [] };
 
@@ -582,9 +571,9 @@ The C<forward>ing to other actions does just what you would expect. But if
 you C<detach> out of a chain, the rest of the chain will not get called
 after the C<detach>.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S Trout <mst@shadowcatsystems.co.uk>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index ecc6e38..2fc3bc9 100644 (file)
@@ -47,10 +47,9 @@ sub match {
     return 0;
 }
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S Trout
-Sebastian Riedel, C<sri@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index b6b649b..610e0a4 100644 (file)
@@ -56,9 +56,9 @@ sub uri_for_action {
     return "/".$action->namespace;
 }
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index fd65523..93d300a 100644 (file)
@@ -57,7 +57,7 @@ first action that matches, if any; if not, returns 0.
 sub match {
     my ( $self, $c, $path ) = @_;
 
-    $path ||= '/';
+    $path = '/' if !defined $path || !length $path;
 
     foreach my $action ( @{ $self->_paths->{$path} || [] } ) {
         next unless $action->match($c);
@@ -128,10 +128,9 @@ sub uri_for_action {
     }
 }
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S Trout
-Sebastian Riedel, C<sri@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index f6d0606..4d8d28a 100644 (file)
@@ -149,10 +149,9 @@ sub uri_for_action {
     return undef;
 }
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S Trout
-Sebastian Riedel, C<sri@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 7c5134b..4cad8c7 100644 (file)
@@ -120,17 +120,15 @@ sub dispatch {
     }
 }
 
-=head2 $self->forward( $c, $command [, \@arguments ] )
-
-Documented in L<Catalyst>
-
-=cut
+# $self->_command2action( $c, $command [, \@arguments ] )
+# Search for an action, from the command and returns C<($action, $args)> on
+# success. Returns C<(0)> on error.
 
-sub forward {
+sub _command2action {
     my ( $self, $c, $command, @extra_params ) = @_;
 
     unless ($command) {
-        $c->log->debug('Nothing to forward to') if $c->debug;
+        $c->log->debug('Nothing to go to') if $c->debug;
         return 0;
     }
 
@@ -139,21 +137,65 @@ sub forward {
     if ( ref( $extra_params[-1] ) eq 'ARRAY' ) {
         @args = @{ pop @extra_params }
     } else {
-        # this is a copy, it may take some abuse from ->_invoke_as_path if the path had trailing parts
+        # this is a copy, it may take some abuse from
+        # ->_invoke_as_path if the path had trailing parts
         @args = @{ $c->request->arguments };
     }
 
     my $action;
 
-    # forward to a string path ("/foo/bar/gorch") or action object which stringifies to that
+    # go to a string path ("/foo/bar/gorch")
+    # or action object which stringifies to that
     $action = $self->_invoke_as_path( $c, "$command", \@args );
 
-    # forward to a component ( "MyApp::*::Foo" or $c->component("...") - a path or an object)
+    # go to a component ( "MyApp::*::Foo" or $c->component("...")
+    # - a path or an object)
     unless ($action) {
         my $method = @extra_params ? $extra_params[0] : "process";
         $action = $self->_invoke_as_component( $c, $command, $method );
     }
 
+    return $action, \@args;
+}
+
+=head2 $self->go( $c, $command [, \@arguments ] )
+
+Documented in L<Catalyst>
+
+=cut
+
+sub go {
+    my $self = shift;
+    my ( $c, $command ) = @_;
+    my ( $action, $args ) = $self->_command2action(@_);
+
+    unless ($action && defined $action->namespace) {
+        my $error =
+            qq/Couldn't go to command "$command": /
+          . qq/Invalid action or component./;
+        $c->error($error);
+        $c->log->debug($error) if $c->debug;
+        return 0;
+    }
+
+    local $c->request->{arguments} = $args;
+    $c->namespace($action->namespace);
+    $c->action($action);
+    $self->dispatch($c);
+
+    die $Catalyst::GO;
+}
+
+=head2 $self->forward( $c, $command [, \@arguments ] )
+
+Documented in L<Catalyst>
+
+=cut
+
+sub forward {
+    my $self = shift;
+    my ( $c, $command ) = @_;
+    my ( $action, $args ) = $self->_command2action(@_);
 
     unless ($action) {
         my $error =
@@ -283,7 +325,7 @@ sub prepare_action {
     s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg for grep { defined } @{$req->captures||[]};
 
     $c->log->debug( 'Path is "' . $req->match . '"' )
-      if ( $c->debug && $req->match );
+      if ( $c->debug && length $req->match );
 
     $c->log->debug( 'Arguments are "' . join( '/', @args ) . '"' )
       if ( $c->debug && @args );
@@ -299,7 +341,7 @@ sub get_action {
     my ( $self, $name, $namespace ) = @_;
     return unless $name;
 
-    $namespace = join( "/", grep { length } split '/', $namespace || "" );
+    $namespace = join( "/", grep { length } split '/', ( defined $namespace ? $namespace : "" ) );
 
     return $self->_action_hash->{"${namespace}/${name}"};
 }
@@ -531,10 +573,9 @@ __PACKAGE__->meta->make_immutable;
 
 Provided by Moose
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-Matt S Trout, C<mst@shadowcatsystems.co.uk>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 814238d..c0cdf1e 100644 (file)
@@ -156,6 +156,7 @@ EOF
 (no) Vennligst prov igjen senere
 (dk) Venligst prov igen senere
 (pl) Prosze sprobowac pozniej
+(pt) Por favor volte mais tarde
 </pre>
 
         $name = '';
@@ -313,7 +314,7 @@ sub prepare_body {
         unless ( $request->{_body} ) {
             my $type = $request->header('Content-Type');
             $request->{_body} = HTTP::Body->new( $type, $length );
-            $request->{_body}->{tmpdir} = $c->config->{uploadtmp}
+            $request->{_body}->tmpdir( $c->config->{uploadtmp} )
               if exists $c->config->{uploadtmp};
         }
         
@@ -450,7 +451,7 @@ sub prepare_query_parameters {
     # replace semi-colons
     $query_string =~ s/;/&/g;
     
-    my @params = split /&/, $query_string;
+    my @params = grep { length $_ } split /&/, $query_string;
 
     for my $item ( @params ) {
         
@@ -670,9 +671,7 @@ sub unescape_uri {
 
 =head1 AUTHORS
 
-Sebastian Riedel, <sri@cpan.org>
-
-Andy Grundman, <andy@hybridized.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 2fcc8f8..7cd01dd 100644 (file)
@@ -237,15 +237,11 @@ sub run { shift; shift->handle_request(@_) }
 
 =head1 SEE ALSO
 
-L<Catalyst> L<Catalyst::Engine>.
+L<Catalyst>, L<Catalyst::Engine>
 
 =head1 AUTHORS
 
-Sebastian Riedel, <sri@cpan.org>
-
-Christian Hansen, <ch@ngmedia.com>
-
-Andy Grundman, <andy@hybridized.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 7a09509..b55a633 100644 (file)
@@ -376,8 +376,9 @@ values are disabled.  The above example would start 5 processes.
 =head3 Non-root configuration
     
 You can also run your application at any non-root location with either of the
-above modes.
+above modes.  Note the required mod_rewrite rule.
 
+    url.rewrite = ( "myapp\$" => "myapp/" )
     fastcgi.server = (
         "/myapp" => (
             "MyApp" => (
@@ -400,11 +401,7 @@ L<Catalyst>, L<FCGI>.
 
 =head1 AUTHORS
 
-Sebastian Riedel, <sri@cpan.org>
-
-Christian Hansen, <ch@ngmedia.com>
-
-Andy Grundman, <andy@hybridized.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 THANKS
 
index fb40d6f..7e6beb1 100644 (file)
@@ -529,17 +529,11 @@ no Moose;
 
 =head1 SEE ALSO
 
-L<Catalyst>, L<Catalyst::Engine>.
+L<Catalyst>, L<Catalyst::Engine>
 
 =head1 AUTHORS
 
-Sebastian Riedel, <sri@cpan.org>
-
-Dan Kubb, <dan.kubb-cpan@onautopilot.com>
-
-Sascha Kiefer, <esskar@cpan.org>
-
-Andy Grundman, <andy@hybridized.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 THANKS
 
index 4691cad..0cae243 100644 (file)
@@ -98,11 +98,7 @@ L<Catalyst::Engine>.
 
 =head1 AUTHORS
 
-Sebastian Riedel, <sri@cpan.org>
-
-Dan Kubb, <dan.kubb-cpan@onautopilot.com>
-
-Andy Grundman, <andy@hybridized.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 THANKS
 
index 9ad126e..da5b24f 100644 (file)
@@ -147,7 +147,7 @@ files
 
     my $watcher = Catalyst::Engine::HTTP::Restarter::Watcher->new(
         directory => '/path/to/MyApp',
-        regex     => '\.yml$|\.yaml$|\.pm$',
+        regex     => '\.yml$|\.yaml$|\.conf|\.pm$',
         delay     => 1,
     );
     
@@ -178,9 +178,7 @@ L<Catalyst>, L<Catalyst::Engine::HTTP::Restarter>, L<File::Modified>
 
 =head1 AUTHORS
 
-Sebastian Riedel, <sri@cpan.org>
-
-Andy Grundman, <andy@hybridized.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 THANKS
 
index deeb3c0..41b411a 100644 (file)
@@ -48,10 +48,9 @@ sub throw {
 
 Provided by Moose
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-Christian Hansen, C<ch@ngmedia.com>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index e1953b2..7a69d03 100644 (file)
@@ -239,11 +239,9 @@ over the log output.
 
 L<Catalyst>.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-Marcus Ramberg, C<mramberg@cpan.org>
-Christian Hansen, C<ch@ngmedia.com>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 8170e5e..0f1c842 100644 (file)
@@ -16,9 +16,8 @@ run the following command from a unix (bash) prompt:
  $ perldoc -t Catalyst::Manual::Tutorial::CatalystBasics 2>&1 >/dev/null && echo OK || echo MISSING
 
 If you see "OK" as the output, it's there, if you see "MISSING" you
-need to install the
-L<Catalyst::Manual|http://search.cpan.org/search?query=Catalyst%3A%3AManual&mode=dist>
-distribution.
+need to install the L<Catalyst::Manual> distribution
+(L<http://search.cpan.org/dist/Catalyst-Manual/>).
 
 =over 4
 
@@ -91,12 +90,13 @@ Mailing-Lists:
     http://lists.rawmode.org/mailman/listinfo/catalyst
     http://lists.rawmode.org/mailman/listinfo/catalyst-dev
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@oook.de>
-Jesse Sheidlower, C<jester@panix.com>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
 This program is free software, you can redistribute it and/or modify it
 under the same terms as Perl itself.
+
+=cut
index f573770..05c913f 100644 (file)
@@ -22,9 +22,9 @@ Catalyst Model base class.
 Implements the same methods as other Catalyst components, see
 L<Catalyst::Component>
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@oook.de>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index e6f0a00..c85ca09 100644 (file)
@@ -537,7 +537,8 @@ Returns a URI object for the current request. Stringifies to the URI text.
 =head2 $req->uri_with( { key => 'value' } );
 
 Returns a rewritten URI object for the current request. Key/value pairs
-passed in will override existing parameters. Unmodified pairs will be
+passed in will override existing parameters. You can remove an existing
+parameter by passing in an undef value. Unmodified pairs will be
 preserved.
 
 =cut
@@ -547,7 +548,7 @@ sub uri_with {
     
     carp( 'No arguments passed to uri_with()' ) unless $args;
 
-    for my $value ( values %$args ) {
+    foreach my $value ( values %$args ) {
         next unless defined $value;
         for ( ref $value eq 'ARRAY' ? @$value : $value ) {
             $_ = "$_";
@@ -555,11 +556,12 @@ sub uri_with {
         }
     };
     
-    my $uri = $self->uri->clone;
-    
+    my $uri   = $self->uri->clone;
+    my %query = ( %{ $uri->query_form_hash }, %$args );
+
     $uri->query_form( {
-        %{ $uri->query_form_hash },
-        %$args
+        # remove undef values
+        map { defined $query{ $_ } ? ( $_ => $query{ $_ } ) : () } keys %query
     } );
     return $uri;
 }
@@ -580,9 +582,7 @@ Provided by Moose
 
 =head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-
-Marcus Ramberg, C<mramberg@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index b1bd4e6..dfb169b 100644 (file)
@@ -56,11 +56,6 @@ option in the Catalyst config. If unset, Catalyst will use the system temp dir.
 
     __PACKAGE__->config( uploadtmp => '/path/to/tmpdir' );
 
-It is provided a way to have configurable temporary directory.
-If there is no config uploadtmp, system temprary directory will used.
-
-    __PACKAGE__->config( uploadtmp => '/path/to/tmpdir' );
-
 See also L<Catalyst>.
 
 =head1 DESCRIPTION
@@ -173,9 +168,7 @@ Provided by Moose
 
 =head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-
-Christian Hansen, C<ch@ngmedia.com>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 9c610d4..545341d 100644 (file)
@@ -52,12 +52,12 @@ will turn the Catalyst::Response into a HTTP Response and return it to the clien
 
 =head1 METHODS
 
-=head2 $res->body(<$text|$fh|$iofh_object)
+=head2 $res->body(<$text|$fh|$iohandle_object)
 
     $c->response->body('Catalyst rocks!');
 
 Sets or returns the output (text or binary data). If you are returning a large body,
-you might want to use a L<IO::FileHandle> type of object (Something that implements the read method
+you might want to use a L<IO::Handle> type of object (Something that implements the read method
 in the same fashion), or a filehandle GLOB. Catalyst
 will write it piece by piece into the response.
 
@@ -119,11 +119,17 @@ Alias for $res->body.
 
 =head2 $res->redirect( $url, $status )
 
-Causes the response to redirect to the specified URL.
+Causes the response to redirect to the specified URL. The default status is
+C<302>.
 
     $c->response->redirect( 'http://slashdot.org' );
     $c->response->redirect( 'http://slashdot.org', 307 );
 
+This is a convenience method that sets the Location header to the
+redirect destination, and then sets the response status.  You will
+want to C< return; > or C< $c->detach() > to interrupt the normal
+processing flow if you want the redirect to occur straight away.
+
 =cut
 
 sub redirect {
@@ -160,9 +166,7 @@ Provided by Moose
 
 =head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-
-Marcus Ramberg, C<mramberg@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 145d33e..6c680a8 100644 (file)
@@ -7,11 +7,13 @@ BEGIN { require 5.008001; }
 
 # Remember to update this in Catalyst as well!
 
-our $VERSION='5.7013';
+our $VERSION='5.7099_03';
+
+$VERSION= eval $VERSION; 
 
 =head1 NAME
 
-Catalyst::Runtime - Catalyst  Runtime version
+Catalyst::Runtime - The Catalyst Framework Runtime
 
 =head1 SYNOPSIS
 
@@ -21,9 +23,9 @@ See L<Catalyst>.
 
 This is the primary class for the Catalyst-Runtime distribution, version 5.70.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-The Catalyst Core Team - see http://catalyst.perl.org/
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index daa5a0e..7b9c47c 100644 (file)
@@ -120,6 +120,49 @@ sub _get_uid {
     return $visitor->getResult;
 } 
 
+sub accept {
+    my $self = shift;
+    $self->{tree}->accept( @_ );
+}
+
+sub addChild {
+    my $self = shift;
+    my $node = $_[ 0 ];
+
+    my $stat = $node->getNodeValue;
+
+    # do we need to fake $stat->{ t } ?
+    if( $stat->{ elapsed } ) {
+        # remove the "s" from elapsed time
+        $stat->{ elapsed } =~ s{s$}{};
+    }
+
+    $self->{tree}->addChild( @_ );
+}
+
+sub setNodeValue {
+    my $self = shift;
+    my $stat = $_[ 0 ];
+
+    # do we need to fake $stat->{ t } ?
+    if( $stat->{ elapsed } ) {
+        # remove the "s" from elapsed time
+        $stat->{ elapsed } =~ s{s$}{};
+    }
+
+    $self->{tree}->setNodeValue( @_ );
+}
+
+sub getNodeValue {
+    my $self = shift;
+    $self->{tree}->getNodeValue( @_ )->{ t };
+}
+
+sub traverse {
+    my $self = shift;
+    $self->{tree}->traverse( @_ );
+}
+
 no Moose;
 __PACKAGE__->meta->make_immutable();
 
@@ -290,14 +333,28 @@ from the previous profiling point.
 The 'rollup' flag indicates whether the reported time is the rolled up time for
 the block, or the elapsed time from the previous profiling point.
 
+=head1 COMPATABILITY METHODS
+
+Some components might expect the stats object to be a regular Tree::Simple object.
+We've added some compatability methods to handle this scenario:
+
+=head2 accept
+
+=head2 addChild
+
+=head2 setNodeValue
+
+=head2 getNodeValue
+
+=head2 traverse
 
 =head1 SEE ALSO
 
-L<Catalyst>.
+L<Catalyst>
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Jon Schutz
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 82a43c1..9e0db01 100644 (file)
@@ -21,6 +21,12 @@ Catalyst::Test - Test Catalyst Applications
     request('index.html');
     get('index.html');
 
+    use HTTP::Request::Common;
+    my $response = request POST '/foo', [
+        bar => 'baz',
+        something => 'else'
+    ];
+
     # Run tests against a remote server
     CATALYST_SERVER='http://localhost:3000/' prove -r -l lib/ t/
 
@@ -45,7 +51,13 @@ Catalyst::Test - Test Catalyst Applications
 
 =head1 DESCRIPTION
 
-Test Catalyst Applications.
+This module allows you to make requests to a Catalyst application either without
+a server, by simulating the environment of an HTTP request using
+L<HTTP::Request::AsCGI> or remotely if you define the CATALYST_SERVER
+environment variable.
+
+The </get> and </request> functions take either a URI or an L<HTTP::Request>
+object.
 
 =head2 METHODS
 
@@ -104,6 +116,8 @@ sub import {
 
 =head2 local_request
 
+Simulate a request using L<HTTP::Request::AsCGI>.
+
 =cut
 
 sub local_request {
@@ -183,11 +197,12 @@ sub remote_request {
 
 =head1 SEE ALSO
 
-L<Catalyst>.
+L<Catalyst>, L<Test::WWW::Mechanize::Catalyst>,
+L<Test::WWW::Selenium::Catalyst>, L<Test::More>, L<HTTP::Request::Common>
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 4e5571e..d27fa9c 100644 (file)
@@ -8,6 +8,7 @@ use Path::Class;
 use URI;
 use Class::Inspector;
 use Carp qw/croak/;
+use Cwd;
 
 =head1 NAME
 
@@ -160,6 +161,7 @@ sub home {
 
             # find the @INC entry in which $file was found
             (my $path = $inc_entry) =~ s/$file$//;
+            $path ||= cwd() if !defined $path || !length $path;
             my $home = dir($path)->absolute->cleanup;
 
             # pop off /lib and /blib if they're there
@@ -329,10 +331,9 @@ sub env_value {
     return;
 }
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@cpan.org>
-Yuval Kogman, C<nothingmuch@woobling.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index a08bbbf..7ff0450 100644 (file)
@@ -52,10 +52,9 @@ Merges two hashes together recursively, giving right-hand precedence.
 
 =cut
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Sebastian Riedel, C<sri@oook.de>
-Marcus Ramberg, C<mramberg@cpan.org>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
index 188c708..287c12e 100755 (executable)
@@ -154,11 +154,9 @@ test directory
 
 =back
 
-
 The application module generated by the C<catalyst.pl> script is functional,
 although it reacts to all requests by outputting a friendly welcome screen.
 
-
 =head1 NOTE
 
 Neither C<catalyst.pl> nor the generated helper script will overwrite existing
@@ -171,22 +169,16 @@ Catalyst or its plugins generate different code, or to see how you may have
 changed the generated code (although you do of course have all your code in a
 version control system anyway, don't you ...).
 
-
-
 =head1 SEE ALSO
 
 L<Catalyst::Manual>, L<Catalyst::Manual::Intro>
 
-=head1 AUTHOR
-
-Sebastian Riedel, C<sri@oook.de>,
-Andrew Ford, C<A.Ford@ford-mason.co.uk>
+=head1 AUTHORS
 
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT
 
-Copyright 2004-2005 Sebastian Riedel. All rights reserved.
-
 This library is free software, you can redistribute it and/or modify it under
 the same terms as Perl itself.
 
index b4bc0a7..57c3bdb 100644 (file)
@@ -11,6 +11,10 @@ plan skip_all => "This test requires Class::C3" if $@;
 # Get a list of all Catalyst:: packages in blib via M::P::O
 my @cat_mods;
 {
+  # problem with @INC on win32, see:
+  # http://rt.cpan.org/Ticket/Display.html?id=26452
+  if ($^O eq 'MSWin32') { require Win32; Win32::GetCwd(); }
+
   local @INC = grep {/blib/} @INC;
   @cat_mods = (
     'Catalyst', 
diff --git a/t/custom_live_path_bug.t b/t/custom_live_path_bug.t
new file mode 100644 (file)
index 0000000..9bbbd55
--- /dev/null
@@ -0,0 +1,39 @@
+#!perl
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+our $iters;
+
+BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
+
+use Test::More tests => 2*$iters;
+use Catalyst::Test 'TestAppPathBug';
+
+if ( $ENV{CAT_BENCHMARK} ) {
+    require Benchmark;
+    Benchmark::timethis( $iters, \&run_tests );
+}
+else {
+    for ( 1 .. $iters ) {
+        run_tests();
+    }
+}
+    
+sub run_tests {
+    SKIP:
+    {
+        if ( $ENV{CATALYST_SERVER} ) {
+            skip 'Using remote server', 2;
+        }
+        
+        {
+            my $expected = 'This is the foo method.';
+            ok( my $response = request('http://localhost/'), 'response ok' );
+            is( $response->content, $expected, 'Content OK' );
+        }
+    }
+}
diff --git a/t/dead_load_multiple_chained_attributes.t b/t/dead_load_multiple_chained_attributes.t
new file mode 100644 (file)
index 0000000..7d1ad34
--- /dev/null
@@ -0,0 +1,31 @@
+#!perl
+
+use strict;
+use warnings;
+use lib 't/lib';
+
+use Test::More;
+
+plan tests => 4;
+
+use Catalyst::Test 'TestApp';
+
+eval q{  
+    package TestApp::Controller::Action::Chained;
+    sub should_fail : Chained('/') Chained('foo') Args(0) {}
+};
+ok(!$@);
+
+eval { TestApp->setup_actions; }; 
+ok($@, 'Multiple chained attributes make action setup fail');
+
+eval q{      
+    package TestApp::Controller::Action::Chained;
+    no warnings 'redefine';
+    sub should_fail {}
+};
+ok(!$@);
+
+eval { TestApp->setup_actions };
+ok(!$@, 'And ok again') or warn $@;
+
index a46ecb1..fb87d4a 100644 (file)
@@ -72,6 +72,11 @@ sub class_forward_test_method :Private {
     $c->response->headers->header( 'X-Class-Forward-Test-Method' => 1 );
 }
 
+sub class_go_test_method :Private {
+    my ( $self, $c ) = @_;
+    $c->response->headers->header( 'X-Class-Go-Test-Method' => 1 );
+}
+
 sub loop_test : Local {
     my ( $self, $c ) = @_;
 
index 61210dc..90b1efe 100644 (file)
@@ -175,6 +175,16 @@ sub cc_b           : Chained('cc_base')    PathPart('b')                           CaptureArgs(0) { }
 sub cc_b_link  : Chained('cc_b')               PathPart('')                            CaptureArgs(1) { }
 sub cc_b_anchor        : Chained('cc_b_link')  PathPart('anchor.html')         Args()             { }
 
+#
+#   Test static paths vs. captures
+#
+
+sub apan        : Chained('/')     CaptureArgs(0) PathPrefix   { }
+sub korv        : Chained('apan')  CaptureArgs(0) PathPart('') { }
+sub wurst       : Chained('apan')  CaptureArgs(1) PathPart('') { }
+sub static_end  : Chained('korv')  Args(0)                     { }
+sub capture_end : Chained('wurst') Args(0)        PathPart('') { }
+
 sub end :Private {
   my ($self, $c) = @_;
   return if $c->stash->{no_end};
index ec72a38..69614d5 100644 (file)
@@ -10,4 +10,16 @@ use base qw/ Catalyst::Controller /;
 #
 sub child :Chained('.') :Args(1) { }
 
+# Should be at /chained/rootdef/*/chained_rel/*/*
+sub chained_rel :Chained('../one') Args(2) {
+}
+
+# Should chain to loose in parent namespace - i.e. at /chained/loose/*/loose/*/*
+sub loose : ChainedParent Args(2) {
+}
+
+# Should be at /chained/cross/*/up_down/*
+sub up_down : Chained('../bar/cross1') Args(1) {
+}
+
 1;
diff --git a/t/lib/TestApp/Controller/Action/Chained/ParentChain/Relative.pm b/t/lib/TestApp/Controller/Action/Chained/ParentChain/Relative.pm
new file mode 100644 (file)
index 0000000..36f2a6c
--- /dev/null
@@ -0,0 +1,10 @@
+package TestApp::Controller::Action::Chained::ParentChain::Relative;
+use warnings;
+use strict;
+
+use base qw/ Catalyst::Controller /;
+
+# using ../ to go up more than one level
+sub chained_rel_two : Chained('../../one') Args(2) { }
+
+1;
diff --git a/t/lib/TestApp/Controller/Action/Chained/PathPrefix.pm b/t/lib/TestApp/Controller/Action/Chained/PathPrefix.pm
new file mode 100644 (file)
index 0000000..0d3b859
--- /dev/null
@@ -0,0 +1,12 @@
+package TestApp::Controller::Action::Chained::PathPrefix;
+
+use strict;
+use warnings;
+
+use base qw/Catalyst::Controller/;
+
+# this is kinda the same thing as: sub instance : Path {}
+# it should respond to: /action/chained/pathprefix/*
+sub instance : Chained('/') PathPrefix Args(1) { }
+
+1;
index c6be731..e118d73 100644 (file)
@@ -85,4 +85,9 @@ sub class_forward_test_action : Local {
     $c->forward(qw/TestApp class_forward_test_method/);
 }
 
+sub forward_to_uri_check : Local {
+    my ( $self, $c ) = @_;
+    $c->forward( 'Action::ForwardTo', 'uri_check' );
+}
+
 1;
diff --git a/t/lib/TestApp/Controller/Action/ForwardTo.pm b/t/lib/TestApp/Controller/Action/ForwardTo.pm
new file mode 100644 (file)
index 0000000..92db7f2
--- /dev/null
@@ -0,0 +1,11 @@
+package TestApp::Controller::Action::ForwardTo;
+
+use strict;
+use base 'TestApp::Controller::Action';
+
+sub uri_check : Private {
+    my ( $self, $c ) = @_;
+    $c->res->body( $c->uri_for('foo/bar')->path );
+}
+
+1;
diff --git a/t/lib/TestApp/Controller/Action/Go.pm b/t/lib/TestApp/Controller/Action/Go.pm
new file mode 100644 (file)
index 0000000..0b12a60
--- /dev/null
@@ -0,0 +1,90 @@
+package TestApp::Controller::Action::Go;
+
+use strict;
+use base 'TestApp::Controller::Action';
+
+sub one : Local {
+    my ( $self, $c ) = @_;
+    $c->go('two');
+}
+
+sub two : Private {
+    my ( $self, $c ) = @_;
+    $c->go('three');
+}
+
+sub three : Local {
+    my ( $self, $c ) = @_;
+    $c->go( $self, 'four' );
+}
+
+sub four : Private {
+    my ( $self, $c ) = @_;
+    $c->go('/action/go/five');
+}
+
+sub five : Local {
+    my ( $self, $c ) = @_;
+    $c->go('View::Dump::Request');
+}
+
+sub inheritance : Local {
+    my ( $self, $c ) = @_;
+    $c->go('/action/inheritance/a/b/default');
+}
+
+sub global : Local {
+    my ( $self, $c ) = @_;
+    $c->go('/global_action');
+}
+
+sub with_args : Local {
+    my ( $self, $c, $arg ) = @_;
+    $c->go( 'args', [$arg] );
+}
+
+sub with_method_and_args : Local {
+    my ( $self, $c, $arg ) = @_;
+    $c->go( qw/TestApp::Controller::Action::Go args/, [$arg] );
+}
+
+sub args : Local {
+    my ( $self, $c, $val ) = @_;
+    die "passed argument does not match args" unless $val eq $c->req->args->[0];
+    $c->res->body($val);
+}
+
+sub go_die : Local {
+    my ( $self, $c, $val ) = @_;
+    eval { $c->go( 'args', [qq/new/] ) };
+    $c->res->body( $@ ? $@ : "go() did not die" );
+    die $Catalyst::GO;
+}
+
+sub go_chained : Local {
+    my ( $self, $c, $val ) = @_;
+    $c->go('/action/chained/foo/spoon',[1]);
+}
+
+sub args_embed_relative : Local {
+    my ( $self, $c ) = @_;
+    $c->go('embed/ok');
+}
+
+sub args_embed_absolute : Local {
+    my ( $self, $c ) = @_;
+    $c->go('/action/go/embed/ok');
+}
+
+sub embed : Local {
+    my ( $self, $c, $ok ) = @_;
+    $ok ||= 'not ok';
+    $c->res->body($ok);
+}
+
+sub class_go_test_action : Local {
+    my ( $self, $c ) = @_;
+    $c->go(qw/TestApp class_go_test_method/);
+}
+
+1;
index 951345e..2efe3f1 100644 (file)
@@ -17,4 +17,13 @@ sub relative_two : Local {
     $c->forward( 'TestApp::Controller::Action::Forward', 'one' );
 }
 
+sub relative_go : Local {
+    my ( $self, $c ) = @_;
+    $c->go('/action/go/one');
+}
+
+sub relative_go_two : Local {
+    my ( $self, $c ) = @_;
+    $c->go( 'TestApp::Controller::Action::Go', 'one' );
+}
 1;
index 6a448cd..6ec7539 100644 (file)
@@ -2,7 +2,6 @@ package TestApp::Controller::Args;
 
 use strict;
 use base 'Catalyst::Base';
-use Data::Dumper;
 
 sub args :Local  {
     my ( $self, $c ) = @_;
@@ -14,4 +13,4 @@ sub params :Local {
     $c->res->body( join('',@_) );
 }
 
-1;
\ No newline at end of file
+1;
index b41c66a..44a2185 100644 (file)
@@ -77,4 +77,26 @@ sub uri_with_undef : Local {
     $c->forward('TestApp::View::Dump::Request');
 }
 
+sub uri_with_undef_only : Local {
+    my ( $self, $c ) = @_;
+
+    my $uri = $c->req->uri_with( { a => undef } );
+    
+    $c->res->header( 'X-Catalyst-uri-with' => "$uri" );
+    $c->forward('TestApp::View::Dump::Request');
+}
+
+sub uri_with_undef_ignore : Local {
+    my ( $self, $c ) = @_;
+
+    my $uri = $c->req->uri_with( { a => 1, b => undef } );
+    
+    my %query = $uri->query_form;
+    $c->res->header( 'X-Catalyst-uri-with' => "$uri" );
+    $c->res->header( 'X-Catalyst-Param-a' => $query{ a } );
+    $c->res->header( 'X-Catalyst-Param-b' => $query{ b } );
+    $c->res->header( 'X-Catalyst-Param-c' => $query{ c } );
+    $c->forward('TestApp::View::Dump::Request');
+}
+
 1;
index a9cbbda..53d79e2 100644 (file)
@@ -6,4 +6,18 @@ __PACKAGE__->config->{namespace} = '';
 
 sub chain_root_index : Chained('/') PathPart('') Args(0) { }
 
+sub zero : Path('0') {
+    my ( $self, $c ) = @_;
+    $c->res->header( 'X-Test-Class' => ref($self) );
+    $c->response->content_type('text/plain; charset=utf-8');
+    $c->forward('TestApp::View::Dump::Request');
+}
+
+sub localregex : LocalRegex('^localregex$') {
+    my ( $self, $c ) = @_;
+    $c->res->header( 'X-Test-Class' => ref($self) );
+    $c->response->content_type('text/plain; charset=utf-8');
+    $c->forward('TestApp::View::Dump::Request');
+}
+
 1;
diff --git a/t/lib/TestAppPathBug.pm b/t/lib/TestAppPathBug.pm
new file mode 100644 (file)
index 0000000..3d3b11b
--- /dev/null
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+
+package TestAppPathBug;
+
+use Catalyst;
+
+our $VERSION = '0.01';
+
+__PACKAGE__->config( name => 'TestAppPathBug', root => '/some/dir' );
+
+__PACKAGE__->setup;
+
+sub foo : Path {
+    my ( $self, $c ) = @_;
+    $c->res->body( 'This is the foo method.' );
+}
+
+1;
index 708fc67..a8b7e09 100644 (file)
@@ -10,7 +10,7 @@ our $iters;
 
 BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
 
-use Test::More tests => 124*$iters;
+use Test::More tests => 141*$iters;
 use Catalyst::Test 'TestApp';
 
 if ( $ENV{CAT_BENCHMARK} ) {
@@ -528,6 +528,86 @@ sub run_tests {
     }
 
     #
+    #   Test if :Chained('../act') is working
+    #
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Chained->begin
+          TestApp::Controller::Action::Chained->one
+          TestApp::Controller::Action::Chained::ParentChain->chained_rel
+          TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/chained/one/1/chained_rel/3/2'),
+            ":Chained('../action') chains to correct action" );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '1; 3, 2', 'Content OK' );
+    }
+
+    #
+    #   Test if ../ works to go up more than one level
+    #
+    {
+        my @expected = qw[
+            TestApp::Controller::Action::Chained->begin
+            TestApp::Controller::Action::Chained->one
+            TestApp::Controller::Action::Chained::ParentChain::Relative->chained_rel_two
+            TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/chained/one/1/chained_rel_two/42/23'),
+            "../ works to go up more than one level" );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '1; 42, 23', 'Content OK' );
+    }
+
+    #
+    #   Test if :ChainedParent is working
+    #
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Chained->begin
+          TestApp::Controller::Action::Chained->loose
+          TestApp::Controller::Action::Chained::ParentChain->loose
+          TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/chained/loose/4/loose/a/b'),
+            ":Chained('../action') chains to correct action" );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '4; a, b', 'Content OK' );
+    }
+
+    #
+    #   Test if :Chained('../name/act') is working
+    #
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Chained->begin
+          TestApp::Controller::Action::Chained::Bar->cross1
+          TestApp::Controller::Action::Chained::ParentChain->up_down
+          TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/chained/cross/4/up_down/5'),
+            ":Chained('../action') chains to correct action" );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '4; 5', 'Content OK' );
+    }
+
+    #
     #   Test behaviour of auto actions returning '1' for the chain.
     #
     {
@@ -863,4 +943,42 @@ sub run_tests {
     
     }
 
+    #
+    #   PathPrefix
+    #
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Chained->begin
+          TestApp::Controller::Action::Chained::PathPrefix->instance
+          TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/chained/pathprefix/1'),
+            "PathPrefix (as an endpoint)" );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '; 1', 'Content OK' );
+    }
+
+    #
+    #   static paths vs. captures
+    #
+    {
+        my @expected = qw[
+            TestApp::Controller::Action::Chained->begin
+            TestApp::Controller::Action::Chained->apan
+            TestApp::Controller::Action::Chained->korv
+            TestApp::Controller::Action::Chained->static_end
+            TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/chained/static_end'),
+            "static paths are prefered over captures" );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+    }
 }
index d4e20f8..7c14b78 100644 (file)
@@ -10,7 +10,7 @@ our $iters;
 
 BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
 
-use Test::More tests => 47 * $iters;
+use Test::More tests => 50 * $iters;
 use Catalyst::Test 'TestApp';
 
 if ( $ENV{CAT_BENCHMARK} ) {
@@ -235,4 +235,14 @@ sub run_tests {
             'Test Method' );
     }
 
+    # test uri_for re r7385
+    {
+        ok( my $response = request(
+            'http://localhost/action/forward/forward_to_uri_check'),
+            'forward_to_uri_check request');
+
+        ok( $response->is_success, 'forward_to_uri_check successful');
+        is( $response->content, '/action/forward/foo/bar',
+             'forward_to_uri_check correct namespace');
+    }
 }
diff --git a/t/live_component_controller_action_go.t b/t/live_component_controller_action_go.t
new file mode 100644 (file)
index 0000000..544166d
--- /dev/null
@@ -0,0 +1,254 @@
+#!perl
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+our $iters;
+
+BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
+
+use Test::More tests => 50 * $iters;
+use Catalyst::Test 'TestApp';
+
+if ( $ENV{CAT_BENCHMARK} ) {
+    require Benchmark;
+    Benchmark::timethis( $iters, \&run_tests );
+}
+else {
+    for ( 1 .. $iters ) {
+        run_tests();
+    }
+}
+
+sub run_tests {
+    {
+        # Test go to global private action
+        ok( my $response = request('http://localhost/action/go/global'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/go/global', 'Main Class Action' );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Go->one
+          TestApp::Controller::Action::Go->two
+          TestApp::Controller::Action::Go->three
+          TestApp::Controller::Action::Go->four
+          TestApp::Controller::Action::Go->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test go to chain of actions.
+        ok( my $response = request('http://localhost/action/go/one'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/go/one', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Go->go_die
+          TestApp::Controller::Action::Go->args
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/go/go_die'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/go/go_die', 'Test Action'
+        );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, $Catalyst::GO, "Go died as expected" );
+    }
+
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/go/with_args/old'),
+            'Request with args'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'old' );
+    }
+
+    {
+        ok(
+            my $response = request(
+                'http://localhost/action/go/with_method_and_args/new'),
+            'Request with args and method'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'new' );
+    }
+
+    # test go with embedded args
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/go/args_embed_relative'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'ok' );
+    }
+
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/go/args_embed_absolute'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'ok' );
+    }
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::TestRelative->relative_go
+          TestApp::Controller::Action::Go->one
+          TestApp::Controller::Action::Go->two
+          TestApp::Controller::Action::Go->three
+          TestApp::Controller::Action::Go->four
+          TestApp::Controller::Action::Go->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test go to chain of actions.
+        ok( my $response = request('http://localhost/action/relative/relative_go'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/relative/relative_go', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::TestRelative->relative_go_two
+          TestApp::Controller::Action::Go->one
+          TestApp::Controller::Action::Go->two
+          TestApp::Controller::Action::Go->three
+          TestApp::Controller::Action::Go->four
+          TestApp::Controller::Action::Go->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test go to chain of actions.
+        ok(
+            my $response =
+              request('http://localhost/action/relative/relative_go_two'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is(
+            $response->header('X-Catalyst-Action'),
+            'action/relative/relative_go_two',
+            'Test Action'
+        );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+
+    # test class go -- MUST FAIL!
+    {
+        ok(
+            my $response = request(
+                'http://localhost/action/go/class_go_test_action'),
+            'Request'
+        );
+        ok( !$response->is_success, 'Response Fails' );
+        is( $response->content, q(FATAL ERROR: Couldn't go to command "TestApp": Invalid action or component.), 'Error message' );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Go->begin
+          TestApp::Controller::Action::Go->go_chained
+          TestApp::Controller::Action::Chained->begin
+          TestApp::Controller::Action::Chained->foo
+          TestApp::Controller::Action::Chained::Foo->spoon
+          TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/go/go_chained'), 'go to chained + subcontroller endpoint' );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '; 1', 'Content OK' );
+    }
+
+}
+
+
+
+sub _begin {
+    local $_ = shift;
+    s/->(.*)$/->begin/;
+    return $_;
+}
+
index 7b1d0cf..18fc83d 100644 (file)
@@ -10,7 +10,7 @@ our $iters;
 
 BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
 
-use Test::More tests => 30*$iters;
+use Test::More tests => 36*$iters;
 use Catalyst::Test 'TestApp';
 
 if ( $ENV{CAT_BENCHMARK} ) {
@@ -124,4 +124,22 @@ sub run_tests {
             'Content is a serialized Catalyst::Request'
         );
     }
+
+    {
+        ok( my $response = request('http://localhost/0'), 'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            '0', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Root',
+            'Test Class'
+        );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
 }
index 4d4500e..be005bc 100644 (file)
@@ -10,7 +10,7 @@ our $iters;
 
 BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
 
-use Test::More tests => 28*$iters;
+use Test::More tests => 33*$iters;
 use Catalyst::Test 'TestApp';
 
 use Catalyst::Request;
@@ -103,4 +103,19 @@ sub run_tests {
         is( $req->captures->[ 0 ], 'mandatory', 'mandatory capture' );
         is( $req->captures->[ 1 ], '/optional', 'optional capture' );
     }
+
+    # test localregex in the root controller
+    {
+        ok( my $response = request('http://localhost/localregex'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            '^localregex$', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Root',
+            'Test Class'
+        );
+    }
 }
index 4b46009..684660a 100644 (file)
@@ -6,7 +6,7 @@ use warnings;
 use FindBin;
 use lib "$FindBin::Bin/lib";
 
-use Test::More tests => 40;
+use Test::More tests => 53;
 use Catalyst::Test 'TestApp';
 
 use Catalyst::Request;
@@ -137,3 +137,26 @@ use HTTP::Request::Common;
     ok( eval '$creq = ' . $response->content, 'Unserialize Catalyst::Request' );
     is( $creq->uri->query, 'x=1&y=1&z=1', 'Catalyst::Request GET query_string' );
 }
+
+{
+    my $creq;
+    ok( my $response = request("http://localhost/dump/request?&&q="),
+        'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->content_type, 'text/plain', 'Response Content-Type' );
+    ok( eval '$creq = ' . $response->content );
+    is( keys %{$creq->{parameters}}, 1, 'remove empty parameter' );
+    is( $creq->{parameters}->{q}, '', 'empty parameter' );
+}
+
+{
+    my $creq;
+    ok( my $response = request("http://localhost/dump/request?&0&q="),
+        'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->content_type, 'text/plain', 'Response Content-Type' );
+    ok( eval '$creq = ' . $response->content );
+    is( keys %{$creq->{parameters}}, 2, 'remove empty parameter' );
+    is( $creq->{parameters}->{q}, '', 'empty parameter' );
+    ok( !defined $creq->{parameters}->{0}, 'empty parameter' );
+}
index 88668fa..e2bb0d4 100644 (file)
@@ -6,7 +6,7 @@ use warnings;
 use FindBin;
 use lib "$FindBin::Bin/lib";
 
-use Test::More tests => 75;
+use Test::More tests => 88;
 use Catalyst::Test 'TestApp';
 
 use Catalyst::Request;
@@ -14,6 +14,7 @@ use Catalyst::Request::Upload;
 use HTTP::Headers;
 use HTTP::Headers::Util 'split_header_words';
 use HTTP::Request::Common;
+use Path::Class::Dir;
 
 {
     my $creq;
@@ -242,3 +243,63 @@ use HTTP::Request::Common;
         is( $upload->filename, 'catalyst_130pix.gif' );
     }
 }
+
+# test uploadtmp config var
+
+{
+    my $creq;
+
+    my $dir = "$FindBin::Bin/";
+    local TestApp->config->{ uploadtmp } = $dir;
+    $dir = Path::Class::Dir->new( $dir );
+
+    my $request = POST(
+        'http://localhost/dump/request/',
+        'Content-Type' => 'multipart/form-data',
+        'Content'      => [
+            'testfile' => ["$FindBin::Bin/live_engine_request_uploads.t"],
+        ]
+    );
+
+    ok( my $response = request($request), 'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->content_type, 'text/plain', 'Response Content-Type' );
+    like(
+        $response->content,
+        qr/^bless\( .* 'Catalyst::Request' \)$/s,
+        'Content is a serialized Catalyst::Request'
+    );
+
+    {
+        no strict 'refs';
+        ok(
+            eval '$creq = ' . $response->content,
+            'Unserialize Catalyst::Request'
+        );
+    }
+
+    isa_ok( $creq, 'Catalyst::Request' );
+    is( $creq->method, 'POST', 'Catalyst::Request method' );
+    is( $creq->content_type, 'multipart/form-data',
+        'Catalyst::Request Content-Type' );
+    is( $creq->content_length, $request->content_length,
+        'Catalyst::Request Content-Length' );
+
+    for my $part ( $request->parts ) {
+
+        my $disposition = $part->header('Content-Disposition');
+        my %parameters  = @{ ( split_header_words($disposition) )[0] };
+
+        next unless exists $parameters{filename};
+
+        my $upload = $creq->{uploads}->{ $parameters{name} };
+
+        isa_ok( $upload, 'Catalyst::Request::Upload' );
+
+        is( $upload->type, $part->content_type, 'Upload Content-Type' );
+        is( $upload->size, length( $part->content ), 'Upload Content-Length' );
+
+        like( $upload->tempname, qr{\Q$dir\E}, 'uploadtmp' );
+    }
+}
+
index 01a19fa..65166dc 100644 (file)
@@ -1,12 +1,10 @@
-\feff#!perl
-
 use strict;
 use warnings;
 
 use FindBin;
 use lib "$FindBin::Bin/lib";
 
-use Test::More tests => 49;
+use Test::More tests => 66;
 use Catalyst::Test 'TestApp';
 use Catalyst::Request;
 
@@ -120,3 +118,36 @@ SKIP:
     is( $response->header( 'X-Catalyst-warnings' ), 0, 'no warnings emitted' );
 }
 
+# more tests with undef - should be ignored
+{
+    my $uri = "http://localhost/engine/request/uri/uri_with_undef_only";
+    ok( my $response = request($uri), 'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->header( 'X-Catalyst-uri-with' ), $uri, 'uri_with ok' );
+
+    # try with existing param
+    $uri = "$uri?x=1";
+    ok( $response = request($uri), 'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->header( 'X-Catalyst-uri-with' ), $uri, 'uri_with ok' );
+}
+
+{
+    my $uri = "http://localhost/engine/request/uri/uri_with_undef_ignore";
+    ok( my $response = request($uri), 'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->header( 'X-Catalyst-uri-with' ), "${uri}?a=1", 'uri_with ok' );
+
+    # remove an existing param
+    ok( $response = request("${uri}?b=1"), 'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->header( 'X-Catalyst-uri-with' ), "${uri}?a=1", 'uri_with ok' );
+
+    # remove an existing param, leave one, and add a new one
+    ok( $response = request("${uri}?b=1&c=1"), 'Request' );
+    ok( $response->is_success, 'Response Successful 2xx' );
+    is( $response->header( 'X-Catalyst-Param-a' ), '1', 'param "a" ok' );
+    ok( !defined $response->header( 'X-Catalyst-Param-b' ),'param "b" ok' );
+    is( $response->header( 'X-Catalyst-Param-c' ), '1', 'param "c" ok' );
+}
+
index 785ae5d..e726027 100644 (file)
@@ -8,7 +8,6 @@ use lib "$FindBin::Bin/lib";
 
 use Test::More tests => 28;
 use Catalyst::Test 'TestApp';
-use Data::Dumper;
 
 local $^W = 0;
 
index 10e8a60..48e7013 100644 (file)
@@ -1,5 +1,3 @@
-#!perl
-
 # This test tests the standalone server's auto-restart feature.
 
 use strict;
@@ -9,6 +7,7 @@ use File::Path;
 use FindBin;
 use LWP::Simple;
 use IO::Socket;
+use IPC::Open3;
 use Test::More;
 use Time::HiRes qw/sleep/;
 eval "use Catalyst::Devel 1.0;";
@@ -21,14 +20,17 @@ plan skip_all => 'File::Copy::Recursive required' if $@;
 
 plan tests => 120;
 
+my $tmpdir = "$FindBin::Bin/../t/tmp";
+
 # clean up
-rmtree "$FindBin::Bin/../t/tmp" if -d "$FindBin::Bin/../t/tmp";
+rmtree $tmpdir if -d $tmpdir;
 
 # create a TestApp and copy the test libs into it
-mkdir "$FindBin::Bin/../t/tmp";
-chdir "$FindBin::Bin/../t/tmp";
-system
-  "perl -I$FindBin::Bin/../lib $FindBin::Bin/../script/catalyst.pl TestApp";
+mkdir $tmpdir;
+chdir $tmpdir;
+
+system( 'perl', "-I$FindBin::Bin/../lib", "$FindBin::Bin/../script/catalyst.pl", 'TestApp' );
+
 chdir "$FindBin::Bin/..";
 File::Copy::Recursive::dircopy( 't/lib', 't/tmp/TestApp/lib' );
 
@@ -38,9 +40,12 @@ rmtree 't/tmp/TestApp/t';
 # spawn the standalone HTTP server
 my $port = 30000 + int rand( 1 + 10000 );
 
-my $pid  = open my $server,
-"perl -I$FindBin::Bin/../lib $FindBin::Bin/../t/tmp/TestApp/script/testapp_server.pl -port $port -restart 2>&1 |"
-  or die "Unable to spawn standalone HTTP server: $!";
+my( $server, $pid );
+$pid = open3( undef, $server, undef,
+  'perl', "-I$FindBin::Bin/../lib",
+  "$FindBin::Bin/../t/tmp/TestApp/script/testapp_server.pl", '-port',
+  $port, '-restart' )
+    or die "Unable to spawn standalone HTTP server: $!";
 
 # switch to non-blocking reads so we can fail
 # gracefully instead of just hanging forever
@@ -167,9 +172,11 @@ my $restartdirs = join ' ', map{
     "-restartdirectory $app_root/lib/TestApp/Controller/$_"
 } qw/Action Engine/;
 
-$pid  = open $server,
-"perl -I$FindBin::Bin/../lib $FindBin::Bin/../t/tmp/TestApp/script/testapp_server.pl -port $port -restart $restartdirs 2>&1 |"
-  or die "Unable to spawn standalone HTTP server: $!";
+$pid = open3( undef, $server, undef,
+  'perl', "-I$FindBin::Bin/../lib",
+  "$FindBin::Bin/../t/tmp/TestApp/script/testapp_server.pl", '-port',
+  $port, '-restart', $restartdirs )
+    or die "Unable to spawn standalone HTTP server: $!";
 $server->blocking( 0 );
 
 
index fbef97f..bf1878b 100644 (file)
@@ -1,10 +1,9 @@
-#!perl
-
 use strict;
 use warnings;
 
 use File::Path;
 use FindBin;
+use IPC::Open3;
 use IO::Socket;
 use Test::More;
 
@@ -18,13 +17,15 @@ plan tests => 1;
 # Run a single test by providing it as the first arg
 my $single_test = shift;
 
+my $tmpdir = "$FindBin::Bin/../t/tmp";
+
 # clean up
-rmtree "$FindBin::Bin/../t/tmp" if -d "$FindBin::Bin/../t/tmp";
+rmtree $tmpdir if -d $tmpdir;
 
 # create a TestApp and copy the test libs into it
-mkdir "$FindBin::Bin/../t/tmp";
-chdir "$FindBin::Bin/../t/tmp";
-system "perl -I$FindBin::Bin/../lib $FindBin::Bin/../script/catalyst.pl TestApp";
+mkdir $tmpdir;
+chdir $tmpdir;
+system( 'perl', "-I$FindBin::Bin/../lib", "$FindBin::Bin/../script/catalyst.pl", 'TestApp' );
 chdir "$FindBin::Bin/..";
 File::Copy::Recursive::dircopy( 't/lib', 't/tmp/TestApp/lib' );
 
@@ -33,8 +34,9 @@ rmtree 't/tmp/TestApp/t';
 
 # spawn the standalone HTTP server
 my $port = 30000 + int rand(1 + 10000);
-my $pid = open my $server, 
-    "perl -I$FindBin::Bin/../lib $FindBin::Bin/../t/tmp/TestApp/script/testapp_server.pl -port $port 2>&1 |"
+my $pid = open3( undef, my $server, undef,
+  'perl', "-I$FindBin::Bin/../lib",
+  "$FindBin::Bin/../t/tmp/TestApp/script/testapp_server.pl", '-port', $port )
     or die "Unable to spawn standalone HTTP server: $!";
 
 # wait for it to start
@@ -46,11 +48,12 @@ while ( check_port( 'localhost', $port ) != 1 ) {
 # run the testsuite against the HTTP server
 $ENV{CATALYST_SERVER} = "http://localhost:$port";
 
+my $return;
 if ( $single_test ) {
-    system( "perl -Ilib/ $single_test" );
+    $return = system( "perl -Ilib/ $single_test" );
 }
 else {
-    system( 'prove -r -Ilib/ t/live_*' );
+    $return = system( 'prove -r -Ilib/ t/live_*.t' );
 }
 
 # shut it down
@@ -60,7 +63,7 @@ close $server;
 # clean up
 rmtree "$FindBin::Bin/../t/tmp" if -d "$FindBin::Bin/../t/tmp";
 
-ok( 'done' );
+is( $return, 0, 'live tests' );
 
 sub check_port {
     my ( $host, $port ) = @_;
diff --git a/t/something/script/foo/bar/for_dist b/t/something/script/foo/bar/for_dist
new file mode 100644 (file)
index 0000000..e69de29
index d12ad59..250960a 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 7;
+use Test::More tests => 22;
 use strict;
 use warnings;
 
@@ -12,6 +12,9 @@ my @complist = map { "MyApp::$_"; } qw/C::Controller M::Model V::View/;
   use base qw/Catalyst/;
 
   __PACKAGE__->components({ map { ($_, $_) } @complist });
+
+  # this is so $c->log->warn will work
+  __PACKAGE__->setup_log;
 }
 
 is(MyApp->comp('MyApp::V::View'), 'MyApp::V::View', 'Explicit return ok');
@@ -20,9 +23,66 @@ is(MyApp->comp('C::Controller'), 'MyApp::C::Controller', 'Two-part return ok');
 
 is(MyApp->comp('Model'), 'MyApp::M::Model', 'Single part return ok');
 
-is(MyApp->comp('::M::'), 'MyApp::M::Model', 'Regex return ok');
-
 is_deeply([ MyApp->comp() ], \@complist, 'Empty return ok');
 
+# Is this desired behaviour?
 is_deeply([ MyApp->comp('Foo') ], \@complist, 'Fallthrough return ok');
-  # Is this desired behaviour?
+
+# regexp behavior
+{
+    is_deeply( [ MyApp->comp( qr{Model} ) ], [ 'MyApp::M::Model'], 'regexp ok' );
+    is_deeply( [ MyApp->comp('MyApp::V::View$') ], [ 'MyApp::V::View' ], 'Explicit return ok');
+    is_deeply( [ MyApp->comp('MyApp::C::Controller$') ], [ 'MyApp::C::Controller' ], 'Explicit return ok');
+    is_deeply( [ MyApp->comp('MyApp::M::Model$') ], [ 'MyApp::M::Model' ], 'Explicit return ok');
+
+    # a couple other varieties for regexp fallback
+    is_deeply( [ MyApp->comp('M::Model') ], [ 'MyApp::M::Model' ], 'Explicit return ok');
+
+    {
+        my $warnings = 0;
+        no warnings 'redefine';
+        local *Catalyst::Log::warn = sub { $warnings++ };
+
+        is_deeply( [ MyApp->comp('::M::Model') ], [ 'MyApp::M::Model' ], 'Explicit return ok');
+        ok( $warnings, 'regexp fallback warnings' );
+
+        $warnings = 0;
+        is_deeply( [ MyApp->comp('Mode') ], [ 'MyApp::M::Model' ], 'Explicit return ok');
+        ok( $warnings, 'regexp fallback warnings' );
+
+        $warnings = 0;
+        is(MyApp->comp('::M::'), 'MyApp::M::Model', 'Regex return ok');
+        ok( $warnings, 'regexp fallback for comp() warns' );
+    }
+
+}
+
+# multiple returns
+{
+    my @expected = qw( MyApp::C::Controller MyApp::M::Model );
+    is_deeply( [ MyApp->comp( qr{::[MC]::} ) ], \@expected, 'multiple results fro regexp ok' );
+}
+
+# failed search
+{
+    is_deeply( scalar MyApp->comp( qr{DNE} ), 0, 'no results for failed search' );
+}
+
+
+#checking @args passed to ACCEPT_CONTEXT
+{
+    my $args;
+
+    no warnings; 
+    *MyApp::M::Model::ACCEPT_CONTEXT = sub { my ($self, $c, @args) = @_; $args= \@args};
+
+    MyApp->component('MyApp::M::Model', qw/foo bar/);
+    is_deeply($args, [qw/foo bar/], 'args passed to ACCEPT_CONTEXT ok');
+
+    MyApp->component('M::Model', qw/foo2 bar2/);
+    is_deeply($args, [qw/foo2 bar2/], 'args passed to ACCEPT_CONTEXT ok');
+
+    MyApp->component('Mode', qw/foo3 bar3/);
+    is_deeply($args, [qw/foo3 bar3/], 'args passed to ACCEPT_CONTEXT ok');
+} 
+
index 0dbbd80..549d758 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 27;
+use Test::More tests => 44;
 use strict;
 use warnings;
 
@@ -18,6 +18,9 @@ push @complist,$thingie;
     use base qw/Catalyst/;
 
     __PACKAGE__->components( { map { ( ref($_)||$_ , $_ ) } @complist } );
+
+    # allow $c->log->warn to work
+    __PACKAGE__->setup_log;
 }
 
 is( MyApp->view('View'), 'MyApp::V::View', 'V::View ok' );
@@ -39,6 +42,11 @@ is( MyApp->controller('C'), 'MyApp::Controller::C', 'Controller::C ok' );
 
 is( MyApp->model('M'), 'MyApp::Model::M', 'Model::M ok' );
 
+# failed search
+{
+    is( MyApp->model('DNE'), undef, 'undef for invalid search' );
+}
+
 is_deeply( [ sort MyApp->views ],
            [ qw/V View/ ],
            'views ok' );
@@ -51,7 +59,14 @@ is_deeply( [ sort MyApp->models ],
            [ qw/Dummy::Model M Model Test::Object/ ],
            'models ok');
 
-is (MyApp->view , 'MyApp::V::View', 'view() with no defaults ok');
+{
+    my $warnings = 0;
+    no warnings 'redefine';
+    local *Catalyst::Log::warn = sub { $warnings++ };
+
+    like (MyApp->view , qr/^MyApp\::(V|View)\::/ , 'view() with no defaults returns *something*');
+    ok( $warnings, 'view() w/o a default is random, warnings thrown' );
+}
 
 is ( bless ({stash=>{current_view=>'V'}}, 'MyApp')->view , 'MyApp::View::V', 'current_view ok');
 
@@ -61,7 +76,14 @@ is ( bless ({stash=>{current_view_instance=> $view }}, 'MyApp')->view , $view, '
 is ( bless ({stash=>{current_view_instance=> $view, current_view=>'MyApp::V::View' }}, 'MyApp')->view , $view, 
   'current_view_instance precedes current_view ok');
 
-is (MyApp->model , 'MyApp::M::Model', 'model() with no defaults ok');
+{
+    my $warnings = 0;
+    no warnings 'redefine';
+    local *Catalyst::Log::warn = sub { $warnings++ };
+
+    like (MyApp->model , qr/^MyApp\::(M|Model)\::/ , 'model() with no defaults returns *something*');
+    ok( $warnings, 'model() w/o a default is random, warnings thrown' );
+}
 
 is ( bless ({stash=>{current_model=>'M'}}, 'MyApp')->model , 'MyApp::Model::M', 'current_model ok');
 
@@ -79,14 +101,66 @@ MyApp->config->{default_model} = 'M';
 is ( bless ({stash=>{}}, 'MyApp')->model , 'MyApp::Model::M', 'default_model ok');
 is ( MyApp->model , 'MyApp::Model::M', 'default_model in class method ok');
 
+# regexp behavior tests
+{
+    # is_deeply is used because regexp behavior means list context
+    is_deeply( [ MyApp->view( qr{^V[ie]+w$} ) ], [ 'MyApp::V::View' ], 'regexp view ok' );
+    is_deeply( [ MyApp->controller( qr{Dummy\::Model$} ) ], [ 'MyApp::Controller::Model::Dummy::Model' ], 'regexp controller ok' );
+    is_deeply( [ MyApp->model( qr{Dum{2}y} ) ], [ 'MyApp::Model::Dummy::Model' ], 'regexp model ok' );
+    
+    # object w/ qr{}
+    is_deeply( [ MyApp->model( qr{Test} ) ], [ MyApp->components->{'MyApp::Model::Test::Object'} ], 'Object returned' );
+
+    {
+        my $warnings = 0;
+        no warnings 'redefine';
+        local *Catalyst::Log::warn = sub { $warnings++ };
+
+        # object w/ regexp fallback
+        is_deeply( [ MyApp->model( 'Test' ) ], [ MyApp->components->{'MyApp::Model::Test::Object'} ], 'Object returned' );
+        ok( $warnings, 'regexp fallback warnings' );
+    }
+
+    is_deeply( [ MyApp->view('MyApp::V::View$') ], [ 'MyApp::V::View' ], 'Explicit return ok');
+    is_deeply( [ MyApp->controller('MyApp::C::Controller$') ], [ 'MyApp::C::Controller' ], 'Explicit return ok');
+    is_deeply( [ MyApp->model('MyApp::M::Model$') ], [ 'MyApp::M::Model' ], 'Explicit return ok');
+}
+
+{
+    my @expected = qw( MyApp::C::Controller MyApp::Controller::C );
+    is_deeply( [ sort MyApp->controller( qr{^C} ) ], \@expected, 'multiple controller returns from regexp search' );
+}
+
+{
+    my @expected = qw( MyApp::V::View MyApp::View::V );
+    is_deeply( [ sort MyApp->view( qr{^V} ) ], \@expected, 'multiple view returns from regexp search' );
+}
+
+{
+    my @expected = qw( MyApp::M::Model MyApp::Model::M );
+    is_deeply( [ sort MyApp->model( qr{^M} ) ], \@expected, 'multiple model returns from regexp search' );
+}
+
+# failed search
+{
+    is( scalar MyApp->controller( qr{DNE} ), 0, '0 results for failed search' );
+}
+
 #checking @args passed to ACCEPT_CONTEXT
-my $args;
 {
+    my $args;
+
     no warnings; 
     *MyApp::Model::M::ACCEPT_CONTEXT = sub { my ($self, $c, @args) = @_; $args= \@args};
     *MyApp::View::V::ACCEPT_CONTEXT = sub { my ($self, $c, @args) = @_; $args= \@args};
-} 
-MyApp->model('M', qw/foo bar/);
-is_deeply($args, [qw/foo bar/], '$c->model args passed to ACCEPT_CONTEXT ok');
-MyApp->view('V', qw/baz moo/);
-is_deeply($args, [qw/baz moo/], '$c->view args passed to ACCEPT_CONTEXT ok');
+
+    MyApp->model('M', qw/foo bar/);
+    is_deeply($args, [qw/foo bar/], '$c->model args passed to ACCEPT_CONTEXT ok');
+
+    my $x = MyApp->view('V', qw/foo2 bar2/);
+    is_deeply($args, [qw/foo2 bar2/], '$c->view args passed to ACCEPT_CONTEXT ok');
+
+    # regexp fallback
+    MyApp->view('::View::V', qw/foo3 bar3/);
+    is_deeply($args, [qw/foo3 bar3/], 'args passed to ACCEPT_CONTEXT ok');
+}
index 0c61435..c767ff2 100644 (file)
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 14;
+use Test::More tests => 15;
 use URI;
 
 use_ok('Catalyst');
@@ -58,6 +58,11 @@ is(
     'http://127.0.0.1/foo/yada/quux?param1=%E2%98%A0',
     'URI for undef action with query params in unicode'
 );
+is(
+    Catalyst::uri_for( $context, 'quux', { 'param:1' => "foo" } )->as_string,
+    'http://127.0.0.1/foo/yada/quux?param%3A1=foo',
+    'URI for undef action with query params in unicode'
+);
 
 # test with object
 is(
index a8579eb..e46baf4 100644 (file)
@@ -3,8 +3,9 @@
 use strict;
 use warnings;
 
-use Test::More tests => 6;
+use Test::More tests => 12;
 use Time::HiRes qw/gettimeofday/;
+use Tree::Simple;
 
 my @fudge_t = ( 0, 0 );
 BEGIN {
@@ -14,74 +15,141 @@ BEGIN {
 
 BEGIN { use_ok("Catalyst::Stats") };
 
+{
+    my $stats = Catalyst::Stats->new;
+    is (ref($stats), "Catalyst::Stats", "new");
 
-my $stats = Catalyst::Stats->new;
-is (ref($stats), "Catalyst::Stats", "new");
+    my @expected; # level, string, time
 
-my @expected; # level, string, time
+    $fudge_t[0] = 1;
+    ok($stats->profile("single comment arg"), "profile");
+    push(@expected, [ 0, "- single comment arg", 1, 0 ]);
 
-$fudge_t[0] = 1;
-ok($stats->profile("single comment arg"), "profile");
-push(@expected, [ 0, "- single comment arg", 1, 0 ]);
+    $fudge_t[0] = 3;
+    $stats->profile(comment => "hash comment arg");
+    push(@expected, [ 0, "- hash comment arg", 2, 0 ]);
 
-$fudge_t[0] = 3;
-$stats->profile(comment => "hash comment arg");
-push(@expected, [ 0, "- hash comment arg", 2, 0 ]);
+    $fudge_t[0] = 10;
+    $stats->profile(begin => "block", comment => "start block");
+    push(@expected, [ 0, "block - start block", 4, 1 ]);
 
-$fudge_t[0] = 10;
-$stats->profile(begin => "block", comment => "start block");
-push(@expected, [ 0, "block - start block", 4, 1 ]);
 
+    $fudge_t[0] = 11;
+    $stats->profile("inside block");
+    push(@expected, [ 1, "- inside block", 1, 0 ]);
 
-$fudge_t[0] = 11;
-$stats->profile("inside block");
-push(@expected, [ 1, "- inside block", 1, 0 ]);
+    $fudge_t[1] = 100000;
+    my $uid = $stats->profile(begin => "nested block", uid => "boo");
+    push(@expected, [ 1, "nested block", 0.7, 1 ]);
+    is ($uid, "boo", "set UID");
 
-$fudge_t[1] = 100000;
-my $uid = $stats->profile(begin => "nested block", uid => "boo");
-push(@expected, [ 1, "nested block", 0.7, 1 ]);
-is ($uid, "boo", "set UID");
+    $stats->enable(0);
+    $fudge_t[1] = 150000;
+    $stats->profile("this shouldn't appear");
+    $stats->enable(1);
 
-$stats->enable(0);
-$fudge_t[1] = 150000;
-$stats->profile("this shouldn't appear");
-$stats->enable(1);
+    $fudge_t[1] = 200000;
+    $stats->profile(begin => "double nested block 1");
+    push(@expected, [ 2, "double nested block 1", 0.2, 1 ]);
 
-$fudge_t[1] = 200000;
-$stats->profile(begin => "double nested block 1");
-push(@expected, [ 2, "double nested block 1", 0.2, 1 ]);
+    $stats->profile(comment => "attach to uid", parent => $uid);
 
-$stats->profile(comment => "attach to uid", parent => $uid);
+    $fudge_t[1] = 250000;
+    $stats->profile(begin => "badly nested block 1");
+    push(@expected, [ 3, "badly nested block 1", 0.35, 1 ]);
 
-$fudge_t[1] = 250000;
-$stats->profile(begin => "badly nested block 1");
-push(@expected, [ 3, "badly nested block 1", 0.35, 1 ]);
+    $fudge_t[1] = 300000;
+    $stats->profile(comment => "interleave 1");
+    push(@expected, [ 4, "- interleave 1", 0.05, 0 ]);
 
-$fudge_t[1] = 300000;
-$stats->profile(comment => "interleave 1");
-push(@expected, [ 4, "- interleave 1", 0.05, 0 ]);
+    $fudge_t[1] = 400000; # end double nested block time
+    $stats->profile(end => "double nested block 1");
 
-$fudge_t[1] = 400000; # end double nested block time
-$stats->profile(end => "double nested block 1");
+    $fudge_t[1] = 500000;
+    $stats->profile(comment => "interleave 2");
+    push(@expected, [ 4, "- interleave 2", 0.2, 0 ]);
 
-$fudge_t[1] = 500000;
-$stats->profile(comment => "interleave 2");
-push(@expected, [ 4, "- interleave 2", 0.2, 0 ]);
+    $fudge_t[1] = 600000; # end badly nested block time
+    $stats->profile(end => "badly nested block 1");
 
-$fudge_t[1] = 600000; # end badly nested block time
-$stats->profile(end => "badly nested block 1");
+    $fudge_t[1] = 800000; # end nested block time
+    $stats->profile(end => "nested block");
 
-$fudge_t[1] = 800000; # end nested block time
-$stats->profile(end => "nested block");
+    $fudge_t[0] = 14; # end block time
+    $fudge_t[1] = 0;
+    $stats->profile(end => "block", comment => "end block");
 
-$fudge_t[0] = 14; # end block time
-$fudge_t[1] = 0;
-$stats->profile(end => "block", comment => "end block");
+    push(@expected, [ 2, "- attach to uid", 0.1, 0 ]);
 
-push(@expected, [ 2, "- attach to uid", 0.1, 0 ]);
 
-my @report = $stats->report;
-is_deeply(\@report, \@expected, "report");
+    my @report = $stats->report;
+    is_deeply(\@report, \@expected, "report");
 
-is ($stats->elapsed, 14, "elapsed");
+    is ($stats->elapsed, 14, "elapsed");
+}
+
+# COMPATABILITY METHODS
+
+# accept
+{
+    my $stats = Catalyst::Stats->new;
+    my $root = $stats->{tree};
+    my $uid = $root->getUID;
+
+    my $visitor = Tree::Simple::Visitor::FindByUID->new;
+    $visitor->includeTrunk(1); # needed for this test
+    $visitor->searchForUID($uid);
+    $stats->accept($visitor);
+    is( $visitor->getResult, $root, '[COMPAT] accept()' );
+
+}
+
+# addChild
+{
+    my $stats = Catalyst::Stats->new;
+    my $node = Tree::Simple->new(
+        {
+            action  => 'test',
+            elapsed => '10s',
+            comment => "",
+        }
+    );
+
+    $stats->addChild( $node );
+
+    my $actual = $stats->{ tree }->{ _children }->[ 0 ];
+    is( $actual, $node, '[COMPAT] addChild()' );
+    is( $actual->getNodeValue->{ elapsed }, 10, '[COMPAT] addChild(), data munged' );
+}
+
+# setNodeValue
+{
+    my $stats = Catalyst::Stats->new;
+    my $stat = {
+        action  => 'test',
+        elapsed => '10s',
+        comment => "",
+    };
+
+    $stats->setNodeValue( $stat );
+
+    is_deeply( $stats->{tree}->getNodeValue, { action => 'test', elapsed => 10, comment => '' }   , '[COMPAT] setNodeValue(), data munged' );
+}
+
+# getNodeValue
+{
+    my $stats = Catalyst::Stats->new;
+    my $expected = $stats->{tree}->getNodeValue->{t};
+    is_deeply( $stats->getNodeValue, $expected, '[COMPAT] getNodeValue()' );
+}
+
+# traverse
+{
+    my $stats = Catalyst::Stats->new;
+    $stats->{tree}->addChild( Tree::Simple->new( { foo => 'bar' } ) );
+    my @value;
+    $stats->traverse( sub { push @value, shift->getNodeValue->{ foo }; } );
+
+    is_deeply( \@value, [ 'bar' ], '[COMPAT] traverse()' );
+}
 
index 83f9f72..852d2d4 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests=>7;
+use Test::More tests => 8;
 
 use strict;
 use warnings;
@@ -9,18 +9,36 @@ use warnings;
 
 BEGIN { use_ok 'Catalyst::Utils' }
 use FindBin;
+use Path::Class::Dir;
 
-$INC{'TestApp.pm'} = "$FindBin::Bin/something/script/foo/../../lib/TestApp.pm";
-my $home = Catalyst::Utils::home('TestApp');
-like($home, qr/t\/something/, "has path TestApp/t/something"); 
-unlike($home, qr/\/script\/foo/, "doesn't have path /script/foo");
+{
+    $INC{'TestApp.pm'} = "$FindBin::Bin/something/script/foo/../../lib/TestApp.pm";
+    my $home = Catalyst::Utils::home('TestApp');
+    like($home, qr{t[\/\\]something}, "has path TestApp/t/something"); 
+    unlike($home, qr{[\/\\]script[\/\\]foo}, "doesn't have path /script/foo");
+}
 
-$INC{'TestApp.pm'} = "$FindBin::Bin/something/script/foo/bar/../../../lib/TestApp.pm";
-$home = Catalyst::Utils::home('TestApp');
-like($home, qr/t\/something/, "has path TestApp/t/something"); 
-unlike($home, qr/\/script\/foo\/bar/, "doesn't have path /script/foo");
+{
+    $INC{'TestApp.pm'} = "$FindBin::Bin/something/script/foo/bar/../../../lib/TestApp.pm";
+    my $home = Catalyst::Utils::home('TestApp');
+    like($home, qr{t[\/\\]something}, "has path TestApp/t/something"); 
+    unlike($home, qr{[\/\\]script[\/\\]foo[\/\\]bar}, "doesn't have path /script/foo/bar");
+}
 
-$INC{'TestApp.pm'} = "$FindBin::Bin/something/script/../lib/TestApp.pm";
-$home = Catalyst::Utils::home('TestApp');
-like($home, qr/t\/something/, "has path TestApp/t/something"); 
-unlike($home, qr/\/script\/foo/, "doesn't have path /script/foo");
+{
+    $INC{'TestApp.pm'} = "$FindBin::Bin/something/script/../lib/TestApp.pm";
+    my $home = Catalyst::Utils::home('TestApp');
+    like($home, qr{t[\/\\]something}, "has path TestApp/t/something"); 
+    unlike($home, qr{[\/\\]script[\/\\]foo}, "doesn't have path /script/foo");
+}
+
+{
+    $INC{'TestApp.pm'} = "TestApp.pm";
+    my $dir = "$FindBin::Bin/something";
+    chdir( $dir );
+  
+    my $home = Catalyst::Utils::home('TestApp');
+
+    $dir = Path::Class::Dir->new( $dir );
+    is( $home, "$dir", 'same dir loading' );
+}