packages feed

snap 0.4.3 → 0.5.0

raw patch · 12 files changed

+216/−139 lines, 12 filesdep ~directory-treedep ~snap-coredep ~snap-serverPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: directory-tree, snap-core, snap-server

API changes (from Hackage documentation)

+ Snap.Extension.Loader.Devel: loadSnapTH' :: [String] -> [String] -> [String] -> String -> Q Exp
- Snap.Extension.Heist.Impl: heistInitializer :: MonadSnap m => FilePath -> Initializer (HeistState m)
+ Snap.Extension.Heist.Impl: heistInitializer :: MonadSnap m => FilePath -> (TemplateState m -> TemplateState m) -> Initializer (HeistState m)

Files

project_template/barebones/foo.cabal view
@@ -19,8 +19,8 @@     bytestring >= 0.9.1 && < 0.10,     MonadCatchIO-transformers >= 0.2.1 && < 0.3,     mtl >= 2 && < 3,-    snap-core == 0.4.*,-    snap-server == 0.4.*+    snap-core == 0.5.*,+    snap-server == 0.5.*    if impl(ghc >= 6.12.0)     ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2
project_template/default/foo.cabal view
@@ -24,9 +24,9 @@     heist >= 0.5 && < 0.6,     MonadCatchIO-transformers >= 0.2.1 && < 0.3,     mtl >= 2 && < 3,-    snap == 0.4.*,-    snap-core == 0.4.*,-    snap-server == 0.4.*,+    snap == 0.5.*,+    snap-core == 0.5.*,+    snap-server == 0.5.*,     text >= 0.11 && < 0.12,     time >= 1.1 && < 1.3,     xmlhtml == 0.1.*
project_template/default/src/Application.hs view
@@ -53,6 +53,6 @@ -- to worry about. applicationInitializer :: Initializer ApplicationState applicationInitializer = do-    heist <- heistInitializer "resources/templates"+    heist <- heistInitializer "resources/templates" id     timer <- timerInitializer     return $ ApplicationState heist timer
project_template/default/src/Main.hs view
@@ -42,6 +42,8 @@ module Main where  #ifdef DEVELOPMENT+import           Control.Exception (SomeException, try)+ import           Snap.Extension.Loader.Devel import           Snap.Http.Server (quickHttpServe) #else@@ -57,9 +59,10 @@     -- All source directories will be watched for updates     -- automatically.  If any extra directories should be watched for     -- updates, include them here.-    snap <- $(let extraWatcheDirs = ["resources/templates"]-              in loadSnapTH 'applicationInitializer 'site extraWatcheDirs)-    quickHttpServe snap+    (snap, cleanup) <- $(let watchDirs = ["resources/templates"]+                         in loadSnapTH 'applicationInitializer 'site watchDirs)+    try $ quickHttpServe snap :: IO (Either SomeException ())+    cleanup #else main = quickHttpServe applicationInitializer site #endif
snap.cabal view
@@ -1,5 +1,5 @@ name:           snap-version:        0.4.3+version:        0.5.0 synopsis:       Snap: A Haskell Web Framework: project starter executable and glue code library description:    Snap Framework project starter executable and glue code library license:        BSD3@@ -58,10 +58,11 @@     blaze-builder >= 0.2.1.4 && <0.4,     bytestring >= 0.9.1 && < 0.10,     directory >= 1.0 && < 1.2,+    directory-tree >= 0.10 && < 0.11,     enumerator == 0.4.*,     filepath >= 1.1 && <1.3,     MonadCatchIO-transformers >= 0.2.1 && < 0.3,-    snap-core == 0.4.*,+    snap-core == 0.5.*,     heist >= 0.5 && < 0.6,     hint >= 0.3.3.1 && < 0.4,     template-haskell >= 2.3 && < 2.6,@@ -100,8 +101,8 @@     mtl >= 2,     old-locale,     old-time,-    snap-core >= 0.4 && <0.5,-    snap-server >= 0.4 && <0.5,+    snap-core == 0.5.*,+    snap-server >= 0.5 && <0.6,     template-haskell >= 2.3 && < 2.6,     text >= 0.11 && <0.12,     time,
src/Snap/Extension/Heist/Impl.hs view
@@ -119,12 +119,16 @@   --------------------------------------------------------------------------------- | The 'Initializer' for 'HeistState'. It takes one argument, a path to a--- template directory containing @.tpl@ files.-heistInitializer :: MonadSnap m => FilePath -> Initializer (HeistState m)-heistInitializer p = do+-- | The 'Initializer' for 'HeistState'.+heistInitializer :: MonadSnap m+                 => FilePath+                 -- ^ Path to a template directory containing @.tpl@ files+                 -> (TemplateState m -> TemplateState m)+                 -- ^ Function to modify the initial template state+                 -> Initializer (HeistState m)+heistInitializer p modTS = do     heistState <- liftIO $ do-        (origTs,sts) <- bindStaticTag $ emptyTemplateState p+        (origTs,sts) <- bindStaticTag $ modTS $ emptyTemplateState p         loadTemplates p origTs >>= either error (\ts -> do             tsMVar <- newMVar ts             return $ HeistState p origTs tsMVar sts id)@@ -183,7 +187,12 @@ ------------------------------------------------------------------------------ -- | Take your application's state and register these splices in it so -- that you don't have to re-list them in every handler. Should be called from--- inside your application's 'Initializer'.+-- inside your application's 'Initializer'.  The splices registered by this+-- function do not persist through template reloads.  Those splices must be+-- passed as a parameter to heistInitializer.+--+-- (NOTE: In the future we may decide to deprecate registerSplices in favor of+-- the heistInitializer argument.) -- -- Typical use cases are dynamically generated components that are present in -- many of your views.
src/Snap/Extension/Loader/Devel.hs view
@@ -7,6 +7,7 @@ -- the calls to the dynamic loader. module Snap.Extension.Loader.Devel   ( loadSnapTH+  , loadSnapTH'   ) where  import           Control.Monad (liftM2)@@ -24,7 +25,6 @@  ------------------------------------------------------------------------------ import           Snap.Types-import           Snap.Extension (runInitializerWithoutReloadAction) import           Snap.Extension.Loader.Devel.Signal import           Snap.Extension.Loader.Devel.Evaluator import           Snap.Extension.Loader.Devel.TreeWatcher@@ -44,23 +44,43 @@ -- during development unless your .cabal file changes, or the code -- that uses this splice changes. loadSnapTH :: Name -> Name -> [String] -> Q Exp-loadSnapTH initializer action additionalWatchDirs = do-    args <- runIO getArgs+loadSnapTH initializer action additionalWatchDirs =+    loadSnapTH' modules imports additionalWatchDirs loadStr+  where+    initMod = nameModule initializer+    initBase = nameBase initializer+    actMod = nameModule action+    actBase = nameBase action -    let initMod = nameModule initializer-        initBase = nameBase initializer-        actMod = nameModule action-        actBase = nameBase action+    modules = catMaybes [initMod, actMod]+    imports = ["Snap.Extension"] -        opts = getHintOpts args-        modules = catMaybes [initMod, actMod]+    loadStr = intercalate " " [ "runInitializerWithoutReloadAction"+                              , initBase+                              , actBase+                              ]+++------------------------------------------------------------------------------+-- | This is the backing implementation for 'loadSnapTH'.  This+-- interface can be used when the types involved don't include a+-- SnapExtend and an Initializer.+loadSnapTH' :: [String] -- ^ the list of modules to interpret+            -> [String] -- ^ the list of modules to import in addition+                        -- to those being interpreted+            -> [String] -- ^ additional directories to watch for+                        -- changes to trigger to recompile/reload+            -> String   -- ^ the expression to interpret in the+                        -- context of the loaded modules and imports.+                        -- It should have the type 'HintLoadable'+            -> Q Exp+loadSnapTH' modules imports additionalWatchDirs loadStr = do+    args <- runIO getArgs++    let opts = getHintOpts args         srcPaths = additionalWatchDirs ++ getSrcPaths args -    -- The let in this block causes an extra static type check that the-    -- types of the names passed in were correct at compile time.-    [| let _ = runInitializerWithoutReloadAction $(varE initializer)-                                                 $(varE action)-       in hintSnap opts modules srcPaths initBase actBase |]+    [| hintSnap opts modules imports srcPaths loadStr |]   ------------------------------------------------------------------------------@@ -111,24 +131,21 @@                      -- modules which contain the initialization,                      -- cleanup, and handler actions.  Everything else                      -- they require will be loaded transitively.+         -> [String] -- ^ A list of modules that need to be be+                     -- imported, in addition to the ones that need to+                     -- be interpreted.  This only needs to contain+                     -- modules that aren't being interpreted, such as+                     -- those from other libraries, that are used in+                     -- the expression passed in.          -> [String] -- ^ A list of paths to watch for updates-         -> String   -- ^ The name of the initializer action-         -> String   -- ^ The name of the SnapExtend action-         -> IO (Snap ())-hintSnap opts modules srcPaths initialization handler =+         -> String   -- ^ The string to execute+         -> IO (Snap (), IO ())+hintSnap opts modules imports srcPaths action =     protectedHintEvaluator initialize test loader   where-    action = intercalate " " [ "runInitializerWithoutReloadAction"-                             , initialization-                             , handler-                             ]     interpreter = do         loadModules . nub $ modules-        let imports = "Prelude" :-                      "Snap.Extension" :-                      "Snap.Types" :-                      modules-        setImports . nub $ imports+        setImports . nub $ "Prelude" : "Snap.Types" : imports ++ modules          interpret action (as :: HintLoadable) 
src/Snap/Extension/Loader/Devel/Evaluator.hs view
@@ -43,7 +43,7 @@                           IO a                        -> (a -> IO Bool)                        -> IO HintLoadable-                       -> IO (Snap ())+                       -> IO (Snap (), IO ()) protectedHintEvaluator start test getInternals = do     -- The list of requesters waiting for a result.  Contains the     -- ThreadId in case of exceptions, and an empty MVar awaiting a@@ -61,70 +61,78 @@     -- "keep them full, unless updating them."  In every case, when     -- one of those MVars is emptied, the next action is to fill that     -- same MVar.  This makes deadlocking on MVar wait impossible.-    return $ do-        let waitForNewResult :: IO (Snap ())-            waitForNewResult = do-                -- Need to calculate a new result-                tid <- myThreadId-                reader <- newEmptyMVar+    let snap = do+            let waitForNewResult :: IO (Snap ())+                waitForNewResult = do+                    -- Need to calculate a new result+                    tid <- myThreadId+                    reader <- newEmptyMVar -                readers <- takeMVar readerContainer+                    readers <- takeMVar readerContainer -                -- Some strictness is employed to ensure the MVar-                -- isn't holding on to a chain of unevaluated thunks.-                let pair = (tid, reader)-                    newReaders = readers `seq` pair `seq` (pair : readers)-                putMVar readerContainer $! newReaders+                    -- Some strictness is employed to ensure the MVar+                    -- isn't holding on to a chain of unevaluated thunks.+                    let pair = (tid, reader)+                        newReaders = readers `seq` pair `seq` (pair : readers)+                    putMVar readerContainer $! newReaders -                -- If this is the first reader to queue, clean up the-                -- previous state, if there was any, and then begin-                -- evaluation of the new code and state.-                when (null readers) $ do-                    let runAndFill = block $ do-                            -- run the cleanup action-                            previous <- readMVar resultContainer-                            unblock $ cleanup previous+                    -- If this is the first reader to queue, clean up the+                    -- previous state, if there was any, and then begin+                    -- evaluation of the new code and state.+                    when (null readers) $ do+                        let runAndFill = block $ do+                                -- run the cleanup action+                                previous <- readMVar resultContainer+                                unblock $ cleanup previous -                            -- compile the new internals and initialize-                            stateInitializer <- unblock getInternals-                            res <- unblock stateInitializer+                                -- compile the new internals and initialize+                                stateInitializer <- unblock getInternals+                                res <- unblock stateInitializer -                            let a = fst res+                                let a = fst res -                            clearAndNotify (Right res) (flip putMVar a . snd)+                                clearAndNotify (Right res) (flip putMVar a . snd) -                        killWaiting :: SomeException -> IO ()-                        killWaiting e = block $ do-                            clearAndNotify (Left e) (flip throwTo e . fst)-                            throwIO e+                            killWaiting :: SomeException -> IO ()+                            killWaiting e = block $ do+                                clearAndNotify (Left e) (flip throwTo e . fst)+                                throwIO e -                        clearAndNotify r f = do-                            a <- unblock start-                            _ <- swapMVar resultContainer $ Just (r, a)-                            allReaders <- swapMVar readerContainer []-                            mapM_ f allReaders+                            clearAndNotify r f = do+                                a <- unblock start+                                _ <- swapMVar resultContainer $ Just (r, a)+                                allReaders <- swapMVar readerContainer []+                                mapM_ f allReaders -                    _ <- forkIO $ runAndFill `catch` killWaiting-                    return ()+                        _ <- forkIO $ runAndFill `catch` killWaiting+                        return () -                -- Wait for the evaluation of the action to complete,-                -- and return its result.-                takeMVar reader+                    -- Wait for the evaluation of the action to complete,+                    -- and return its result.+                    takeMVar reader -        existingResult <- liftIO $ readMVar resultContainer+            existingResult <- liftIO $ readMVar resultContainer -        getResult <- liftIO $ case existingResult of-            Just (res, a) -> do-                -- There's an existing result.  Check for validity-                valid <- test a-                case (valid, res) of-                    (True, Right (x, _)) -> return x-                    (True, Left e)       -> throwIO e-                    (False, _)           -> do-                        _ <- swapMVar resultContainer Nothing-                        waitForNewResult-            Nothing -> waitForNewResult-        getResult+            getResult <- liftIO $ case existingResult of+                Just (res, a) -> do+                    -- There's an existing result.  Check for validity+                    valid <- test a+                    case (valid, res) of+                        (True, Right (x, _)) -> return x+                        (True, Left e)       -> throwIO e+                        (False, _)           -> do+                            _ <- swapMVar resultContainer Nothing+                            waitForNewResult+                Nothing -> waitForNewResult+            getResult++        clean = do+             let msg = "invalid dynamic loader state.  " +++                       "The cleanup action has been executed"+             contents <- swapMVar resultContainer $ error msg+             cleanup contents++    return (snap, clean)   where     newReaderContainer :: IO (MVar [(ThreadId, MVar (Snap ()))])     newReaderContainer = newMVar []
src/Snap/Extension/Loader/Devel/TreeWatcher.hs view
@@ -30,12 +30,7 @@ checkTreeStatus :: TreeStatus -> IO Bool checkTreeStatus (TS paths entries) = check <$> readModificationTimes paths   where-    check = and . zipWith adtEq entries-    adtEq (n1 :/ dt1) (n2 :/ dt2) = n1 == n2 && dtEq dt1 dt2--    dtEq (Dir n1 d1) (Dir n2 d2) = n1 == n2 && and (zipWith dtEq d1 d2)-    dtEq (File n1 t1) (File n2 t2) = n1 == n2 && t1 == t2-    dtEq _ _ = False+    check = and . zipWith (==) entries   ------------------------------------------------------------------------------
src/Snap/Extension/Server.hs view
@@ -1,6 +1,7 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns      #-}+{-# LANGUAGE CPP               #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TemplateHaskell   #-}  {-| @@ -31,7 +32,7 @@ import qualified Data.Text.Encoding as T import           Prelude hiding (catch) import           Snap.Extension-import           Snap.Http.Server (simpleHttpServe)+import qualified Snap.Http.Server as SS import qualified Snap.Http.Server.Config as C import           Snap.Http.Server.Config hiding ( defaultConfig                                                 , completeConfig@@ -89,11 +90,8 @@ ------------------------------------------------------------------------------ -- | Completes a partial 'Config' by filling in the unspecified values with -- the default values from 'defaultConfig'.-completeConfig :: ConfigExtend s -> ConfigExtend s-completeConfig c = case getListen c' of-                    [] -> addListen (ListenHttp "0.0.0.0" 8000) c'-                    _ -> c'-  where c' = mappend defaultConfig c+completeConfig :: ConfigExtend s -> IO (ConfigExtend s)+completeConfig = C.completeConfig . (mappend defaultConfig)   ------------------------------------------------------------------------------@@ -108,33 +106,20 @@           -- ^ The application to be served           -> IO () httpServe config initializer handler = do-    (snap, cleanup) <- runInitializerWithReloadAction+    conf <- completeConfig config+    let !verbose  = fromJust $ getVerbose conf+    let !reloader = fromJust $ getReloadHandler conf+    let !compress = if fromJust $ getCompression conf then withCompression else id+    let !catch500 = flip catch $ fromJust $ getErrorHandler conf+    let !serve    = SS.simpleHttpServe config+    (site, cleanup) <- runInitializerWithReloadAction                          verbose                          initializer-                         (catch500 handler)+                         (catch500 $ compress handler)                          reloader-    let site = compress $ snap-    mapM_ printListen $ C.getListen config     _   <- try $ serve $ site :: IO (Either SomeException ())     putStr "\n"     cleanup-    output "Shutting down..."--  where-    conf     = completeConfig config-    verbose  = fromJust $ getVerbose conf-    output   = when verbose . hPutStrLn stderr-    reloader = fromJust $ getReloadHandler conf-    compress = if fromJust $ getCompression conf then withCompression else id-    catch500 = flip catch $ fromJust $ getErrorHandler conf-    serve    = simpleHttpServe config--    listenToString (C.ListenHttp host port) =-        concat ["http://", fromUTF8 host, ":", show port, "/"]-    listenToString (C.ListenHttps host port _ _) =-        concat ["https://", fromUTF8 host, ":", show port, "/"]--    printListen l = output $ "Listening on " ++ listenToString l   ------------------------------------------------------------------------------
test/snap-testsuite.cabal view
@@ -13,8 +13,9 @@     bytestring == 0.9.*,     directory,     filepath,+    Glob == 0.5.*,     HUnit >= 1.2 && < 2,-    http-enumerator >= 0.2.1.5 && <0.3,+    http-enumerator >= 0.6.5.3 && <0.7,     process == 1.*,     test-framework >= 0.3.1 && <0.4,     test-framework-hunit >= 0.2.5 && < 0.3,
test/suite/Snap/TestCommon.hs view
@@ -1,14 +1,22 @@+{-# LANGUAGE ScopedTypeVariables #-}+ module Snap.TestCommon where  import Control.Concurrent import Control.Exception-import Control.Monad     (when)+import Control.Monad (forM_, when)+import Data.Maybe+import Data.Monoid+import Prelude hiding (catch) import System.Cmd import System.Directory+import System.Environment import System.Exit import System.FilePath-import System.Process+import System.FilePath.Glob+import System.Process hiding (cwd) + testGeneratedProject :: String  -- ^ project name and directory                      -> String  -- ^ arguments to @snap init@                      -> String  -- ^ arguments to @cabal install@@@ -25,8 +33,24 @@                           removeDirectoryRecursive projectPath) $ do             makeWorkDirectory projectPath             setCurrentDirectory projectPath-            systemOrDie $ "snap init " ++ snapInitArgs-            systemOrDie $ "cabal install " ++ cabalInstallArgs+            snapExe <- findSnap+            systemOrDie $ snapExe ++ " init " ++ snapInitArgs++            snapCoreSrc   <- fromEnv "SNAP_CORE_SRC" "../../../snap-core"+            snapServerSrc <- fromEnv "SNAP_SERVER_SRC" "../../../snap-server"+            xmlhtmlSrc    <- fromEnv "XMLHTML_SRC" "../../../xmlhtml"+            heistSrc      <- fromEnv "HEIST_SRC" "../../../heist"+            let snapSrc   =  "../.."++            forM_ [ "snap-core", "snap-server", "xmlhtml", "heist", "snap" ]+                  (pkgCleanUp sandbox)++            forM_ [ snapCoreSrc, snapServerSrc, xmlhtmlSrc, heistSrc+                  , snapSrc] $ \s ->+                systemOrDie $ "cabal-dev " ++ cabalDevArgs+                                ++ " add-source " ++ s++            systemOrDie $ "cabal-dev install " ++ args             let cmd = ("." </> "dist" </> "build" </> projName </> projName)                       ++ " -p " ++ show httpPort             putStrLn $ "Running \"" ++ cmd ++ "\""@@ -34,6 +58,10 @@             waitABit             return (cwd, projectPath, pHandle) +    fromEnv name def = do+        r <- getEnv name `catch` \(_::SomeException) -> return ""+        if r == "" then return def else return r+     cleanup (cwd, projectPath, pHandle) = do         setCurrentDirectory cwd         terminateProcess pHandle@@ -41,6 +69,36 @@         removeDirectoryRecursive projectPath      waitABit = threadDelay $ 2*10^(6::Int)++    sandbox = "../test-cabal-dev" </> projName++    cabalDevArgs = "-s " ++ sandbox++    pkgCleanUp d pkg = do+        paths <- globDir1 (compile $ "packages*conf/" ++ pkg ++ "-*") d+        forM_ paths+              (\x -> (rm x `catch` \(_::SomeException) -> return ()))+      where+        rm x = do+            putStrLn $ "removing " ++ x+            removeFile x++    args = cabalDevArgs ++ " " ++ cabalInstallArgs ++    gimmeIfExists p = do+        b <- doesFileExist p+        if b then return (Just p) else return Nothing+++    findSnap = do+        home <- fromEnv "HOME" "."+        p1 <- gimmeIfExists "../../dist/build/snap/snap"+        p2 <- gimmeIfExists $ home </> ".cabal/bin/snap"+        p3 <- findExecutable "snap"++        return $ fromMaybe (error "couldn't find snap executable")+                           (getFirst $ mconcat $ map First [p1,p2,p3])+                systemOrDie :: String -> IO () systemOrDie s = do