Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
1
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 fur die Entwicklung von dynamischen Webseiten
Vorgefertigter Code fur wesentliche Systemteile undwiederkehrende Programmierarbeiten
Templates (Vorlagen) zur Generierung der Webseiten
Datenbankanbindung
Trennung von Datenmodell, Reprasentation 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 selbstangeben
benutzt Template Haskell: Programmtemplates, erst beimCompilieren 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:
Einfuhrung in Yesod: Installation und kurzes Tutorial uber diewichtigsten Features
Danach Aufgaben zum Programmieren eines Quiz
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 5/22
Einleitung
Ziel: Quiz und Administrationsmoglichkeiten
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 auswahlen
Projekt kompilieren (siehe Anleitung)
Entwicklungsmodus startenyesod 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
einfugen.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 fur die Handler
Verzeichnis templates/: HTML und CSS Templates
Verzeichnis config/models: Datenmodelle fur 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 ubergebene Text
Handler ist eine Monade (genauer ein MonadTransformer),IO-Aktionen konnen mit liftIO in der Handler-Monadeausgefuhrt werdenz.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 Seiteeingefugt 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 offnenden und schließenden HTML-Tags, nur offende:
<h1>Meine Uberschrift!
statt
<h1>Meine Uberschrift</h1>
Einruckung 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>aber:
<ul>
<li>Erstes Listenelement
<li>Zweites Listenelement
<li>Drittes Listenelement
ergibt
<ul>
<li>Erstes Listenelement</li>
<li>Zweites Listenelement</li>
</ul>
<li>Drittes Listenelement</li>
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 } fuhrt den code aus und setzt das Ergebnis imTemplate ein.Funktionen und Namen die im Scope von$(widgetFile "echo") stehen, konnen im Templateverwendet werdenMit @{ route } kann man innerhalb des Webframeworksverlinken!
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 15/22
Einleitung
Hamlet-Templates Syntax und Features (3)
if-then-else:
$if Haskell-CodeHTML-Code1
$else
HTML-Code2
Je nachdem ob Haskell-Code zu True oder False auswertet, wirdHTML-Code1 oder HTML-Code2 in die Seite eingefugt.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 uber 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-Templatesecho.hamlet wird automatisch mit echo.lucius verknupft.
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 18/22
Einleitung
Hamlet und Lucius
Beispiel:
echo.hamlet enthalt:
<h1> Der eingebene Text ist
<span .red> #{txt}
echo.lucius enthalt:
.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 fur JavaScript.Doku: siehehttp://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 fur GET-Anfragen
postHalloR fur POST-Anfragen
Beispiel Formular:
bei GET wird das Formular angezeigt, welches als POST-Request abgeschicktwird
bei POST werden die ubermittelten 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 enthalt Methode POSTund das Ziel (wieder derHandler HalloR)
Zwei Eingabefelder fur 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 undverarbeiten.
runInputPost als Hauptfunktion, Funktionen ireq und ioptzum Parsen
ireq zum Parsen eines erforderlichen Eintragsiopt zum Parsen eines optionalen Eintrags (Ergebnis imMaybe-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 dieErgebnisse 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 ausgewahlt haben, sind die Daten lokal ineiner 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: Schlussel 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 (ungefahr) ein Datentypangelegt:
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 Ausfuhren der Abfrageget: holt Datenbankeintrag anhand seines Schlussels(Ruckgabe mit Maybe verpackt)getPerson :: PersonId -> Handler (Maybe Person)
getPerson personid = runDB (get personid)
insert: Eintrag einfugen, Id wird als Ruckgabe erzeugtinsertPerson :: Person -> Handler (PersonId)
insertPerson p = runDB (insert p)
delete: Loschen anhand der IddeletePersonById :: PersonId -> Handler ()
deletePersonById pid = runDB (delete pid)
update: aktualisieren eines Eintrags.Argumente: Id und Liste von Aktualisierungen der FormTypnameAttribut =. neuerWertupdateVorname :: 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 Eintrage als Liste, wobeimit filter nur bestimmte Eintrage und mit selectopts dieAusgabe bearbeitet werden kann (sortieren etc.)selectList [] [] ergibt alle EintragegetAllPersonsDB :: Handler ([Entity Person])
getAllPersonsDB = runDB (selectList [] [])
Ruckgabe: 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
Schlussel
type PersonId = Key Person
Key ist vordefinierter Phantom-Typ
data Key a = Key {unKey = PersistValue}
PersistValue ist Datentyp fur persistente Werte, im BeispielPersistInt64 iBeispiel
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 wahrend einerSitzung gebraucht werden
z.B. Anzahl richtige Antworten im Quiz
Speichern in der Datenbank? Ungeeignet
Losung: Cookies: Client speichert die Daten im Cookie undubermittelt 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 einfugenBeispiel: Zahlen, wie oft ein Benutzer die Seite mit dem Formularaufgerufen 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
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GET
QuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Aufgaben
HomeR
/
GETQuizStartR
/quiz
GET
QuizR
/quiz/#Text
GET
QuizR
/quiz/#Text
POST
AdminShowR
/admin
GET
AdminNewR
/admin/new
GET POST
AdminDeleteR
/admin/delete/#QuestionId
GET POST
AdminEditR
/admin/edit/#QuestionId
GET POST
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 33/22
Einleitung
Datenmodell
Question
question Text
answer1 Text
answer2 Text
answer3 Text
answer4 Text
right Int
deriving 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 (Lange der Liste)
Praktikum BKSPP: Blatt 4 – WS 2014/15 – D. Sabel 35/22