[added XML/XSLT, added sideEffec, indentation, removed spaces at line ends stefan@aeschbacher.ch**20060422210935] { hunk ./doc/tutorial.html 63 -apply your xslt stylesheets to the xml files. +apply your xslt stylesheets to the XML files. hunk ./doc/tutorial.html 69 -The application model in HAppS is to help separate state, application +The application model in HAppS is to help separate state, application hunk ./doc/tutorial.html 149 -As usual with some programming languages (not Haskell) we will begin with a +As usual with some programming languages (not Haskell) we will begin with a hunk ./doc/tutorial.html 166 - startStateM = return $ MyState "Hello World!" + startStateM = return $ MyState "Hello World!" hunk ./doc/tutorial.html 168 - typeString _ = "MyState" - encodeStringM = defaultEncodeStringM - decodeStringM = defaultDecodeStringM + typeString _ = "MyState" + encodeStringM = defaultEncodeStringM + decodeStringM = defaultDecodeStringM hunk ./doc/tutorial.html 191 -your browser at http://localhost:8000/ and you should see the line +your browser at http://localhost:8000/ and you should see the line hunk ./doc/tutorial.html 197 -that a new directory with the name 'hello_world_state' has been created. It -contains the data needed by your application to run (or to continue it's +that a new directory with the name 'hello_world_state' has been created. It +contains the data needed by your application to run (or to continue it's hunk ./doc/tutorial.html 222 -With this line the state for this program is defined. Currently it is very +With this line the state for this program is defined. Currently it is very hunk ./doc/tutorial.html 229 - startStateM = return $ MyState "Hello World!" + startStateM = return $ MyState "Hello World!" hunk ./doc/tutorial.html 241 - typeString _ = "MyState" - encodeStringM = defaultEncodeStringM - decodeStringM = defaultDecodeStringM + typeString _ = "MyState" + encodeStringM = defaultEncodeStringM + decodeStringM = defaultDecodeStringM hunk ./doc/tutorial.html 248 -to be able to be serialized. This is achieved by deriving from the Serialize -class. The given code shows an easy way how to do this when the state derives +to be able to be serialized. This is achieved by deriving from the Serialize +class. The given code shows an easy way how to do this when the state derives hunk ./doc/tutorial.html 256 -The app function contains the application. Currently we ignore all the +The app function contains the application. Currently we ignore all the hunk ./doc/tutorial.html 265 - MyState message <- get - sresult 200 message + MyState message <- get + sresult 200 message hunk ./doc/tutorial.html 272 -With the sresult function, a return code 200 (OK, see [3]) and the message +With the sresult function, a return code 200 (OK, see [3]) and the message hunk ./doc/tutorial.html 293 -example before, copy the following program in a file (e.g. change_state.hs), compile +example before, copy the following program in a file (e.g. change_state.hs), compile hunk ./doc/tutorial.html 307 - startStateM = return $ MyState "Hello World!" + startStateM = return $ MyState "Hello World!" hunk ./doc/tutorial.html 309 - typeString _ = "MyState" - encodeStringM = defaultEncodeStringM - decodeStringM = defaultDecodeStringM + typeString _ = "MyState" + encodeStringM = defaultEncodeStringM + decodeStringM = defaultDecodeStringM hunk ./doc/tutorial.html 315 - sresult 200 $ renderHtml page - where - page = header - << thetitle << "Change State" - +++ h1 << "Change State" - +++ gui "/change" - << ((textfield "state") +++ submit "change" "change") + sresult 200 $ renderHtml page + where + page = header + << thetitle << "Change State" + +++ h1 << "Change State" + +++ gui "/change" + << ((textfield "state") +++ submit "change" "change") hunk ./doc/tutorial.html 323 - rq <- getEvent - put $ MyState (lookS 100 rq "state") - redirect "/" + rq <- getEvent + put $ MyState (lookS 100 rq "state") + redirect "/" hunk ./doc/tutorial.html 327 - MyState message <- get - sresult 200 message + MyState message <- get + sresult 200 message hunk ./doc/tutorial.html 353 -The type definition stayed also the same but we will now use some of the +The type definition stayed also the same but we will now use some of the hunk ./doc/tutorial.html 362 - sresult 200 $ renderHtml page - where - page = header - << thetitle << "Change State" - +++ h1 << "Change State" - +++ gui "/change" - << ((textfield "state") +++ submit "change" "change") + sresult 200 $ renderHtml page + where + page = header + << thetitle << "Change State" + +++ h1 << "Change State" + +++ gui "/change" + << ((textfield "state") +++ submit "change" "change") hunk ./doc/tutorial.html 388 - rq <- getEvent - put $ MyState (lookS 100 rq "state") - redirect "/" + rq <- getEvent + put $ MyState (lookS 100 rq "state") + redirect "/" hunk ./doc/tutorial.html 408 -length of the string to read, the request and the name of the +length of the string to read, the request and the name of the hunk ./doc/tutorial.html 427 - MyState message <- get - sresult 200 message + MyState message <- get + sresult 200 message hunk ./doc/tutorial.html 445 -As you probably know, such operations are said to produce side effects and +As you probably know, such operations are said to produce side effects and hunk ./doc/tutorial.html 450 -Side effects can easily be added to a HAppS program with the function -'addSideEffect'. It takes a time in seconds and an IO function as parameters. -The time in seconds is a timeout. If the computation takes longer than the +Side effects can easily be added to a HAppS program with the function +'addSideEffect'. It takes a time in seconds and an IO function as parameters. +The time in seconds is a timeout. If the computation takes longer than the hunk ./doc/tutorial.html 457 +

+
+module Main where
+import HAppS
+
+data MyState = MyState deriving (Read,Show)
+
+instance StartState MyState where
+	startStateM = return $ MyState
+instance Serialize MyState where
+	typeString _ = "MyState"
+	encodeStringM = defaultEncodeStringM
+	decodeStringM = defaultDecodeStringM
+
+app :: Method -> Host -> [String] -> Ev MyState Request Result
+app _ _ _ = do
+	addSideEffect 10 $ putStrLn "Hello World!"
+	sresult 200 "Hello World!"
+
+main = stdMain $ simpleHTTP "" [] app :*: End
+
+

+This simple program returns the string "Hello World!" to the +browser and prints it on the console where you run it. +

+ +

XML and XSLT

+

+HAppS offers some easy ways to read and write XML and apply XSLT transformations +to the generated XML. +

+

Creating XML

+

+The following example shows how to generate XML from a datatype and how to +serialize it. Additionally a XSLT [9] stylesheet is used to +transform the XML into HTML. If you don't know how XSLT works, try a result +from [10] to learn it, it is an easy way to separate your +date from the representation. +

+

+To run the example, copy the following code into a file and compile it. The +ToElement instance of Books requires the use of the -fglasgow-exts +compile flag. +

+
+module Main where
+import HAppS
+
+data MyState = MyState deriving (Read,Show)
+
+instance StartState MyState where
+	startStateM = return $ MyState
+instance Serialize MyState where
+	typeString _ = "MyState"
+	encodeStringM = defaultEncodeStringM
+	decodeStringM = defaultDecodeStringM
+
+type Books = [Book]
+instance ToElement Books where
+	toElement bs = listElem "Books" [] $ map toElement bs
+
+data Book = Book {author::String, title::String, content::String}
+instance ToElement Book where
+	toElement b = textElem "Book"
+		[("title", title b)
+		,("author", author b)] $ content b
+
+app :: Method -> Host -> [String] -> Ev MyState Request Result
+app GET _ ["books"] = do
+	toMessageM $ XML (XSL "/static/books.xsl") $ toElement
+		[(Book "Haskell Curry" "Functions" "lambda")
+		,(Book "Many" "Haskell Report" "String -> IO ()")]
+
+main = stdMain $ simpleHTTP "static/books.xsl" [("/static/", "static")] app :*: End
+
+ +

+Make a new directory called 'static' and insert the code below into a file called +'static/books.xsl'. This directory has to be at the same place where you run the +program. +

+
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:template match="/">
+<html>
+	<body>
+		<h2>Books</h2>
+		<table border="1">
+			<tr bgcolor="#C0C0C0">
+				<th align="left">Title</th>
+				<th align="left">Author</th>
+			</tr>
+			<xsl:for-each select="Books/Book">
+			<tr>
+				<td><xsl:value-of select="@title"/></td>
+				<td><xsl:value-of select="@author"/></td>
+			</tr>
+			</xsl:for-each>
+		</table>
+	</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>
+
+

+Now you can point your browser at http://localhost:8000/books and you will see +a list of books. Depending on your browser, you will see the XML code or HTML +code when you examine the source of the document. +

+

+Let's examine this program. +

+
+module Main where
+import HAppS
+
+data MyState = MyState deriving (Read,Show)
+
+instance StartState MyState where
+	startStateM = return $ MyState
+instance Serialize MyState where
+	typeString _ = "MyState"
+	encodeStringM = defaultEncodeStringM
+	decodeStringM = defaultDecodeStringM
+
+

+For this example we use a very minimal state which contains no information. +

+ +
+type Books = [Book]
+instance ToElement Books where
+	toElement bs = listElem "Books" [] $ map toElement bs
+
+

+The type Books is nothing but a list of single books. The derivation of the +class ToElement allows us later on to call the toElement function to get +a XML representation of this book list. +

+

+The toElement function is written with the listElem function which is +provided from the HAppS.Protocols.MinHaXML module (automatically imported +when you import HAppS). It creates a XML element called "Books" with no +attributes (the empty list). This element contains the XML representation +of the books in the list as child elements. These are generated by +applying the toElement function to the list of books. +

+ +
+data Book = Book {author::String, title::String, content::String}
+instance ToElement Book where
+	toElement b = textElem "Book"
+		[("title", title b)
+		,("author", author b)] $ content b
+
+

+The datatype Book defines a book itself. It has an author, a title and +a content. As before with the Books type, it derives from the ToElement +class. +

+

+Instead of the listElem function we use now textElem. It allows to create +an element that contains text in it's body. Additionally we define two +attributes of this element, title and author. The content of the book +is written as text. +

+ +
+app :: Method -> Host -> [String] -> Ev MyState Request Result
+app GET _ ["books"] = do
+	toMessageM $ XML (XSL "/static/books.xsl") $ toElement
+		[(Book "Haskell Curry" "Functions" "lambda")
+		,(Book "Many" "Haskell Report" "String -> IO ()")]
+
+

+The message to send to the client is constructed by a call to the toElement +function. The XML constructor takes an element and information to the +stylesheet to be used as parameters. In this case, an XSL stylesheet is used. +

+

+The toMessageM finally converts the XML into a message with header +information that can be sent to the client. It is predefined for XML and some +few other types. You can derive from the ToMessage class for any type you +want. +

+

+Please note that the XSLT handling can happen on server or on client side. +If for example the content field is very big and you rely on the XSLT to +remove it from the HTML (as it is the case in this example). The whole +content will be transferred to the client if the client supports XSLT. hunk ./doc/tutorial.html 650 -To be Continued +

+main = stdMain $ simpleHTTP "static/books.xsl" [("/static/", "static")] app :*: End
+
+

+The main function is now extended with an indication to serve static +pages from the 'static' directory under the path "/static/". The +list contains tuples of URL locations to directory location mappings. +

+

Reading XML

hunk ./doc/tutorial.html 669 +[9] XSLT specification
+[10] XSLT tutorials
}