Ein kleines Nagare-Tutorial
Verfasst: Mittwoch 1. Oktober 2008, 08:53
Teil 1: Installation
Nagare benötigt Stackless-Python, welches man sich außer unter Windows selbst kompilieren muss. Ich beschreibe hier, wie es unter Ubuntu 8.04 funktioniert.
Ich musste (glaube ich) fünf Pakete nachinstallieren: libc6-dev mit linux-libc-dev, libzlib1g-dev, libxml2-dev und libxslt1-dev. Ohne die ersten beiden Pakete geht gar nichts. Das nächste wird für die Installation von "setuptools" benötigt. Die letzten beiden werden später benötigt, um "lxml" zu kompilieren und zu installieren.
Die Pakete installiert man (gefahrlos, falls sie schon da sind) so:
Als nächstes lädt man stackless-252-export.tar.bz2, packt das Archiv aus und übersetzt es so, das es im eigenen HOME-Verzeichnis lebt und nicht mit einem installierten Python in Konflikt gerät:
Für den nächsten Schritt ist es wichtig, dass das eigene Python im PATH for dem normalen Python steht. Andernfalls klappt die Installation von "easy_install" nicht:
Prüfe nun, ob es geklappt hat:
Beim normalen Python fehlt der Hinweis auf "Stackless" in der Ausgabe.
Lade nun setuptools-0.6c9-py2.5.egg und installiere es, um das "easy_install"-Kommando zu bekommen:
Unter $HOME/stackless/bin/ sollte jetzt ein easy_install-Skript zu sehen sein, was man mit "which easy_install" prüfen kann. Es sollte im PATH sein, der weiter oben angepasst wurde.
Optional kann man jetzt "virtualenv" installieren. Mir scheint es nicht ganz so wichtig, weil man ja eh mit einer eigenen Version von Python unterwegs ist, aber andererseits schadet es auch nicht:
Nun kann man Nagare mit all seinen Abhänigkeiten installieren, was eine ganze Zeit lang dauert. Abhängig davon, ob man ein virtualenv gesetzt hat oder nicht, wird das "easy_install" aus $HOME/stackless/bin oder $HOME/nagare/bin benutzt:
Es sollte jetzt ein neues Kommando "nagare-admin" geben, was man mit "which" prüfen kann.
Teil 2 - Die erste Anwendung
Erzeuge nun eine neue Nagare-Anwendung:
Zum Starten benutzt man am besten eine zweite Shell (in der man dann ebenfalls das aktuelle virtualenv aktivieren muss bzw. wenigstens den PATH auf die Stackless-Version von Python setzen muss). Danach gibt man ein:
Nun kann man auf [url=localhost:8080/myapp/]localhost:8080/myapp/[/url] die Standardwillkommensmeldung sehen. Der Server kann weiterlaufen und lädt automatisch alle veränderten Dateien nach. Er stürzt nur bei Syntaxfehlern im Python ab und muss dann neu gestartet werden.
Der Programmcode der angezeigten Seite befindet sich im Verzeichnis "myapp" in der gleichnamigen Datei "myapp.py", in der eine Klasse "Myapp" definiert ist. Die Variable "app" enthält diese Klasse und ist die Verbindung nach außen, der "Entrypoint". In "setup.py" ist festgelegt, dass "myapp.myapp::app" die Stelle ist, wo Nagare die Anwendung findet. Eigentlich müsste man dort auch direkt Myapp eintragen können und kann sich dann die Variable sparen.
Die Beispielanwendung besteht neben der leeren Klasse "Myapp" nur noch aus einer "render"-Methode, die über "@presentation.render_for(Myapp)" mit "Myapp" verknüpft wird.
Bauen wir diese Anwendung nun zu einer Anwendung um, die nacheinander zwei Zahlen erfragt und dann die Summe dieser Zahlen ausgibt. Dazu benötigen wir eine Komponente, die mit einem Eingabefeld nach der Zahl fragt und eine Schaltfläche "Weiter" enthält.
Dies ist das Modell für meinen Dialog. Es speichert einen Fragetext und den anzuzeigenden Wert:
Dazu kommt die "render"-Methode:
In "render" von "Myapp" kann ich nun meine Komponente anzeigen. Fügen wir dazu hinter "Your application is running" folgendes ein:
"component" muss genau wie "presentation" von "nagare" importiert werden.
Hier ist eine zweite Komponente, die etwas anzeigen kann:
Auch diese können wir testweise als Komponente in render() von Myapp eintragen.
Ich möchte nun aber nicht, dass beide Eingabefelder gleichzeitig angezeigt werden, sondern dass dies nacheinander geschieht. Dazu braucht man einen "Task" mit einer go()-Methode:
Um diese Komponente jetzt an der Stelle einzubauen, wo vorher die beiden Eingabefelder waren, kann man nicht einfach immer neue Komponenten erzeugen, sondern muss eine einmal initialisierte Komponente wiederverwenden:
Noch wird aber nichts berechnet. Die call()-Funktionen rufen eine andere Komponente wie eine Subroutine auf. Aus so einer Subroutine kann man mit answer() ein Ergebnis zurückliefern. Dazu muss dem Weiter-Button eine action() gesetzt werden:
Ich überlasse es dem Leser, dafür zu sorgen, eine Fehlermeldung auszugeben, wenn keine Zahl eingegeben wurde.
Stefan
Nagare benötigt Stackless-Python, welches man sich außer unter Windows selbst kompilieren muss. Ich beschreibe hier, wie es unter Ubuntu 8.04 funktioniert.
Ich musste (glaube ich) fünf Pakete nachinstallieren: libc6-dev mit linux-libc-dev, libzlib1g-dev, libxml2-dev und libxslt1-dev. Ohne die ersten beiden Pakete geht gar nichts. Das nächste wird für die Installation von "setuptools" benötigt. Die letzten beiden werden später benötigt, um "lxml" zu kompilieren und zu installieren.
Die Pakete installiert man (gefahrlos, falls sie schon da sind) so:
Code: Alles auswählen
$ sudo apt-get install libc6-dev linux-libc-dev zlib1g-dev libxml2-dev libxslt1-dev
Code: Alles auswählen
$ cd $HOME
$ wget http://www.stackless.com/binaries/stackless-252-export.tar.bz2
$ tar xfj stackless-252-export.tar.bz2
$ cd stackless-2.5.2-*
$ ./configure --prefix=$HOME/stackless
$ make
$ make install
Code: Alles auswählen
$ export PATH=$HOME/stackless/bin:$PATH
Code: Alles auswählen
$ python
Python 2.5.2 Stackless 3.1b3 060516 (release25-maint, Sep 28 2008, 17:22:40)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Lade nun setuptools-0.6c9-py2.5.egg und installiere es, um das "easy_install"-Kommando zu bekommen:
Code: Alles auswählen
$ cd $HOME
$ wget http://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c9-py2.5.egg#md5=fe67c3e5a17b12c0e7c541b7ea43a8e6
$ sh setuptools-0.6c9-py2.5.egg
Optional kann man jetzt "virtualenv" installieren. Mir scheint es nicht ganz so wichtig, weil man ja eh mit einer eigenen Version von Python unterwegs ist, aber andererseits schadet es auch nicht:
Code: Alles auswählen
$ cd $HOME
$ easy_install virtualenv
$ virtualenv nagare
$ cd nagare
$ source bin/activate
Code: Alles auswählen
$ easy_install nagare[full]
Teil 2 - Die erste Anwendung
Erzeuge nun eine neue Nagare-Anwendung:
Code: Alles auswählen
$ cd $HOME
$ nagare-admin create-app myapp
$ cd myapp
$ python setup.py develop
Code: Alles auswählen
$ nagare-admin serve --debug --reload myapp
Der Programmcode der angezeigten Seite befindet sich im Verzeichnis "myapp" in der gleichnamigen Datei "myapp.py", in der eine Klasse "Myapp" definiert ist. Die Variable "app" enthält diese Klasse und ist die Verbindung nach außen, der "Entrypoint". In "setup.py" ist festgelegt, dass "myapp.myapp::app" die Stelle ist, wo Nagare die Anwendung findet. Eigentlich müsste man dort auch direkt Myapp eintragen können und kann sich dann die Variable sparen.
Die Beispielanwendung besteht neben der leeren Klasse "Myapp" nur noch aus einer "render"-Methode, die über "@presentation.render_for(Myapp)" mit "Myapp" verknüpft wird.
Bauen wir diese Anwendung nun zu einer Anwendung um, die nacheinander zwei Zahlen erfragt und dann die Summe dieser Zahlen ausgibt. Dazu benötigen wir eine Komponente, die mit einem Eingabefeld nach der Zahl fragt und eine Schaltfläche "Weiter" enthält.
Dies ist das Modell für meinen Dialog. Es speichert einen Fragetext und den anzuzeigenden Wert:
Code: Alles auswählen
class Input(object):
def __init__(self, prompt, value=None):
self.prompt, self.value = prompt, value
def set_value(self, value):
self.value = value
Code: Alles auswählen
@presentation.render_for(Input)
def render(self, h, comp, *args):
with h.form:
h << self.prompt
h << h.input(value=self.value).action(self.set_value)
h << h.input(type='submit', value='Weiter')
return h.root
Code: Alles auswählen
...
h << h.h1('Your application is running')
h << component.Component(Input("Erste Zahl:", 3))
h << component.Component(Input("Zweite Zahl:", 4))
Hier ist eine zweite Komponente, die etwas anzeigen kann:
Code: Alles auswählen
class Display(object):
def __init__(self, value):
self.value = value
@presentation.render_for(Display)
def render(self, h, comp, *args):
with h.p:
h << self.value
return h.root
Ich möchte nun aber nicht, dass beide Eingabefelder gleichzeitig angezeigt werden, sondern dass dies nacheinander geschieht. Dazu braucht man einen "Task" mit einer go()-Methode:
Code: Alles auswählen
class Add(component.Task):
def go(self, comp):
comp.call(Input("Erste Zahl:", 3))
comp.call(Input("Zweite Zahl:", 4))
comp.call(Display(7))
Code: Alles auswählen
class Myapp(object);
def __init__(self):
self.add = component.Component(Add())
...
h << h.h1('Your application is running')
h << self.add
Code: Alles auswählen
class Input...
...input(type='submit', value='Weiter').action(lambda: comp.answer(self.value))
class Add(component.Task):
def go(self, comp):
a = int(comp.call(Input("Erste Zahl:", 3)))
b = int(comp.call(Input("Zweite Zahl:", 4)))
comp.call(Display(a + b))
Stefan