{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, TemplateHaskell, TypeFamilies #-}
module Clckwrks.Acid where
import Clckwrks.NavBar.Acid (NavBarState , initialNavBarState)
import Clckwrks.ProfileData.Acid (ProfileDataState, initialProfileDataState)
import Clckwrks.Types (UUID)
import Clckwrks.URL (ClckURL)
import Control.Applicative ((<$>))
import Control.Exception (bracket, catch, throw)
import Control.Concurrent (killThread, forkIO)
import Control.Monad.Reader (ask)
import Control.Monad.State (modify, put)
import Data.Acid (AcidState, Query, Update, createArchive, makeAcidic)
import Data.Acid.Local (openLocalStateFrom, createCheckpointAndClose)
import Data.Acid.Remote (acidServer, skipAuthenticationCheck)
import Data.Data (Data, Typeable)
import Data.Maybe (fromMaybe)
import Data.SafeCopy (Migrate(..), base, deriveSafeCopy, extension)
import Data.Text (Text)
import Happstack.Authenticate.Core (AuthenticateState)
import Happstack.Authenticate.Password.Core (PasswordState)
import Network (PortID(UnixSocket))
import Prelude hiding (catch)
import System.Directory (removeFile)
import System.FilePath ((</>))
import System.IO.Error (isDoesNotExistError)
import HSP.Google.Analytics (UACCT)
data CoreState_v0 = CoreState_v0
{ coreUACCT_v0 :: Maybe UACCT
, coreRootRedirect_v0 :: Maybe Text
}
deriving (Eq, Data, Typeable, Show)
$(deriveSafeCopy 0 'base ''CoreState_v0)
data CoreState = CoreState
{ coreSiteName :: Maybe Text
, coreUACCT :: Maybe UACCT
, coreRootRedirect :: Maybe Text
, coreLoginRedirect :: Maybe Text
}
deriving (Eq, Data, Typeable, Show)
$(deriveSafeCopy 1 'extension ''CoreState)
instance Migrate CoreState where
type MigrateFrom CoreState = CoreState_v0
migrate (CoreState_v0 ua rr) = CoreState Nothing ua rr Nothing
initialCoreState :: CoreState
initialCoreState = CoreState
{ coreSiteName = Nothing
, coreUACCT = Nothing
, coreRootRedirect = Nothing
, coreLoginRedirect = Nothing
}
getUACCT :: Query CoreState (Maybe UACCT)
getUACCT = coreUACCT <$> ask
setUACCT :: Maybe UACCT -> Update CoreState ()
setUACCT mua = modify $ \cs -> cs { coreUACCT = mua }
getRootRedirect :: Query CoreState (Maybe Text)
getRootRedirect = coreRootRedirect <$> ask
setRootRedirect :: Maybe Text -> Update CoreState ()
setRootRedirect path = modify $ \cs -> cs { coreRootRedirect = path }
getLoginRedirect :: Query CoreState (Maybe Text)
getLoginRedirect = coreLoginRedirect <$> ask
setLoginRedirect :: Maybe Text -> Update CoreState ()
setLoginRedirect path = modify $ \cs -> cs { coreLoginRedirect = path }
getSiteName :: Query CoreState (Maybe Text)
getSiteName = coreSiteName <$> ask
setSiteName :: Maybe Text -> Update CoreState ()
setSiteName name = modify $ \cs -> cs { coreSiteName = name }
getCoreState :: Query CoreState CoreState
getCoreState = ask
setCoreState :: CoreState -> Update CoreState ()
setCoreState = put
$(makeAcidic ''CoreState
[ 'getUACCT
, 'setUACCT
, 'getRootRedirect
, 'setRootRedirect
, 'getLoginRedirect
, 'setLoginRedirect
, 'getSiteName
, 'setSiteName
, 'getCoreState
, 'setCoreState
])
data Acid = Acid
{
acidProfileData :: AcidState ProfileDataState
, acidCore :: AcidState CoreState
, acidNavBar :: AcidState NavBarState
}
class GetAcidState m st where
getAcidState :: m (AcidState st)
withAcid :: Maybe FilePath -> (Acid -> IO a) -> IO a
withAcid mBasePath f =
let basePath = fromMaybe "_state" mBasePath in
bracket (openLocalStateFrom (basePath </> "profileData") initialProfileDataState) (createArchiveCheckpointAndClose) $ \profileData ->
bracket (openLocalStateFrom (basePath </> "core") initialCoreState) (createArchiveCheckpointAndClose) $ \core ->
bracket (openLocalStateFrom (basePath </> "navBar") initialNavBarState) (createArchiveCheckpointAndClose) $ \navBar ->
bracket (forkIO (tryRemoveFile (basePath </> "profileData_socket") >> acidServer skipAuthenticationCheck (UnixSocket $ basePath </> "profileData_socket") profileData))
(\tid -> killThread tid >> tryRemoveFile (basePath </> "profileData_socket"))
(const $ f (Acid profileData core navBar))
where
tryRemoveFile fp = removeFile fp `catch` (\e -> if isDoesNotExistError e then return () else throw e)
createArchiveCheckpointAndClose acid =
do createArchive acid
createCheckpointAndClose acid