Praktikum BKSPP: [2ex] Blatt 4

Werbung
Praktikum BKSPP:
Blatt 4
PD Dr. David Sabel
WS 2014/15
Einleitung
Yesod
Yesod Web Framework
http://www.yesodweb.com
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
2/22
Einleitung
Webframefork allgemein
Software für die Entwicklung von dynamischen Webseiten
Vorgefertigter Code für wesentliche Systemteile und
wiederkehrende Programmierarbeiten
Templates (Vorlagen) zur Generierung der Webseiten
Datenbankanbindung
Trennung von Datenmodell, Repräsentation und Steuerung
(Model View Controller)
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
3/22
Einleitung
Yesod
Webframefork in Haskell programmiert
benutzt viele Erweiterungen des Typsystems
Typinferenz funktioniert dabei nicht immer, d.h. Typen selbst
angeben
benutzt Template Haskell: Programmtemplates, erst beim
Compilieren wird der echte Haskell-Code erstellt
verwendet Cabal um den Webservice zu kompilieren
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
4/22
Einleitung
Aufgabenblatt
Zwei Teile:
Einführung in Yesod: Installation und kurzes Tutorial über die
wichtigsten Features
Danach Aufgaben zum Programmieren eines Quiz
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
5/22
Einleitung
Ziel: Quiz und Administrationsmöglichkeiten
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
6/22
Einleitung
Erste Schritte
Installation von Yesod (siehe Anleitung)
Neues Yesod-Projekt anlegen:
yesod init
Projektname eingeben und Sqllite als Datenbank auswählen
Projekt kompilieren (siehe Anleitung)
Entwicklungsmodus starten
yesod devel
Im Browser http://localhost:3000 aufrufen
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
7/22
Einleitung
Data.Text statt String verwenden
In Foundation.hs die Zeile
import Data.Text
einfügen.
In anderen Modulen qualifiziert importieren:
import qualified Data.Text as T
pack :: String -> Text
unpack :: Text -> String
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
8/22
Einleitung
Wesentliche Dateien im Projekt
Datei config/routes: Routen URL nach Code (Handler)
Verzeichnis Handler/: Haskell-Module für die Handler
Verzeichnis templates/: HTML und CSS Templates
Verzeichnis config/models: Datenmodelle für die Datenbank
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
9/22
Einleitung
Eine erste Seite
Quiz/> yesod add-handler
Name of route (without trailing R): Echo
Enter route pattern (ex: /entry/#EntryId): /echo/#Text
Enter space-separated list of methods (ex: GET POST): GET
Yesod legt dadurch an:
/echo/#Text EchoR GET in /config/routes
Bedeutet: GET-Anfragen an die URL /echo/irgendeinText
werden dem Handler EchoR behandelt
Modul Handler.Echo, dort muss die Funktion getEchoR
implementiert werden
Importe des neuen Moduls, Eintrag in Quiz.cabal
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
10/22
Einleitung
Implementierung in Handler.Echo
Default (durch Yesod angelegt)
module Handler.Echo where
import Import
getEchoR :: Text -> Handler Html
getEchoR = error "Not yet implemented: getEchoR"
Text ist der mit der URL übergebene Text
Handler ist eine Monade (genauer ein MonadTransformer),
IO-Aktionen können mit liftIO in der Handler-Monade
ausgeführt werden
z.B.
do
...
liftIO (putStrLn "Hallo")
zufallszahl <- liftIO randomIO
...
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
11/22
Einleitung
Implementierung in Handler.Echo
Eigene Implementierung
module Handler.Echo where
import Import
getEchoR :: Text -> Handler Html
getEchoR txt = defaultLayout [whamlet|<h1>#{txt}|]
defaultLayout: erzeugt Webseite im Standardlayout
[whamlet|<h1>#{txt}]: Hamlet-Widget, dass in die Seite
eingefügt wird, erzeugt:
<h1>irgendeinText</h1>
wenn txt gerade "irgendeinText" ist
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
12/22
Einleitung
Hamlet-Templates per Datei einbinden
Statt
getEchoR :: Text -> Handler Html
getEchoR txt = defaultLayout [whamlet|<h1>#{txt}|]
besserer Stil:
getEchoR :: Text -> Handler Html
getEchoR txt = defaultLayout $(widgetFile "echo")
und in /templates/echo.hamlet:
<h1>#{txt}
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
13/22
Einleitung
Hamlet-Templates Syntax und Features
Statt öffnenden und schließenden HTML-Tags, nur öffende:
<h1>Meine Überschrift!
statt
<h1>Meine Überschrift</h1>
Einrückung ist wichtig:
<ul>
<li>Erstes Listenelement
<li>Zweites Listenelement
<li>Drittes Listenelement
ergibt
<ul>
<li>Erstes Listenelement</li>
<li>Zweites Listenelement</li>
<li>Drittes Listenelement</li>
</ul>
ergibt
<ul>
<li>Erstes Listenelement</li>
<li>Zweites Listenelement</li>
</ul>
<li>Drittes Listenelement</li>
aber:
<ul>
<li>Erstes Listenelement
<li>Zweites Listenelement
<li>Drittes Listenelement
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
14/22
Einleitung
Hamlet-Templates Syntax und Features (2)
(mit import qualified Data.Text as T in Handler.Echo)
echo.hamlet mit mehr Features:
<h1> Der eingegebene Text ist #{txt}
<ul>
<li> Der Text hat #{T.length txt} Zeichen
<li> und lautet in Großbuchstaben: #{T.toUpper txt}
<p> Ein neuer Absatz.
Dieser Text ist im gleichen Absatz
Dieser Text nicht mehr, da Hamlet Einrückungen ernst nimmt.
<p>
<a href=@{EchoR "EinAndererText"}> Ein Link mit anderem Text
#{ code } führt den code aus und setzt das Ergebnis im
Template ein.
Funktionen und Namen die im Scope von
$(widgetFile "echo") stehen, können im Template
verwendet werden
Mit @{ route } kann man innerhalb des Webframeworks
verlinken!
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
15/22
Einleitung
Hamlet-Templates Syntax und Features (3)
if-then-else:
$if Haskell-Code
HTML-Code1
$else
HTML-Code2
Je nachdem ob Haskell-Code zu True oder False auswertet, wird
HTML-Code1 oder HTML-Code2 in die Seite eingefügt.
Z.B.
<h1> Der eingegebene Text ist #{txt}
<p>
$if T.length txt > 20
der eingegebe Text ist sehr lang
$else
der eingebene Text ist eher kurz
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
16/22
Einleitung
Hamlet-Templates Syntax und Features (4)
Iterieren über eine Liste $forall x <- xs: Z.B.
<h1> Der eingegebene Text ist #{txt}
<ul>
$forall x <- T.unpack txt
<li> #{x}
Erzeugt alle Buchstaben in der Liste als Items.
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
17/22
Einleitung
Hamlet-Templates Syntax und Features (5)
Cascading Style Sheets: Spezialsyntax
statt class="name" kann .name verwendet werden
statt id="name" kann #name verwendet werden
Stylesheet: Lucius-Templates
echo.hamlet wird automatisch mit echo.lucius verknüpft.
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
18/22
Einleitung
Hamlet und Lucius
Beispiel:
echo.hamlet enthält:
<h1> Der eingebene Text ist
<span .red> #{txt}
echo.lucius enthält:
.red {
color:red;
background-color:black;
}
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
19/22
Einleitung
Hamlet, Lucius und Julius
Es gibt noch Julius-Templates für JavaScript.
Doku: siehe
http://www.yesodweb.com/book/shakespearean-templates
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
20/22
Einleitung
Formulare und POST-Requests
Beispiel: Neue Route:
yesod add-handler
Name of route (without trailing R): Hallo
Enter route pattern (ex: /entry/#EntryId): /hallo
Enter space-separated list of methods (ex: GET POST): GET POST
In Handler.Hallo sind zu implementieren:
getHalloR für GET-Anfragen
postHalloR für POST-Anfragen
Beispiel Formular:
bei GET wird das Formular angezeigt, welches als POST-Request abgeschickt
wird
bei POST werden die übermittelten Formulardaten ausgelesen und verarbeitet
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
21/22
Einleitung
Formulare und POST-Requests (2)
getHalloR = do
defaultLayout [whamlet|<form method=post action=@{HalloR}>
<h1>Formular
<p>
Dein Vorname:
<input type=text name=Vorname>
<p>
Dein Nachname:
<input type=text name=Nachname>
<p>
<input type=submit>
<form>-Tag enthält Methode POSTund das Ziel (wieder der
Handler HalloR)
Zwei Eingabefelder für Vor- und Nachnamen
Submit-Button zum Absenden des Formulars
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
22/22
Einleitung
POST-Request – Auslesen der Daten
postHalloR muss die erhaltenen Formulardaten einlesen und
verarbeiten.
runInputPost als Hauptfunktion, Funktionen ireq und iopt
zum Parsen
ireq zum Parsen eines erforderlichen Eintrags
iopt zum Parsen eines optionalen Eintrags (Ergebnis im
Maybe-Typ verpackt)
Argumente: ein Feldtyp und Name des Felds
Feldtypen: textField und z.B. intField
Beispiel:
postHalloR = do
v <- runInputPost (ireq textField "Vorname")
defaultLayout [whamlet|<p> Hallo #{v}
|]
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
23/22
Einleitung
POST-Request – Auslesen der Daten (2)
Mehrere Daten auslesen mit dem Applicative Interface
pure :: a -> m a
<*> :: m (a -> b) -> m a -> m b
<$> :: (a -> b) -> m a -> m b
f n-stellige pure Funktion, dann ist
f <$> a1 <*> ... <*> an
die sequentielle Anwendung: Werte a1 bis an aus und wende die
Ergebnisse auf f an.
postHalloR :: Handler Html
postHalloR = do
(v,n) <- runInputPost (pair <$> (ireq textField "Vorname")
<*> (ireq textField "Nachname"))
defaultLayout [whamlet|<p> Hallo #{v} #{n}
|]
where pair a b = (a,b)
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
24/22
Einleitung
Datenbank-Anbindung
Yesod verwendet das sogenannte Persistent-Interface
Da wir sqllite ausgewählt haben, sind die Daten lokal in
einer Datei (Quiz.sqlite3) abgelegt
Datenmodelle sind in config/models
Beispiel:
Person
vorname Text
nachname Text
Legt Tabelle Person mit zwei Attributen vom Typ Text an
Automatisch: Schlüssel namens PersonId
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
25/22
Einleitung
Beispiel (Forts.)
Person
vorname Text
nachname Text
Auf der Haskell-Ebene wird dadurch (ungefähr) ein Datentyp
angelegt:
data Person = Person { personVorname :: Text
, personNachname :: Text }
Groß-/Kleinschreibung beachten!
Außerdem ein Typ PersonId
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
26/22
Einleitung
Datenbankzugriff
Wesentliche Funktionen:
runDB zum Ausführen der Abfrage
get: holt Datenbankeintrag anhand seines Schlüssels
(Rückgabe mit Maybe verpackt)
getPerson :: PersonId -> Handler (Maybe Person)
getPerson personid = runDB (get personid)
insert: Eintrag einfügen, Id wird als Rückgabe erzeugt
insertPerson :: Person -> Handler (PersonId)
insertPerson p = runDB (insert p)
delete: Löschen anhand der Id
deletePersonById :: PersonId -> Handler ()
deletePersonById pid = runDB (delete pid)
update: aktualisieren eines Eintrags.
Argumente: Id und Liste von Aktualisierungen der Form
TypnameAttribut =. neuerWert
updateVorname :: PersonId -> Text -> Handler ()
updateVorname pid neuerName =
runDB (update pid [PersonVorname =. neuerName])
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
27/22
Einleitung
Datenbankzugriff (2)
selectList filter selectopts: alle Einträge als Liste, wobei
mit filter nur bestimmte Einträge und mit selectopts die
Ausgabe bearbeitet werden kann (sortieren etc.)
selectList [] [] ergibt alle Einträge
getAllPersonsDB :: Handler ([Entity Person])
getAllPersonsDB = runDB (selectList [] [])
Rückgabe: Liste von Entity Person:
data Entity Person = Entity PersonId Person
Beispiele:
getAllPersonIds :: Handler ([PersonId])
getAllPersonIds = do
list <- getAllPersonsDB
return (map (\(Entity pid person) -> pid) list)
getAllPersons :: Handler ([Person])
getAllPersons = do
list <- getAllPersonsDB
return (map (\(Entity pid person) -> person) list)
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
28/22
Einleitung
Beispiel: Namen speichern und Anzeigen
postHalloR :: Handler Html
postHalloR =
do
(v,n) <- runInputPost (pair <$> (ireq textField "Vorname")
<*> (ireq textField "Nachname"))
insertPerson (Person {personVorname = v, personNachname = n})
defaultLayout [whamlet|<p> Hallo #{v} #{n}
|]
where pair a b = (a,b)
Route PersonsR mit der URL /persons und der GET-Methode
getPersonsR :: Handler Html
getPersonsR = do
personen <- getAllPersons
defaultLayout [whamlet|<table>
$forall x <- personen
<tr>
<td>#{personVorname x}
<td>#{personNachname x}
|]
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
29/22
Einleitung
Schlüssel
type PersonId = Key Person
Key ist vordefinierter Phantom-Typ
data Key a = Key {unKey = PersistValue}
PersistValue ist Datentyp für persistente Werte, im Beispiel
PersistInt64 i
Beispiel
intToPersonId :: Integer -> PersonId
intToPersonId i = Key (PersistInt64 (fromIntegral i))
personIdToInt :: PersonId -> Integer
personIdToInt pid = case (unKey pid) of
PersistInt64 i -> fromIntegral i
_
-> error "kein PersistInt64-Wert"
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
30/22
Einleitung
Sessions und Cookies
Daten die nicht permanent, sondern nur während einer
Sitzung gebraucht werden
z.B. Anzahl richtige Antworten im Quiz
Speichern in der Datenbank? Ungeeignet
Lösung: Cookies: Client speichert die Daten im Cookie und
übermittelt sie an Server
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
31/22
Einleitung
Cookies in Yesod
Wesentliche Methoden
lookupSession: Eintrag nachschauen
setSession Eintrag einfügen
Beispiel: Zählen, wie oft ein Benutzer die Seite mit dem Formular
aufgerufen hat:
getHalloR :: Handler Html
getHalloR = do
wert <- lookupSession "anzahlAufrufe"
let anzahl = case wert of
Nothing -> 1
Just i -> 1+(read (T.unpack i))::Int
setSession "anzahlAufrufe" (T.pack (show anzahl))
defaultLayout [whamlet|<form method=post action=@{HalloR}>
<h1>Formular (Ihr #{anzahl}.Aufruf)
...
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
32/22
Einleitung
Aufgaben
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
GET
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
QuizStartR
GET
/quiz
GET
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
QuizStartR
GET
/quiz
GET
QuizR
/quiz/#Text
GET
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
QuizStartR
GET
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
QuizStartR
/quiz
GET
AdminShowR
GET
/admin
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
QuizStartR
/quiz
GET
AdminShowR
GET
/admin
GET
QuizR
/quiz/#Text
GET
QuizR
AdminNewR
/quiz/#Text
/admin/new
POST
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
QuizStartR
/quiz
GET
AdminShowR
GET
/admin
GET
QuizR
/quiz/#Text
AdminDeleteR
GET
/admin/delete/#QuestionId
GET POST
QuizR
AdminNewR
/quiz/#Text
/admin/new
POST
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Aufgaben
HomeR
/
QuizStartR
/quiz
GET
AdminShowR
GET
/admin
GET
QuizR
/quiz/#Text
GET
AdminEditR
AdminDeleteR
/admin/edit/#QuestionId
/admin/delete/#QuestionId
GET POST
GET POST
QuizR
AdminNewR
/quiz/#Text
/admin/new
POST
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
33/22
Einleitung
Datenmodell
Question
question
answer1
answer2
answer3
answer4
right
deriving
Text
Text
Text
Text
Text
Int
Show
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
34/22
Einleitung
Sessioncookie
Liste der gestellten Fragen (QuestionIds)
Anzahl der richtigen Antworten
Anzahl der gestellten Fragen (Länge der Liste)
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel
35/22
Herunterladen