also was verursacht mal probleme bei modulen:
- administration der module
- namespace (wo werden die module hin-geladen)
- ladereihenfolge (wie werden module geladen, module sollen code ueberschreiben/erweitern koennen)
- wie wird code aus den modulen, bzw der core aus modulen aus, angesprochen,
zu 1)
das ist mal das leichteste. zum einen koennen ueber das webtek-script module angelegt werden, bzw in den modulen Pages, Models, migrations, etc. erzeugt werden.
./webtek module comments
./webtek --module comments Page Comment
./webtek --module comments migrate create comment_table
# ausfuehren von migrations (u.a. in den modulen tags, comments)
./webtek --modules comments,tags migrate up
zum anderen definiert man in der httpd.conf welche module geladen werden
PerlSetVar module comments,tags
zu 2)
wenn wir jetzt unser Weblog nehmen, dann sind die Perl-Module unter Weblog::Model::Comment, Weblog::Page::Comment usw erreichbar. jetzt wollte ich es so, dass die kommentare unter Weblog::Model::Comment bzw, Weblog::Page::Comment erreichbar sind. das hat den vorteil, dass man noetigenfalls den modul-code nochmal per applikation abaendern/ueberschreiben kann. damit das funktioniert kommen wir mit dem herkoemmlichen:
package Weblog::Model::Comment
...
use Weblog::Model::Comment
nicht mehr aus. darum gibt es jetzt WebTek::Module welches das laden der module uebernimmt. d.h. u.a. das richtige package definiert. aber das wiederum loest noch nicht das use problem, denn auch dort muss magic passieren, um fuer einen package-namen das richtige modul zu laden, und perl waere nich perl wenn man nicht auch das hacken koennte:
my $prefix = app->class_prefix; # liefert in unserem fall 'Weblog'
*CORE::GLOBAL::require = sub {
my $package = shift;
my ($pkg, $ret) = ($package, undef);
if ($pkg =~ /$prefix/) {
$pkg =~ s/\//::/g;
$pkg =~ s/.pm$//;
$ret = WebTek::Module->require($pkg);
} else {
$ret = eval { CORE::require($package); 1 } or die $@;
}
return $ret;
};
somit wird ein package, welches in unserer Application liegt ueber WebTek::Module geladen, alle anderen ueber den konventionellen weg (file suchen in @INC usw.).
zu 3)
diese funktionalitaet uerbnimmt auch WebTek::Module. wenn wir z.b. das File Model/Comment.pm laden wollen, dann wird dieses einfach ueberall in allen core/modulen gesucht und der reihe nach geladen. somit ist es auch moeglich modul-code in einem anderen (spaeter geladenen modul) wieder zu ueberschreiben (ob das sinnvoll ist weisz ich nicht)
zu 4)
im tags modul gibt es eine Page welche uns alle Items fuer einen tag rendert. wir haetten das gerne unter der url:
/weblog/tag/<tag_name>
normalerweise wuerden wir das so definieren
package Weblog::Page::Tag
use WebTek::Parent qw( Weblog::Page::Root );
sub new_for_tag :Path(tag/(\w+)) {
my ($class, $path, $tagname) = @_;
...
}
...
1;
hier sind aber leider haufenweise absolute module-namen (=schlecht). darum koennen wir jetzt schreiben.
use WebTek::Parent app->Page->Root
sub new_for_tag :Path(tag/(\w+)) {
my ($class, $path, $tagname) = @_;
...
}
...
die package definietion koennen wir uns sparen (uebernimmt WebTek::Module), und alle package-namen koennen wir in der form:
app->Page->Root
app->Model->Tag
...
definieren. weiters zur demo noch z.b. das items-count macro:
sub count :Macro :Param(render the number of items of this tag)
:Param(disabled='1' count also disabled items for this tag, default 0)
{
my ($self, %params) = @_;
return app->Model->Tag->count(
'appname' => app->name,
'name' => $self->name,
'is_enabled' => [1, ($params{'disabled'} ? 0 : 1)],
);
}
damit diese magic funktioniert muss man wieder tief in der perl-trickkiste kramen (vereinfachte from von WebTek::App).
package WebTek::App;
...
our $AUTOLOAD;
sub AUTOLOAD {
my $name = $AUTOLOAD =~ /::([^:]+)$/ ? $1 : $AUTOLOAD;
return if $name eq 'DESTROY';
return WebTek::App::Class->___new___($App->class_prefix . "::$name");
}
package WebTek::App::Class;
use overload '""' => sub { my $self = shift; return $$self };
our $AUTOLOAD;
sub ___new___ {
my ($class, $name) = @_;
return bless \$name, $class;
}
sub can { my $self = shift; $$self->can(@_) }
sub isa { my $self = shift; $$self->isa(@_) }
sub DOES { my $self = shift; $$self->DOES(@_) }
sub VERSION { my $self = shift; $$self->VERSION(@_) }
sub AUTOLOAD {
my $self = shift;
my $name = $AUTOLOAD =~ /::([^:]+)$/ ? $1 : $AUTOLOAD;
return if $name eq 'DESTROY';
return $$self->$name(@_) if UNIVERSAL::can($$self, $name);
return WebTek::App::Class->___new___("$self\::$name");
}
naja das waeren mal ein paar eckpunkte der module-implementiereung in webtek.
Neuen Kommentar schreiben: