[Jifty-commit] r1486 - in jifty/trunk: . lib lib/Jifty lib/Jifty/Plugin plugins/Login/doc plugins/Login/share/web/static plugins/Login/t

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Wed Jul 5 21:54:11 EDT 2006


Author: jpeacock
Date: Wed Jul  5 21:54:05 2006
New Revision: 1486

Added:
   jifty/trunk/lib/Jifty/Plugin/
   jifty/trunk/lib/Jifty/Plugin/ClassLoader.pm
   jifty/trunk/plugins/Login/doc/
   jifty/trunk/plugins/Login/share/po/
   jifty/trunk/plugins/Login/share/web/static/
   jifty/trunk/plugins/Login/t/
Modified:
   jifty/trunk/   (props changed)
   jifty/trunk/MANIFEST
   jifty/trunk/lib/Jifty.pm
   jifty/trunk/lib/Jifty/Plugin.pm
   jifty/trunk/lib/Jifty/Util.pm

Log:
Merging back plugins_rewrite branch, since it seems to all work with wifty
now.  As soon as someone confirms that I haven't broken anything else, I'll
make the changes to wifty to use the Login plugin.

Modified: jifty/trunk/MANIFEST
==============================================================================
--- jifty/trunk/MANIFEST	(original)
+++ jifty/trunk/MANIFEST	Wed Jul  5 21:54:05 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/trunk/lib/Jifty.pm
==============================================================================
--- jifty/trunk/lib/Jifty.pm	(original)
+++ jifty/trunk/lib/Jifty.pm	Wed Jul  5 21:54:05 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;
@@ -128,8 +126,13 @@
         my $class = "Jifty::Plugin::".(keys %{$plugin})[0];
         my %options = %{ $plugin->{(keys %{$plugin})[0]} };
         Jifty::Util->require($class);
+        Jifty::ClassLoader->new(base => $class)->require;
         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/trunk/lib/Jifty/Plugin.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin.pm	(original)
+++ jifty/trunk/lib/Jifty/Plugin.pm	Wed Jul  5 21:54:05 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/trunk/lib/Jifty/Plugin/ClassLoader.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Plugin/ClassLoader.pm	Wed Jul  5 21:54:05 2006
@@ -0,0 +1,198 @@
+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!;
+
+    # Note that at this point, all of the plugins classes will already be
+    # loaded, so we can just check their presence when deciding whether
+    # this is a class the plugin intends to autocreate
+    if ( $module =~ m{^(?:$base)::(Notification|CurrentUser)$} ) {
+        my $method = "${plugin}::$1";
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/$method/;\n"
+                . "sub _autogenerated { 1 };\n"
+                . "1;" ) 
+            if Jifty::Util->already_required($method);
+    } 
+    elsif ( $module =~ m{^(?:$base)::Action::([^\.]+)$} ) {
+        my $method = $1;
+
+        # Check to see if this is an action for a model that this plugin 
+        # doesn't support
+        if ( $method =~ m/^(?:Create|Update|Delete)([^\.]+)$/ ) {
+            my $model = "${plugin}::Model::$1";
+            return undef unless Jifty::Util->already_required($model);
+        }
+
+        $method = "${plugin}::Action::$method";
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/$method/;\n"
+                . "sub autogenerated { 1 };\n"
+                . "1;" )
+            if Jifty::Util->already_required($method);
+    } 
+
+    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;

Modified: jifty/trunk/lib/Jifty/Util.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Util.pm	(original)
+++ jifty/trunk/lib/Jifty/Util.pm	Wed Jul  5 21:54:05 2006
@@ -190,8 +190,7 @@
         return 0;
     }
 
-    my $path =  join('/', split(/::/,$class)).".pm";
-    return 1 if $INC{$path};
+    return 1 if $self->already_required($class);
 
     my $retval = $class->require;
     if ($UNIVERSAL::require::ERROR) {
@@ -210,6 +209,12 @@
     return 1;
 }
 
+sub already_required {
+    my ($self, $class) = @_;
+    my $path =  join('/', split(/::/,$class)).".pm";
+    return ( $INC{$path} ? 1 : 0);
+}
+
 =head1 AUTHOR
 
 Various folks at Best Practical Solutions, LLC.


More information about the Jifty-commit mailing list