[Jifty-commit] r1467 - in jifty/branches/plugin_rewrite: . lib/Jifty lib/Jifty/Plugin

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Mon Jul 3 21:27:32 EDT 2006


Author: jpeacock
Date: Mon Jul  3 21:27:28 2006
New Revision: 1467

Added:
   jifty/branches/plugin_rewrite/lib/Jifty/Plugin/
   jifty/branches/plugin_rewrite/lib/Jifty/Plugin/ClassLoader.pm
Modified:
   jifty/branches/plugin_rewrite/   (props changed)
   jifty/branches/plugin_rewrite/MANIFEST
   jifty/branches/plugin_rewrite/lib/Jifty.pm
   jifty/branches/plugin_rewrite/lib/Jifty/Plugin.pm

Log:
Go back to previously working plugin model.  

Creates three @INC entries (in order):
1) on behalf of plugin class (using Jifty-generated classes)
2) on behalf of application (using plugin-generate classes)
3) on behalf of application (using Jifty-generated classes)

Tested with Wifty by deleting all login-related files:
!      share/web/templates/let/confirm_email
!      share/web/templates/signup
!      share/web/templates/login
!      share/web/templates/logout
M      lib/Wifty/Model/User.pm
!      lib/Wifty/Notification/ConfirmAddress.pm
!      lib/Wifty/CurrentUser.pm
!      lib/Wifty/Action/ConfirmEmail.pm
!      lib/Wifty/Action/Signup.pm
!      lib/Wifty/Action/ResetLostPassword.pm
!      lib/Wifty/Action/Login.pm
!      lib/Wifty/Action/RecoverPassword.pm
!      lib/Wifty/Action/Logout.pm
!      lib/Wifty/Action/SendAccountConfrimation.pm
!      lib/Wifty/Action/SendPasswordReminder.pm
M      etc/config.yml

Still throws unnecessary errors for Models that the plugin has no intention
of supporting (but that Jifty will).

Modified: jifty/branches/plugin_rewrite/MANIFEST
==============================================================================
--- jifty/branches/plugin_rewrite/MANIFEST	(original)
+++ jifty/branches/plugin_rewrite/MANIFEST	Mon Jul  3 21:27:28 2006
@@ -129,6 +129,7 @@
 lib/Jifty/Notification.pm
 lib/Jifty/Object.pm
 lib/Jifty/Plugin.pm
+lib/Jifty/Plugin/ClassLoader.pm
 lib/Jifty/Record.pm
 lib/Jifty/Request.pm
 lib/Jifty/Request/Mapper.pm

Modified: jifty/branches/plugin_rewrite/lib/Jifty.pm
==============================================================================
--- jifty/branches/plugin_rewrite/lib/Jifty.pm	(original)
+++ jifty/branches/plugin_rewrite/lib/Jifty.pm	Mon Jul  3 21:27:28 2006
@@ -119,8 +119,6 @@
     push @Jifty::Record::ISA, Jifty->config->framework('Database')->{'RecordBaseClass'};
 
     __PACKAGE__->logger( Jifty::Logger->new( $args{'logger_component'} ) );
-    # Get a classloader set up
-    Jifty::ClassLoader->new(base => Jifty->config->framework('ApplicationClass'))->require;
 
     # Set up plugins
     my @plugins;
@@ -130,6 +128,10 @@
         Jifty::Util->require($class);
         push @plugins, $class->new(%options);
     }
+
+    # Get a classloader set up
+    Jifty::ClassLoader->new(base => Jifty->config->framework('ApplicationClass'))->require;
+
     __PACKAGE__->plugins(@plugins);
     __PACKAGE__->handler(Jifty::Handler->new());
     __PACKAGE__->api(Jifty::API->new());

Modified: jifty/branches/plugin_rewrite/lib/Jifty/Plugin.pm
==============================================================================
--- jifty/branches/plugin_rewrite/lib/Jifty/Plugin.pm	(original)
+++ jifty/branches/plugin_rewrite/lib/Jifty/Plugin.pm	Mon Jul  3 21:27:28 2006
@@ -54,6 +54,13 @@
     Jifty::ClassLoader->new(base => $class)->require;
     Jifty::Util->require($class->dispatcher);
 
+    # Start a plugin classloader set up on behalf of the application
+    require Jifty::Plugin::ClassLoader;
+    Jifty::Plugin::ClassLoader->new(
+	base => Jifty->config->framework('ApplicationClass'),
+	plugin => $class,
+    )->require;
+
     # XXX TODO: Add .po path
 
     my $self = bless {} => $class;

Added: jifty/branches/plugin_rewrite/lib/Jifty/Plugin/ClassLoader.pm
==============================================================================
--- (empty file)
+++ jifty/branches/plugin_rewrite/lib/Jifty/Plugin/ClassLoader.pm	Mon Jul  3 21:27:28 2006
@@ -0,0 +1,218 @@
+use warnings;
+use strict;
+
+package Jifty::Plugin::ClassLoader;
+
+=head1 NAME
+
+Jifty::Plugin::ClassLoader - Autogenerates application classes
+
+=head1 DESCRIPTION
+
+C<Jifty::Plugin::ClassLoader> loads additional model and action classes on
+behalf of the application out of the configured plugin classes.  Unlike, 
+C<Jifty::ClassLoader>, this class will only autogenerate classes if the
+plugin provides them.  The plugin classes are checked before the base Jifty
+classes, so that a plugin can override the Jifty class, just as any
+existing application classes will be loaded first.
+
+=head2 new
+
+Returns a new ClassLoader object.  Doing this installs a hook into
+C<@INC> that allows L<Jifty::Plugin::ClassLoader> to dynamically create
+needed classes if they do not exist already.  This works because if
+use/require encounters a blessed reference in C<@INC>, it will
+invoke the INC method with the name of the module it is searching
+for on the reference.
+
+Takes two mandatory arguments, C<base>, which should be the 
+application's base path; and C<plugin> which is the plugin classname.
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self = bless {@_}, $class;
+
+    push @INC, $self;
+    return $self;
+}
+
+=head2 INC
+
+The hook that is called when a module has been C<require>'d that
+cannot be found on disk.  The following stub classes are
+auto-generated:
+
+=over
+
+=item I<Application>
+
+An empty application base class is created that doen't provide any
+methods or inherit from anything.
+
+=item I<Application>::Record
+
+An empty class that descends from L<Jifty::Record> is created.
+
+=item I<Application>::Collection
+
+An empty class that descends from L<Jifty::Collection> is created.
+
+=item I<Application>::Notification
+
+An empty class that descends from L<Jifty::Notification>.
+
+=item I<Application>::Dispatcher
+
+An empty class that descends from L<Jifty::Dispatcher>.
+
+=item I<Application>::Bootstrap
+
+An empty class that descends from L<Jifty::Bootstrap>.
+
+=item I<Application>::Upgrade
+
+An empty class that descends from L<Jifty::Upgrade>.
+
+=item I<Application>::CurrentUser
+
+An empty class that descends from L<Jifty::CurrentUser>.
+
+=item I<Application>::Model::I<Anything>Collection
+
+If C<I<Application>::Model::I<Something>> is a valid model class, then
+it creates a subclass of L<Jifty::Collection> whose C<record_class> is
+C<I<Application>::Model::I<Something>>.
+
+=item I<Application>::Action::(Create or Update or Delete)I<Anything>
+
+If C<I<Application>::Model::I<Something>> is a valid model class, then
+it creates a subclass of L<Jifty::Action::Record::Create>,
+L<Jifty::Action::Record::Update>, or L<Jifty::Action::Record::Delete>
+whose I<record_class> is C<I<Application>::Model::I<Something>>.
+
+=back
+
+=cut
+
+# This subroutine's name is fully qualified, as perl will ignore a 'sub INC'
+sub Jifty::Plugin::ClassLoader::INC {
+    my ( $self, $module ) = @_;
+
+    my $base = $self->{base};
+    my $plugin = $self->{plugin};
+    return undef unless ( $module and $base and $plugin);
+
+    # Canonicalize $module to :: style rather than / and .pm style;
+    $module =~ s/.pm$//;
+    $module =~ s{/}{::}g;
+
+    # The quick check
+    return undef unless $module =~ m!^($base|$plugin)!;
+
+    if ( $module =~ m{^(?:$base)::(Notification|CurrentUser)$} ) {
+        my $toplevel = "${plugin}::$1";
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/$toplevel/;\n"
+                . "sub _autogenerated { 1 };\n"
+                . "1;" ) 
+            if ($toplevel->require);
+    } 
+    elsif ( $module =~ m{^(?:$base)::Action::([^\.]+)$} ) {
+        my $action = "${plugin}::Action::$1";
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/$action/;\n"
+                . "sub autogenerated { 1 };\n"
+                . "1;" )
+            if ($action->require);
+    } 
+    # now generate inherited classes for the plugin itself
+    elsif ( $module =~ m{^(?:$plugin)::Action::(Create|Update|Delete)([^\.]+)$} ) {
+        my $modelclass = $plugin . "::Model::" . $2;
+	return undef if
+	    ( keys %{$self->{models}} ) &&
+	    ( not defined $self->{models}{$modelclass});
+
+        Jifty::Util->require($modelclass);
+
+        return undef unless eval { $modelclass->table };
+
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/Jifty::Action::Record::$1/;\n"
+                . "sub record_class { '$modelclass' };\n"
+                . "sub autogenerated { 1 };\n"
+                . "1;" );
+    }
+    elsif ( $module =~ m{^(?:$plugin)::(Record|Collection|Notification|Dispatcher|Bootstrap|Upgrade)$} ) {
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/Jifty::$1/;\n"
+                . "sub _autogenerated { 1 };\n"
+                . "1;" );
+    } 
+    elsif ( $module =~ m{^(?:$plugin)::Model::(\w+)Collection$} ) {
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/@{[$plugin]}::Collection/;\n"
+                . "sub record_class { '@{[$plugin]}::Model::$1' }\n"
+                . "1;" );
+    }
+
+    return undef;
+}
+
+=head2 return_class CODE
+
+A helper method; takes CODE as a string and returns an open filehandle
+containing that CODE.
+
+=cut
+
+sub return_class {
+    my $self = shift;
+    my $content = shift;
+    open my $fh, '<', \$content;
+    return $fh;
+
+}
+
+=head2 require
+
+Loads all of the application's Actions and Models.  It additionally
+C<require>'s all Collections and Create/Update actions for each Model
+base class -- which will auto-create them using the above code if they
+do not exist on disk.
+
+=cut
+
+sub require {
+    my $self = shift;
+    
+    my $base = $self->{plugin};
+
+    # if we don't even have an application class, this trick will not work
+    return unless ($base); 
+    Jifty::Util->require($base);
+    Jifty::Util->require($base."::CurrentUser");
+
+    Module::Pluggable->import(
+        search_path =>
+          [ map { $base . "::" . $_ } 'Model', 'Action', 'Notification' ],
+        require => 1,
+        except  => qr/\.#/,
+        inner   => 0
+    );
+    $self->{models}{$_} = 1 for grep {/^($base)::Model::(.*)$/ and not /Collection$/} $self->plugins;
+    for my $full (keys %{$self->{models}}) {
+        my($short) = $full =~ /::Model::(.*)/;
+        Jifty::Util->require($full . "Collection");
+        Jifty::Util->require($base . "::Action::" . $_ . $short)
+            for qw/Create Update Delete/;
+    }
+}
+
+1;


More information about the Jifty-commit mailing list