[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. +
+ ++HAppS offers some easy ways to read and write XML and apply XSLT transformations +to the generated 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
+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. +
+