Added plugin introspection.
Curtis "Ovid" Poe [Fri, 17 Feb 2006 23:18:26 +0000 (23:18 +0000)]
Now you can call $c->registered_plugins to find out which plugins are loaded.
Also, you can now use fully qualified plugin names when you load Catalyst:

use Catalyst qw(
DateTime
+Fully::Qualified::Plugin::Name
);

This makes it easy to add plugins which do not start with
"Catalyst::Plugin::".

lib/Catalyst.pm
t/lib/Catalyst/Plugin/Test/Headers.pm
t/lib/TestApp.pm
t/live_plugin_loaded.t
t/unit_core_plugin.t [new file with mode: 0644]

index d93707f..a0c4d40 100644 (file)
@@ -178,6 +178,14 @@ C<My::Module>.
 
     use Catalyst qw/My::Module/;
 
+If your plugin starts with a name other than C<Catalyst::Plugin::>, you can
+fully qualify the name by using a unary plus:
+
+    use Catalyst qw/
+        My::Module
+        +Fully::Qualified::Plugin::Name
+    /;
+
 Special flags like C<-Debug> and C<-Engine> can also be specified as
 arguments when Catalyst is loaded:
 
@@ -534,12 +542,7 @@ loads and instantiates the given class.
 
 sub plugin {
     my ( $class, $name, $plugin, @args ) = @_;
-    $plugin->require;
-
-    if ( my $error = $UNIVERSAL::require::ERROR ) {
-        Catalyst::Exception->throw(
-            message => qq/Couldn't load instant plugin "$plugin", "$error"/ );
-    }
+    $class->_register_plugin($plugin, 1);
 
     eval { $plugin->import };
     $class->mk_classdata($name);
@@ -1841,25 +1844,63 @@ Sets up plugins.
 
 =cut
 
-sub setup_plugins {
-    my ( $class, $plugins ) = @_;
+=head2 $c->registered_plugins 
+
+Returns a sorted list of the plugins which have either been stated in the
+import list or which have been added via C<< MyApp->plugin(@args); >>.
+
+If passed a given plugin name, it will report a boolean value indicating
+whether or not that plugin is loaded.  A fully qualified name is required if
+the plugin name does not begin with C<Catalyst::Plugin::>.
+
+ if ($c->registered_plugins('Some::Plugin')) {
+     ...
+ }
+
+=cut
 
-    $plugins ||= [];
-    for my $plugin ( reverse @$plugins ) {
+{
+    my %PLUGINS;
+    sub registered_plugins { 
+        my $proto = shift;
+        return sort keys %PLUGINS unless @_;
+        my $plugin = shift;
+        return 1 if exists $PLUGINS{$plugin};
+        return exists $PLUGINS{"Catalyst::Plugin::$plugin"};
+    }
 
-        $plugin = "Catalyst::Plugin::$plugin";
+    sub _register_plugin {
+        my ( $proto, $plugin, $instant ) = @_;
+        my $class = ref $proto || $proto;
 
         $plugin->require;
 
-        if ($@) {
+        if ( my $error = $@ ) {
+            my $type = $instant ? "instant " : '';
             Catalyst::Exception->throw(
-                message => qq/Couldn't load plugin "$plugin", "$@"/ );
+                message => qq/Couldn't load ${type}plugin "$plugin", $error/ );
         }
 
-        {
+        $PLUGINS{$plugin} = 1;        
+        unless ($instant) {
             no strict 'refs';
             unshift @{"$class\::ISA"}, $plugin;
         }
+        return $class;
+    }
+
+    sub setup_plugins {
+        my ( $class, $plugins ) = @_;
+
+        $plugins ||= [];
+        for my $plugin ( reverse @$plugins ) {
+
+            unless ( $plugin =~ s/\A\+// ) {
+                $plugin = "Catalyst::Plugin::$plugin";
+            }
+
+            $class->_register_plugin($plugin);
+        }
     }
 }
 
index c5a89cb..ac47209 100644 (file)
@@ -17,7 +17,7 @@ sub prepare {
 
     {
         no strict 'refs';
-        my $plugins = join( ', ', sort grep { m/^Catalyst::Plugin/ } @{ $class . '::ISA' } );
+        my $plugins = join ', ', $class->registered_plugins;
         $c->response->header( 'X-Catalyst-Plugins' => $plugins );
     }
 
index 8630a98..00cde9a 100644 (file)
@@ -1,7 +1,12 @@
 package TestApp;
 
 use strict;
-use Catalyst qw/Test::Errors Test::Headers Test::Plugin/;
+use Catalyst qw/
+    Test::Errors 
+    Test::Headers 
+    Test::Plugin
+    +TestApp::Plugin::FullyQualified
+/;
 use Catalyst::Utils;
 
 our $VERSION = '0.01';
index 57a1646..36f3336 100644 (file)
@@ -13,6 +13,7 @@ my @expected = qw[
   Catalyst::Plugin::Test::Errors
   Catalyst::Plugin::Test::Headers
   Catalyst::Plugin::Test::Plugin
+  TestApp::Plugin::FullyQualified
 ];
 
 my $expected = join( ", ", @expected );
diff --git a/t/unit_core_plugin.t b/t/unit_core_plugin.t
new file mode 100644 (file)
index 0000000..46647fb
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 20;
+
+use lib 't/lib';
+
+{
+
+    package Faux::Plugin;
+
+    sub new { bless {}, shift }
+    my $count = 1;
+    sub count { $count++ }
+}
+
+{
+
+    package PluginTestApp;
+    use Test::More;
+
+    use Catalyst qw(
+      Test::Plugin
+      +TestApp::Plugin::FullyQualified
+    );
+
+    sub compile_time_plugins : Local {
+        my ( $self, $c ) = @_;
+
+        isa_ok $c, 'Catalyst::Plugin::Test::Plugin';
+        isa_ok $c, 'TestApp::Plugin::FullyQualified';
+
+        can_ok $c, 'registered_plugins';
+        $c->_test_plugins;
+
+        $c->res->body("ok");
+    }
+
+    sub run_time_plugins : Local {
+        my ( $self, $c ) = @_;
+
+        $c->_test_plugins;
+        my $faux_plugin = 'Faux::Plugin';
+
+        # Trick perl into thinking the plugin is already loaded
+        $INC{'Faux/Plugin.pm'} = 1;
+
+        __PACKAGE__->plugin( faux => $faux_plugin );
+
+        isa_ok $c, 'Catalyst::Plugin::Test::Plugin';
+        isa_ok $c, 'TestApp::Plugin::FullyQualified';
+        ok !$c->isa($faux_plugin),
+          '... and it should not inherit from the instant plugin';
+        can_ok $c, 'faux';
+        is $c->faux->count, 1, '... and it should behave correctly';
+        is_deeply [ $c->registered_plugins ],
+            [
+                qw/Catalyst::Plugin::Test::Plugin
+                   Faux::Plugin
+                   TestApp::Plugin::FullyQualified/
+            ],
+            'registered_plugins() should report all plugins';
+        ok $c->registered_plugins('Faux::Plugin'),
+            '... and even the specific instant plugin';
+
+        $c->res->body("ok");
+    }
+
+    sub _test_plugins {
+        my $c = shift;
+        is_deeply [ $c->registered_plugins ],
+            [
+                qw/Catalyst::Plugin::Test::Plugin
+                   TestApp::Plugin::FullyQualified/
+            ],
+            '... and it should report the correct plugins';
+        ok $c->registered_plugins('Catalyst::Plugin::Test::Plugin'),
+            '... or if we have a particular plugin';
+        ok $c->registered_plugins('Test::Plugin'),
+            '... even if it is not fully qualified';
+        ok !$c->registered_plugins('No::Such::Plugin'),
+            '... and it should return false if the plugin does not exist';
+    }
+
+    __PACKAGE__->setup;
+}
+
+use Catalyst::Test qw/PluginTestApp/;
+
+ok( get("/compile_time_plugins"), "get ok" );
+ok( get("/run_time_plugins"),     "get ok" );