{-# LANGUAGE OverloadedStrings, NoImplicitPrelude, PackageImports #-} {- | The server-side half of a typed AJAX communication channel. To use this library, you could start by defining a type in a file that can be shared between the Haskell Server and Fay client. For example: @ data Command = SendGuess Guess (ResponseType (Maybe Row)) | FetchBoard (ResponseType (Maybe Board)) deriving (Read, Show, Data, Typeable) instance Foreign Command @ The 'ResponseType' argument specifies what type each command should return. Using GADTs would be cleaner, but Fay does not support GADTs yet. In the server, you would then have a route that handles ajax requests such as: @ , dir "ajax" $ handleCommand (commandR acid) @ @commandR@ would then call functions to handle the various requests: @ -- | handle an AJAX request commandR :: AcidState Games -> Command -> ServerPart Response commandR acid cmd = case cmd of (SendGuess guess rt) -> fayResponse rt $ sendGuessC acid guess (FetchBoard rt) -> fayResponse rt $ fetchBoardC acid @ @commandR@ uses 'fayResponse' to convert the value returned by each command handler to a valid Fay value. Note that it takes 'ResponseType' argument and passes it to 'fayResponse'. This is how we ensure that each commend handler is returning the right type. See also @AJAX@ from the @happstack-client-fay@ package. -} module Happstack.Fay where -- NOTE: we do not really need to use NoImplicitPrelude and -- PackageImports here since this module only needs things from "base" -- and we do not compile against "fay-base". However, when debugging -- things in GHCi, we might have both "base" and "fay-base" -- loaded. So, using PackageImports just makes things easier. import "base" Prelude import Control.Monad.Trans (liftIO) import Data.Aeson import "base" Data.Data import Happstack.Server import ResponseType import Fay.Convert -- | decode the 'cmd' and call the response handler. -- -- See also: 'fayResponse' handleCommand :: (Data cmd, Show cmd, Happstack m) => (cmd -> m Response) -> m Response handleCommand handler = do json <- lookBS "json" liftIO $ print json let val = (decode' json) mCmd = readFromFay =<< val liftIO $ print val liftIO $ print mCmd case mCmd of Nothing -> badRequest $ toResponse ("Failed to turn this into a command: " ++ show (val)) (Just cmd) -> handler cmd -- | convert the return value to a fay response. -- fayResponse :: (Happstack m, Show a) => ResponseType a -- ^ used to help the type-checker enforce type safety -> m a -- ^ handler that calculates a response -> m Response fayResponse _rt m = do a <- m case showToFay a of Nothing -> internalServerError $ toResponse ("showToFay failed to convert response." :: String) (Just json) -> ok $ toResponseBS "application/json;charset=UTF-8" $ encode json