[Catalyst-commits] r7867 - in
Catalyst-Runtime/5.70/branches/compres: lib t
bricas at dev.catalyst.perl.org
bricas at dev.catalyst.perl.org
Fri May 30 13:34:31 BST 2008
Author: bricas
Date: 2008-05-30 13:34:31 +0100 (Fri, 30 May 2008)
New Revision: 7867
Modified:
Catalyst-Runtime/5.70/branches/compres/lib/Catalyst.pm
Catalyst-Runtime/5.70/branches/compres/t/unit_core_component.t
Catalyst-Runtime/5.70/branches/compres/t/unit_core_mvc.t
Log:
rework component resolution methods (i.e. model(), models(), etc), plus add in some regexp specific behavior
Modified: Catalyst-Runtime/5.70/branches/compres/lib/Catalyst.pm
===================================================================
--- Catalyst-Runtime/5.70/branches/compres/lib/Catalyst.pm 2008-05-30 08:03:45 UTC (rev 7866)
+++ Catalyst-Runtime/5.70/branches/compres/lib/Catalyst.pm 2008-05-30 12:34:31 UTC (rev 7867)
@@ -414,87 +414,60 @@
$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 ) = @_;
+ # 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 };
- foreach my $name (@names) {
- foreach my $component ( keys %{ $c->components } ) {
- return $c->components->{$component} if $component =~ /$name/i;
- }
- }
+ # undef for a name will return all
+ return keys %eligible if !defined $name;
- return undef;
-}
+ my $query = ref $name ? $name : qr/^$name$/i;
+ my @result = grep { $eligible{$_} =~ m{$query} } keys %eligible;
-# try explicit component names
-sub _comp_explicit {
- my ( $c, @names ) = @_;
+ return map { $c->components->{ $_ } } @result if @result;
- foreach my $try (@names) {
- return $c->components->{$try} if ( exists $c->components->{$try} );
- }
+ # if we were given a regexp to search against, we're done.
+ return if ref $name;
- return undef;
-}
+ # regexp fallback
+ $query = qr/$name/i;
+ @result = grep { $eligible{ $_ } =~ m{$query} } keys %eligible;
-# 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 ) = @_;
+ # don't warn if we didn't find any results, it just might not exist
+ if( @result ) {
+ $c->log->warn( 'Relying on the regexp fallback behavior for component resolution' );
+ $c->log->warn( 'is unreliable and unsafe. You have been warned' );
+ }
- my $appclass = ref $c || $c;
-
- my @names = map { "${appclass}::${_}::${name}" } @prefixes;
-
- 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 $filter = "^${appclass}::(" . join( '|', @prefixes ) . ')::';
- my @names;
-
- COMPONENT: foreach my $comp ($c->component) {
- foreach my $p (@pre) {
- if ($comp =~ s/^$p//) {
- push(@names, $comp);
- next COMPONENT;
- }
- }
- }
-
+ 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
@@ -512,9 +485,13 @@
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 );
}
@@ -536,9 +513,13 @@
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};
@@ -547,19 +528,17 @@
}
return $c->model( $c->config->{default_model} )
if $c->config->{default_model};
- return $c->_filter_component( $c->_comp_singular(qw/Model M/) );
-}
+ my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/Model M/);
-=head2 $c->controllers
+ 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' );
+ }
-Returns the available names which can be passed to $c->controller
-
-=cut
-
-sub controllers {
- my ( $c ) = @_;
- return $c->_comp_names(qw/Controller C/);
+ return $c->_filter_component( $comp );
}
@@ -581,9 +560,13 @@
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};
@@ -592,9 +575,30 @@
}
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' );
+ }
+
+ 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
Returns the available names which can be passed to $c->model
@@ -630,32 +634,38 @@
=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 $comps->{ $name } if exists $comps->{ $name };
- my $appclass = ref $c || $c;
+ # perhaps we just omitted "MyApp"?
+ my $composed = ( ref $c || $c ) . "::${name}";
+ return $comps->{ $composed } 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;
+ }
- my $comp = $c->_comp_explicit(@names);
- return $c->_filter_component( $comp, @_ ) if defined($comp);
+ # This is here so $c->comp( '::M::' ) works
+ my $query = ref $name ? $name : qr{$name}i;
- $comp = $c->_comp_search($name);
- return $c->_filter_component( $comp, @_ ) if defined($comp);
+ my @result = grep { m{$query} } keys %{ $c->components };
+ return @result if ref $name;
+ return $result[ 0 ] if $result[ 0 ];
+
+ # 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
Modified: Catalyst-Runtime/5.70/branches/compres/t/unit_core_component.t
===================================================================
--- Catalyst-Runtime/5.70/branches/compres/t/unit_core_component.t 2008-05-30 08:03:45 UTC (rev 7866)
+++ Catalyst-Runtime/5.70/branches/compres/t/unit_core_component.t 2008-05-30 12:34:31 UTC (rev 7867)
@@ -1,4 +1,4 @@
-use Test::More tests => 7;
+use Test::More tests => 10;
use strict;
use warnings;
@@ -20,9 +20,27 @@
is(MyApp->comp('Model'), 'MyApp::M::Model', 'Single part return ok');
+# regexp fallback
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' );
+}
+
+# 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' );
+}
+
Modified: Catalyst-Runtime/5.70/branches/compres/t/unit_core_mvc.t
===================================================================
--- Catalyst-Runtime/5.70/branches/compres/t/unit_core_mvc.t 2008-05-30 08:03:45 UTC (rev 7866)
+++ Catalyst-Runtime/5.70/branches/compres/t/unit_core_mvc.t 2008-05-30 12:34:31 UTC (rev 7867)
@@ -1,4 +1,4 @@
-use Test::More tests => 27;
+use Test::More tests => 37;
use strict;
use warnings;
@@ -18,6 +18,9 @@
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->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,8 +59,15 @@
[ 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');
my $view = bless {} , 'MyApp::View::V';
@@ -61,8 +76,15 @@
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');
my $model = bless {} , 'MyApp::Model::M';
@@ -79,6 +101,34 @@
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' );
+}
+
+{
+ 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;
{
@@ -90,3 +140,4 @@
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');
+
More information about the Catalyst-commits
mailing list