packages feed

happstack-fay-0.1.0.0: Language/Fay/AJAX.hs

{-# LANGUAGE DeriveDataTypeable, NoImplicitPrelude #-}
{- |

client-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.

To execute a remote function we use the 'call' function:

@
      call "/ajax" FetchBoard $ \mboard -> ...
@

Due to the single-threaded nature of Javascript, we do not want to
block until the 'call' returns a value, so we perform the AJAX request
asynchronously. The third argument to 'call' is the callback function
to run when the response is received.

See also: "Happstack.Fay"

-}
module Language.Fay.AJAX where

import Language.Fay.FFI
import Language.Fay.Prelude

-- | 'ResponseType' is used in lieu of `GADTs` as a mechanism for
-- specifying the expected return type of remote AJAX calls.
data ResponseType a = ResponseType
    deriving (Eq, Read, Show, Data, Typeable)
instance Foreign (ResponseType a)

-- | Asynchronously call a command
--
-- Note: if the server returns 404 or some other non-success exit
-- code, the callback function will never be run.
--
-- This function is just a wrapper around 'ajaxCommand' which uses the
-- 'ResponseType res' phantom-typed parameter for added type safety.
call :: (Foreign cmd, Foreign res) =>
        String                    -- ^ URL to 'POST' AJAX request to
     -> (ResponseType res -> cmd) -- ^ AJAX command to send to server
     -> (res -> Fay ())           -- ^ callback function to handle response
     -> Fay ()
call uri f g =
    ajaxCommand uri (f ResponseType) g

-- | Run the AJAX command. (internal)
--
-- You probably want to use 'call' which provides additional
-- type-safety.
--
-- Note: if the server returns 404 or some other non-success exit
-- code, the callback function will never be run.
--
-- see also: 'call'
ajaxCommand :: (Foreign cmd, Foreign res) =>
               String
            -> cmd
            -> (res -> Fay ())
            -> Fay ()
ajaxCommand =
    ffi "jQuery['ajax']({\
        \ \"url\": %1, \
        \ \"type\": 'POST', \
        \ \"data\": { \"json\": JSON.stringify(%2) }, \
        \ \"dataType\": 'json', \
        \ \"success\" : %3 \
        \})"