Tag: webtek

erstellt von max, am 19.02.2008 13:04

so, wie schon erwaehnt hab ich ja das weblog programmiert, um webtek zu testen. es ist naemlich so, dass jetzt webtek-applikation mit modulen bereichert werden koennen. und da bietet sich halt ein weblog gerade zu an:

  • den core (beitraege schreiben/verwalten)
  • kommentare
  • tags
  • content (= zur zeit mal nur bilder)

ich hab lang hin und her ueberlegt, wie kann das mit modulen funktionieren. prinzipiell bin ich auf zwei ansaetze gekommen:

  1. es gibt module fuer eine applikation, und je nach anwendung der applikation (z.b. deren klone) verwendet man nur ein subset aller module: z.b.
    • es gibt bikemap und dort gibt es radreisen
    • es gibt runmap (gleich wie bikemap), dort gibt es aber keine laufreisen
  2. der zweite ansatz ist, es gibt module, welche fuer jede applikation verwendet werden koennen:
    • kommentare
    • tags
    • content

fuer den fall 1) ist es noch relativ leicht, da der module-code davon ausgehen kann dass gewisse code-strukturen im core enthalten sind, aber beim fall 2) muss man schon allgemeiner programmieren. richtig kompliziert wird es dann, wenn z.b. content aus dem content-modul tagbar sein soll. aber hierfuer reicht meine vorstellung nicht aus, wie sowas unkompliziert und verstaendlich programmiert werden kann. lange rede kurzer sinn, mit dem modul-framework in webtek sind bei arten module moeglich, wie genau kommt in einem anderen beitrag.

dann noch eine angeber code-statistik (weils mich selber so freut):


      53 ./Handler.pm
       5 ./Model/Post.pm
       6 ./modules/comments/Model/Comment.pm
     138 ./modules/comments/Page/Root.pm
      48 ./modules/content/Model/Content.pm
      86 ./modules/content/Page/Content.pm
      96 ./modules/content/Page/Contents.pm
      52 ./modules/tags/Model/Tag.pm
      85 ./modules/tags/Page/Root.pm
      71 ./modules/tags/Page/Tag.pm
      79 ./Page/Post.pm
     154 ./Page/Root.pm
     873 total

wen's genauer interessiert. hier ist der code. arbeitszeit war ca 50 stunden, wobei 20 fuer html/css draufgegangen sind (ich hab mir naemlich jetzt das buch CSS Cookbook besorgt. Vielleicht schaff ichs ja doch nochmal simple css geschichten selbst zu loesen). hier noch ein screenshot vom beitrag anlegen:

weblog1.jpg

naja. hat mir wieder mal viel spass gemacht das zu programmieren.

erstellt von max, am 19.02.2008 19:05

also was verursacht mal probleme bei modulen:

  1. administration der module
  2. namespace (wo werden die module hin-geladen)
  3. ladereihenfolge (wie werden module geladen, module sollen code ueberschreiben/erweitern koennen)
  4. 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.

erstellt von max, am 02.04.2008 21:24

ich hab jetzt relativ viel zeit investiert, um das rendering in webtek, von string-processing und vielen regular expressions, auf in perl-code uebersetzte templates umzubauen, ... mit maessig erfolg, denn leider ist perl's string-processing viel zu schnell um dort einen wirklichen vorteil rauszuholen.

einen aehnlichen ansatz hatte schon adrian im jahre 2001, als er fuer uboot eine templating engine in C programmiert hat, ... mit genauso wenig erfolg.

ein beispiel:

man wuerde ja denken, dass gerade die standard unix commandos schnell sind, aber...

imac:DrMap max$ time find . -not -path '*.svn*' -name '*.tpl' | wc -l
     198

real	0m0.233s
user	0m0.153s
sys	0m0.078s

braucht deutlich laenger als

imac:DrMap max$ time perl -e \
'foreach (`find .`) { next if /.svn/; next unless /.tpl\n$/; print }' \
| wc -l
     198

real	0m0.112s
user	0m0.036s
sys	0m0.083s

also dass hat mich dann schon stutzig gemacht (ich hab beide kommandos mehrere male ausgefuehrt, damit auch ja alle sys-calls im betriebssystem-cache sind).

nochmal zum eigentlichen thema. hurra jetzt ist es so, dass webtek um 5 - 10% schnellere responsezeiten hat (hab mir min. 30% erwartet). aber das programmieren hat wiedermal viel spass gemacht. der compiler ist ein einfacher rekursiver parser mit folgender syntax:

Terminale:
==========
CHAR           -> .
EQUAL          -> =
DOT            -> \.
PIPE           -> \|
ESCAPE         -> \\
QUOTE_START    -> " | ' | q\{ | q\[ | q\(
QUOTE_END      -> " | ' | \} | \] | \)
SPACE          -> \s+
NAME           -> \w+
MACRO_START    -> <%
MACRO_END      -> %>

NonTerminale:
=============
Template       -> ( Macro | CHAR )*
Macro          -> MACRO_START
                  SPACE
                  NAME ( DOT NAME )*
                  ( Param )*
                  ( PIPE SPACE NAME ( Param )* )*
                  SPACE
                  MACRO_END
Param          -> NAME EQUAL QUOTE_START Template QUOTE_END

und wiedermal, was ich an perl so mag, mit sehr wenig code-zeilen:

  • scanner: 24 zielen
  • parser: 70 zielen
  • aus dem syntax-tree perl code erzeugen: 100 zeilen

wers genauer wissen will, hier der Compiler und hier der alte Renderer.

einen kleinen trost gibt es. es sollte jetzt doch leicht moeglich sein weitere macro-funktionalitaet zu implementieren (wie z.b. ein loop macro)

so denn...

erstellt von max, am 07.05.2008 09:06

so. ich hab jetzt wiedermal einen screencast gemacht, in welchem eine weblogsoftware erstellt wird, die aehnlich dem funktionsumfang von diesem weblog ist. das weblog bekommt folgende features:

  • login/logout
  • artikel erstellen/aendern/loeschen/online-offline stellen
  • suche nach artikeln
  • tags (von tagthe.net) zum artikel speichern + tagcloud in der sidebar
  • kommentare

dabei werden folgende webtek-techniken verwendet:

  • pages/templates (eh klar)
  • events
  • paginator
  • module

soviel funktionalitaet hat auch ihren preis:

  • der screencast dauert 1h 20min (bin gespannt ob sich das wer antut, aber man hat dann wirklich einen guten einblick wie man eine webtek-applikation from scratch erstellt). ich sage am anfang der screencast wird ca. 30-40 min dauern, und wie das halt beim programmieren so ist, dauert immer alles doppelt so lang
  • es ist leider mit einer schlechten audio-quali aufgenommen, deshalb hab ich einen leichten s-sprachfehler ;) und es ist ein bisserl dumpf -> dadurch wirkt die stimme sehr beruhigend (oder auch einschlaefernd...)
  • naja, das video ist fast 200MB gross.

so will es euch nimmer laenger vorenthalten. hier ist der screencast:

http://max.xaok.org/static/weblog/webtek-weblog-demo.mov

den source-code zum nachblaettern findet man unter:

https://max.xaok.org/svn/webtek-apps/Weblog1

und um die applikation laufen zu lassen gehoert das noch ins httpd.conf file.


PerlSwitches -I/WebTek/app
PerlSwitches -I/WebTek/lib
PerlRequire /WebTek/extra/startup.pl

Alias /static/weblog /WebTek/app/Weblog1/static
<Directory /WebTek/app/Weblog1/static>
   Order allow,deny
   Allow from all
</Directory>

<Location /weblog>
   SetHandler modperl
   PerlSetVar name Weblog
   PerlSetVar dir /WebTek/app/Weblog1
   PerlSetVar modules comments
   PerlResponseHandler Weblog1::Handler
</Location>
<Location /weblog/login>
   AuthType Basic
   AuthName "WebTek Weblog Demo"
   AuthBasicProvider file
   AuthUserFile /etc/apache2/auth-files/http.passwd
   Require valid-user
</Location>

etwas kurzweile damit!

erstellt von max, am 16.05.2008 16:06

habe soeben die I18N von properties files auf po files umgestellt. grund dafuer ist, dass po files quasi der standard in I18N sind, und es dafuer schon coole Uebersetzungsprogramme gibt (z.b. poedit). weiters ist gleich eine routine dazugekommen, welche das uebersetzten sehr vereinfachen sollte:

  1. automatische message-key extraktion aus .pm und .tpl files
  2. message-files (= .po files) mit diesen keys erweitern, sofern sie noch nicht vorhanden sind
  3. erstellen von .po files fuer komplett neue sprachen

das alles geht ab jetzt mit dem webtek script, z.b.

./webtek translate de
./webtek translate de en
./webtek translate de en it

extended/erstellt die neotigen po files fuer die angegebenen sprachen. selbstverstaendlich funktioniert das auch in den modulen:

./webtek --modules comments,tags translate en

output:
update file /WebTek/app/Test/messages/en.po with 24 missing keys
update file /WebTek/app/Test/modules/comments/messages/en.po with 4 missing keys
update file /WebTek/app/Test/modules/tags/messages/en.po with 6 missing keys

in den modulen werden natuerlich nur die keys gesucht/erstellt welche auch in dem modul verwendet werden.

zu guter letzt gibt es noch ein helper-script, welches die .properties files in .po files umwandelt:

./webtek script /WebTek/extra/properties_to_po.pl

sollte alles erledigen! der code ist im trunk eingecheckt, und vielleicht noch ein bisserl beta ...

erstellt von max, am 23.05.2008 15:06

hab heut wieder etwas gebastelt und will euch das nicht vorenthalten. zum einen ETag support, zum anderen einen page-cache. weiters will ich hier gleich generell die caching-mechanismen in webtek erlaeutern:

Es gibt folgende arten von cache:

  • macro-cache
  • model-cache
  • page-cache
  • ETag (= caching auf client-seite)

Macro-Cache

um den output eines macros zu cachen, wird einfach das code-attribute Cache angegeben. alternativ kann auch noch eine zeit definiert werden. z.b.

sub posts :Macro :Cache { ... }
sub posts :Macro :Cache(300) { ... } # cached fuer 5min

Model-Cache

dieser cache erlaubt model-suchabfragen zu cachen. dieses gilt aber nur fuer die find_one methode. wenn man z.b. folgende abfrage cachen will:

my $user = app->Model->User->find_one('nickname' => 'max');

dann muss man folgenden code in das User-Model schreiben:

use WebTek::Cache qw( nickname );

folgende definition erlaubt folgende (gecache'te) abfragen:

use WebTek::Cache qw( id nickname street,zipcode );

...

my $user = app->Model->User->find_one('id' => 123);
my $user = app->Model->User->find_one('nickname' => 'max');
my $user = app->Model->User->find_one(
   'street' => $street,
   'zipcode' => $zipcode
);

wenn man das $user object aendert (sprich ein $user->update(%params) macht), wird selbstverstaendlich jeglicher cache geloescht. d.h. bei der naechsten abfrage wird das model neu von der datenbank geholt, und in den cache geschrieben. weiters ist zu beachten, wenn man den protoypen von User aendert (z.b. neue db-column), dass man auch den cache loescht, da dieser sonnst natuerlich noch die alten objekte zurueckliefert!

Page-Cache

heute neu dazugekommen ist der page-cache, welcher es erlaubt gleich ganze seiten zu cachen. hierfuer einfach das Cache attribute (wieder optional mit der zeit, defaultwert ist 1min) bei der action dazufuegen.

sub rss :Action :Cache { ... }
sub rss :Action :Cache(60) { ... }

weiters zu beachten:

  • dieser cache ist hauptsaechlich dafuer da, um peaks auf eine bestimme url zu "verkraften", nicht um die halbe applikation im cache zu halten (obwohl das auch funktioniert)
  • der cache funktioniert nur fuer requests welche ohne einen session->user sind, da meist bei eingeloggten requests die seite viel zu dynamisch ist, um einen cache effektiv arbeiten zu lassen.
  • als (teil vom) cachekey wird die komplette URI verwendet. d.h.

    /weblog/rss
    /weblog/rss?page=2
    

    sind zwei verschiedene cache eintraege.

ETag's

Ein Etag ist ein unique-key fuer eine url, welchen der client bei jedem request dieser url an den server uebermittelt. der server generiert nun fuer diese url seinen eigenen key, und wenn diese zwei uebereinstimmen schickt der server ein http-status 304 (= not modified) retour. damit man diese funktionalitaet in webtek verwenden kann muessen einfach nur die abhaenigkeiten ermittelt werden, wann sich client-cache out-of-date ist. klingt vielleicht kompliziert, ist es aber meist nicht.

z.b. wenn wir ein posting anzeigen wollen:

sub index :Action :ETag($self->post->modify_time) { ... }

reicht meist schon aus, um festzustellen, ob der client noch die aktuelle version im cache hat. man kann auch einfach mehr abhaenigkeiten definieren:

sub index :Action
   :ETag($self->post->modify_time)
   :ETag($self->post->comment_count)
{
    ...
}

Nachtrag

zu guter letzt noch die antwort auf die frage wo webtek daten cached. mit dabei ist schon ein client fuer memcached, welches einfach in der config/cache.config datei aktiviert wird:

{
   #... define the class which should be used for caching
   'class' => 'WebTek::Cache::Memcached',
   
   #... config settings for WebTek::Cache::Memcached
   'WebTek::Cache::Memcached' => {
      'servers' => [ '127.0.0.1:11211' ],
   },

}

man kann auch eigene Cache klassen verwenden solange sie:

  • eine subclasse von WebTek::Cache sind
  • und folgende methoden besitzten
    • new
    • set($key, $value, $expire_time)
    • add($key, $value, $expire_time)
    • get($key)
    • delete($key)
erstellt von max, am 26.05.2008 22:18

so, seit heute unterstuetzt webtek single und concrete table inheritance.

single table inheritance (weitere infos)


create table text (
   id int auto_increment,
   class varchar(100),
   text text,
   comment text,
   PRIMARY KEY (id)
);

man sieht hier, dass es eine column class gibt, welche die model-klasse definiert. hier noch der noetige perl code. wichtig ist, dass die subklassen jeweils in eigenen dateien stehen (sonnst kommt der WebTek::Loader durcheinander). mit den (optionalen) PROPERTIES funktionen kann man die models validieren.

MyApp/Model/Text.pm

use base qw( WebTek::Model );

sub PROPERTIES { 'text' => '.' }

MyApp/Model/Comment.pm

use base qw( MyApp::Model::Text );

sub PROPERTIES { 'comment' => '.' }

nun funktioniert folgender code:

> app->Model->Text->new_default('text'=>'text')->save
Model::Model::Text=HASH(0x....)
> app->Model->Comment->new_default('comment'=>'comment')->save
Model::Model::Comment=HASH(0x....)
> app->Model->Text->find_one(id=>1)
Model::Model::Text=HASH(0x....)
> app->Model->Text->find_one(id=>2)
Model::Model::Comment=HASH(0x....)

concrete table inheritance (weitere infos)


create table posts (
   id int auto_increment,
   text text,
   PRIMARY KEY (id)
);
create table comment (
   id int auto_increment,
   text text,
   PRIMARY KEY (id)
);

hier gibt es zwei tabellen (zusaetzlich muss noch sichergestellt werden, dass diese tabellen nicht die gleichen ids verwenden, entweder auto_increment bei einer tabelle erst bei 1000000 beginnen lassen (danke fuer den tip von adrian), oder irgendwas mit einer sequence). hier wieder der noetige perl code. weiters zu beachten ist, dass man bei den subclassen zusaetzlich zur superklasse (hier z.b. MyApp::Model::Text) noch das WebTek::Model in das @ISA array aufnimmt (sonnst initialisiert der WebTek::Loader das model nicht).

MyApp/Model/Text.pm

sub find_one_factory {
   my $class = shift;
   return app->Model->Post->find_one(@_)
      || app->Model->Comment->find_one(@_);
}
sub find_factory {
   my $class = shift;
   return app->Model->Post->find(@_)
      || app->Model->Comment->find(@_);
}
sub where_factory {
   my $class = shift;
   return app->Model->Post->where(@_)
      || app->Model->Comment->where(@_);
}
sub count_factory {
   my $class = shift;
   return app->Model->Post->count(@_)
      || app->Model->Comment->count(@_);
}
sub count_where_factory {
   my $class = shift;
   return app->Model->Post->count_where(@_)
      || app->Model->Comment->count_where(@_);
}

#... add more general methods
sub foo { ... }
sub text_as_uppercase { ... }
sub whatever { ... }

MyApp/Model/Post.pm

use base qw( MyApp::Model::Text WebTek::Model );

MyApp/Model/Comment.pm

use base qw( MyApp::Model::Text WebTek::Model );

so, dass ist jetzt viel komplizierter, aber man hat dafuer eine saubere trennung der datenbank. und so kann das dann verwendet werden:

> app->Model->Post->new_default('text'=>'text')->save
Model::Model::Post=HASH(0x....)
> app->Model->Comment->new_default('text'=>'comment')->save
Model::Model::Comment=HASH(0x....)
> app->Model->Text->find_one_factory(id=>1)
Model::Model::Post=HASH(0x....)
> app->Model->Text->find_one_factory(id=>2)
Model::Model::Comment=HASH(0x....)
> app->Model->Text->find_one_factory(id=>2)->text_as_uppercase
COMMENT

class table inheritance (weitere infos)

gibts nicht, ist mir zu kompliziert... :)

erstellt von max, am 09.06.2008 10:59

so.. hab jetzt ein mini-test framework in webtek eingebaut:

  • es gibt jetzt ein MyApp/scripts/test verzeichnis
  • in diesem koennen .t files drinnen liegen (gruppierung in unterverzeichnisse ist keine problem)
  • dieses test verzeichnis ist natuerlich auch in jedem modul moeglich
  • ein simples test-file sieht so aus:

    sub init   { }    #... place some code before calling the tests
    sub finish { }    #... place some code after calling the tests
    
    sub sample_test :Test(2) {
       ok(1, "sample test");
       is(2, 2, "sample test2");
    }
    

    • zuerst gibt es die funktionen init und finish. diese funktionen werden in jedem fall zum anfang und zum ende eines test-files ausgefuehrt. diese kann man verwenden um irgendwas zu initialisieren, bzw dann wieder aufzuraeumen (db. usw.)
    • dann gibt es test funktionen. diese werden durch das Test(\d+) attribute deklariert.
    • in diesem attribute muss man angeben, wieviele tests in dieser funktion gemacht werden (also ok, is, like, dies_ok, ... usw aufrufe)
    • alle weiteren funktionen ohne dem attribute werden nicht im rahmen der tests aufgerufen. d.h. diese koennen als hilfsfunktionen fungieren
  • als assertions koennen alle funktionen aus Test::More und Test::Exception verwendet werden.
  • um die tests auszufuehren wird wiedermal das webtek script verwendet. hier der aufruf mitsammt ergebnisliste:

    maxs-macbook:MapToolkit max$ ./webtek test
    
    [info] run testfile /WebTek/app/MapToolkit/scripts/test/Model/X.t
    [info]    - run test 'sample_test' with 2 tests:
    [info] run testfile /WebTek/app/MapToolkit/scripts/test/Page/X.t
    [info]    - run test 'index' with 1 tests:
    [error]      there were some failed tests, look at the details:
    [error]         not ok 1
    [error]            Failed test at /WebTek/app/MapToolkit/scripts/test/Page/X.t line 9.
    [error]                   got: undef
    [error]              expected: 'testvalue'
    
    result: 
     - planned: 3
     - successful: 2
     - failed: 1
    

    man sieht hier, dass ein test fehlerhaft war, und ganz unten ist noch eine zusammenfassung von allen tests in allen test-files.

Controller testen

um einen controller zu testen gibt es die WebTek::Engine::Test Engine. mit dieser kann man den request, response und session initialisieren. ein simpler test sieht dann aus:

sub index :Test(1) {
   app->engine->prepare;
   app->Page->Root->new->index;
   is(response->title, 'WebTek Tests');
}

sub create :Test(4) {
   #... test get
   app->engine->prepare;
   app->Page->Root->new->create;
   is(response->title, 'WebTek Tests - Create new Test');
   #... test post
   app->engine->prepare(
      'method' => 'post',
      'params' => { 'name' => [qw( new test )] },
   );
   thorws_ok { app->Page->Root->new->create } 'WebTek::Exception::Redirect';
   is(response->status, 302);
   #... check created model
   my $test = app->Model->Test->find_one('name' => 'new test');
   isa_ok($test, app->Model->Test);
}

Nachtrag

so.. das war jetzt nur ein kleiner auszug, wie man testen kann. mehr weis ich selber noch nicht, da ich bis jetzt selber noch nicht getestet hab (schande ueber mich).

weiters bin ich jetzt an dem punkt angelangt, andem WebTek feature-complete ist. die aktuelle version ist 0.9.1, und hoffenlich gibt es dann bald eine version 1.0, welche ueber

  • viele Tests
  • und eine gute doku

verfuegt!

erstellt von max, am 10.06.2008 19:18

also in perl gibt es packages:

package MyApp::Model::X;

...

sub x { ... }

soweit so gut. aber in einem modularen Framework kommt man oft mit packagenamen nicht weit, denn diese sind normalerweise in irgendeinem namespace. z.b. gib es in der MyApp application u.a. folgende Packages.

package MyApp::Model::Comment;

...

package MyApp::Model::Text;

use MyApp::Model::Comment;

...


wenn man jetzt aber modular programmiert, hat man optimalerweise die comment-funktionalitaet in einem modul gekapselt, damit man das bei einer anderen applikation auch gleich verwenden kann. nur heisst die andere applikation dann z.b. MyApp2 und somit wird auch das Comment package in dem MyApp2 namespace geladen. d.h. das Text package muesste so definiert sein.

package MyApp2::Model::Text;

use MyApp2::Model::Comment;

damit man jetzt ein modul in mehreren namespaces verwenden kann, gab es in webtek den trick mit folgender definition:

package MyApp2::Model::Text;

use app->Modul->Comment;

das app->Modul->Comment wurde dann fuer die jeweilige applikation in z.b. MyApp2::Module::Comment uebersetzt. diese logik ist aber alles andere als gut, da es dann bei folgendem aufruf:

app->Model->Comment->find_one(id=>123)

keine unterscheidung mehr zwischen dem packetnamen, und der methode die aufgerufen wird, gibt. darum heisst die neue syntax jetzt wie folgt:

package MyApp2::Model::Text;

use app::Model::Comment;

my $comment = app::Model::Comment->find_one(id=>123);

intern wird das app::Model::Comment allerdings weiter in MyApp2::Model::Comment uebersetzt (mittels einem source-filter). d.h. weiters, dass man die module zusatztlich noch in jeder applikation in einem eigenen namespace liegen (= geladen) hat.

ich hoffe es hat jetzt irgendwer verstanden, was ich gemeint hab. wenn nicht, dann bitte nachfragen.