> {-# OPTIONS -fglasgow-exts #-} > {-# LANGUAGE TemplateHaskell , FlexibleInstances, > FlexibleContexts, UndecidableInstances, OverlappingInstances, > MultiParamTypeClasses, GeneralizedNewtypeDeriving #-} > > module Main where > > import Control.Concurrent > import Control.Monad > import Control.Monad.Reader > import Control.Monad.State (modify,put,get,gets,MonadState) > import Data.Generics hiding ((:+:)) > import Happstack.Server > import Happstack.State

> data Post = Post { > postTitle :: String, > postBody :: String > } deriving (Read,Show,Ord,Eq,Typeable,Data)

> data BlogState = BlogState { > postDB :: [Post] > } deriving (Read,Show,Ord,Eq,Typeable,Data)

> instance Version Post > $(deriveSerialize ''Post) > instance Version BlogState > $(deriveSerialize ''BlogState)

> instance Component BlogState where > type Dependencies BlogState = End > initialValue = BlogState []

> addPost :: (MonadState BlogState m) => Post -> m () > addPost p = modify $ (\s -> BlogState $ p:(postDB s)) > > getPosts :: (MonadReader BlogState m) => m [Post] > getPosts = asks postDB

> $(mkMethods ''BlogState ['addPost, 'getPosts])

> redir url = seeOther url (toResponse "")

> impl = msum > [ dir "new" newPostHandlers > , methodSP GET viewPostsHandler > ] > > newPostHandlers = msum [methodSP GET $ fileServe ["new_post.html"] "." > ,methodSP POST addPostHandler]

> addPostHandler = do > (Just title) (Just body) update $ AddPost (Post title body) > redir "/"

> viewPostsHandler = do > posts case posts of > [] -> ok $ toResponse "No posts yet" > otherwise -> ok $ toResponse $ unlines $ map show posts

> entryPoint :: Proxy BlogState > entryPoint = Proxy > > main = do > control tid waitForTermination > putStrLn "Shutting down..." > killThread tid > shutdownSystem control > putStrLn "Shutdown complete"

Happstack is improving, and my old blog posts on HAppS are now out of date. So we're in need of a bare-bones app to demonstrate functionality. I also wanted a simple app without authentication capability to demonstrate how to integrate Happstack-Auth into a project. Blog apps seem to be one of the canonical beginner's examples in the web framework world, so I thought that would be a good choice to start with. As usual, this post is literate haskell and should compile as Main.lhs.First, we define a blog post. A post has a title and a body, both of type String. The deriving clause is required by the Happstack state mechanism. The Ord instance isn't actually required for this example, but it is required if you want to build an IxSet from this data type, so I'm in the habit of including it.Our top level blog state will be a simple list of posts.These are the standard Version and Serialize instances needed by Happstack's state mechanism. I won't go into detail about them here. Jeremy Shaw has a nice post describing them in much more detail.Our state has to be an instance of component. We have no dependencies and our state's initial value is a simple empty list.Now we need some accessor functions. For now we'll keep it simple with a function to add a post, and one to get the list of all posts.Now the required Template Haskell voodoo to build these methods for us.Before we go further I want to add a little infrastructure code that will simplify things later. It's a simple function that redirects the client to the specified URL.New we define the layout of our site. We have one URL at "http://site.com/new". A GET request to this url returns a static HTML file containing a new post form. The form POSTS the results to the same URL, so we have to define a function to handle this request. The last "methodSP GET" matches the root URL "http://site.com/" and will just display the list of posts.The msum in newPostHandlers is new as of Happstack 0.2. In prior versions, some functions required or returned data of type ServerPartT, and some used [ServerPartT]. Other functions required WebT data. In Happstack 0.2, everything was unified to work around a single ServerPartT. If you have an abstraction that needs several ServerPartTs, you usually want to collapse them using msum. Our new post handler gets POSTed values from the HTTP request, constructs a Post, passes that to AddPost, and redirects to the root.The view posts handler queries the GetPosts action for the list of posts. If the list is empty, it returns a simple message, otherwise it returns the posts as a string. toResponse converts strings into responses with a content type of text/plain. Generating proper HTML pages is not hard, but is beyond the scope of this example.The rest of the code is standard infrastructure for a Happstack application. The only thing we have to do is declare an entry point with our state Component and pass it to startSystemState. Then we pass our previously defined impl function to simpleHTTP and we're done. A clean, working Happstack application in 75 lines of code.The code for this app is available in the Happstack-Auth github repository in the demos/Blog-NoAuth directory . In the next post I will show how to add authentication to this app using the auth framework I mentioned in the last post.