[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