[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