on master help sign in
Added directory monitoring
commit384998916b3ea194ca2cb411534fd968f2f141ed authorMichael Snoyman <michael@snoyman.com> dateMon 20 Sep 2010, 08:30 (16 years ago) parent6b76a6b
4 files changed+49−13
Network/Wai/Handler/DevelServer.hs+36−8 TestApp.hs+6−2 wai-handler-devel.cabal+1−1 wai-handler-devel.hs+6−2
Network/Wai/Handler/DevelServer.hs+36−8
@@ -21,15 +21,21 @@ import qualified Control.Concurrent.MVar as M
2121import qualified Control.Concurrent.Chan as C
2222import System.Directory (getModificationTime)
2323import Network.Wai.Handler.SimpleServer (parseRequest, sendResponse)
24+import Control.Monad (filterM)
25+
26+import Control.Arrow ((&&&))
27+import System.Directory (doesDirectoryExist,
28+ doesFileExist, getDirectoryContents)
29+import Control.Applicative ((<$>))
2430
2531type FunctionName = String
2632
27−run :: Port -> ModuleName -> FunctionName -> IO ()
28−run port modu func = do
33+run :: Port -> ModuleName -> FunctionName -> [FilePath] -> IO ()
34+run port modu func dirs = do
2935 queue <- C.newChan
3036 mqueue <- M.newMVar queue
3137 startApp queue $ loadingApp Nothing
32− _ <- forkIO $ fillApp modu func mqueue
38+ _ <- forkIO $ fillApp modu func mqueue dirs
3339 run' port mqueue
3440
3541startApp :: Queue -> Handler -> IO ()
@@ -41,7 +47,7 @@ startApp queue withApp = do
4147 case msession of
4248 Nothing -> return ()
4349 Just (req, onRes) -> do
44− void $ forkIO $ (handle onErr $ app req) >>= onRes
50+ void $ forkIO $ (E.handle onErr $ app req) >>= onRes
4551 go app
4652 onErr :: SomeException -> IO Response
4753 onErr e = return
@@ -50,13 +56,13 @@ startApp queue withApp = do
5056 $ "Exception thrown while running application\n\n" ++ show e
5157 void x = x >> return ()
5258
53−fillApp :: String -> String -> M.MVar Queue -> IO ()
54−fillApp modu func mqueue =
59+fillApp :: String -> String -> M.MVar Queue -> [FilePath] -> IO ()
60+fillApp modu func mqueue dirs =
5561 go Nothing []
5662 where
5763 constSE :: x -> SomeException -> x
5864 constSE = const
59− getTimes = handle (constSE $ return []) . mapM getModificationTime
65+ getTimes = E.handle (constSE $ return []) . mapM getModificationTime
6066 go prevError prevFiles = do
6167 toReload <-
6268 if null prevFiles
@@ -79,7 +85,9 @@ fillApp modu func mqueue =
7985 putStrLn $ "Compile failed: " ++ show err
8086 loadingApp' (Just $ toException err) mqueue
8187 return (Just $ toException err, [])
82− Right (app, files) -> do
88+ Right (app, files') -> E.handle onInitErr $ do
89+ files'' <- mapM fileList dirs
90+ let files = concat $ files' : files''
8391 putStrLn "Interpreting success, new app loaded"
8492 E.handle onInitErr $ do
8593 swapApp app mqueue
@@ -90,6 +98,26 @@ fillApp modu func mqueue =
9098 loadingApp' (Just e) mqueue
9199 return (Just e, [])
92100
101+fileList :: FilePath -> IO [FilePath]
102+fileList top = do
103+ ex <- doesDirectoryExist top
104+ if ex then fileList' top "" else return []
105+
106+fileList' :: FilePath -> FilePath -> IO [FilePath]
107+fileList' realTop top = do
108+ let prefix1 = top ++ "/"
109+ prefix2 = realTop ++ prefix1
110+ allContents <- filter notHidden <$> getDirectoryContents prefix2
111+ let all' = map ((++) prefix1 &&& (++) prefix2) allContents
112+ files <- map snd <$> filterM (doesFileExist . snd) all'
113+ dirs <- filterM (doesDirectoryExist . snd) all' >>=
114+ mapM (fileList' realTop . fst)
115+ return $ concat $ files : dirs
116+
117+notHidden :: FilePath -> Bool
118+notHidden ('.':_) = False
119+notHidden _ = True
120+
93121loadingApp' :: Maybe SomeException -> M.MVar Queue -> IO ()
94122loadingApp' err mqueue = swapApp (loadingApp err) mqueue
95123
TestApp.hs+6−2
@@ -1,4 +1,5 @@
11{-# LANGUAGE QuasiQuotes, TypeFamilies, GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE TemplateHaskell #-}
23{-# LANGUAGE OverloadedStrings #-}
34module TestApp (testApp) where
45
@@ -8,6 +9,7 @@ import Database.Persist.Sqlite
89import System.Directory
910import Control.Monad (when)
1011import Helper
12+import Text.Hamlet
1113
1214mkPersist [$persist|
1315Dummy
@@ -32,6 +34,8 @@ testApp handler = do
3234 insert $ Dummy ""
3335 count ([] :: [Filter Dummy])
3436 return $ Response status200
35− [("Content-Type", "text/plain; charset=utf-8")]
36− $ ResponseLBS $ pack $ "Counter: " ++ show x
37+ [("Content-Type", "text/html; charset=utf-8")]
38+ $ ResponseLBS
39+ $ renderHamlet id
40+ $(hamletFileDebug "hamlet/testapp.hamlet")
3741 putStrLn "handler completed, this should only happen at the beginning of a reload"
wai-handler-devel.cabal+1−1
@@ -1,5 +1,5 @@
11Name: wai-handler-devel
2−Version: 0.0.2
2+Version: 0.1.0
33Synopsis: WAI server that automatically reloads code after modification.
44Description: This handler automatically reloads your source code upon any changes. It works by using the hint package, essentially embedding GHC inside the handler. The handler (both the executable and library) takes three arguments: the port to listen on, the module name containing the application function, and the name of the function.
55 .
wai-handler-devel.hs+6−2
@@ -7,19 +7,23 @@ data Devel = Devel
77 { port :: Int
88 , moduleName :: String
99 , function :: String
10+ , yesod :: Bool
1011 }
1112 deriving (Show, Data, Typeable)
1213
1314main :: IO ()
1415main = do
15− Devel p m f <- cmdArgs Devel
16+ Devel p m f y <- cmdArgs Devel
1617 { port = 3000 &= argPos 0 &= typ "PORT"
1718 , moduleName = "" &= argPos 1 &= typ "MODULE"
1819 , function = "" &= argPos 2 &= typ "FUNCTION"
20+ , yesod = False &= help "Monitor typical Yesod folders (hamlet, etc)"
1921 } &= summary "WAI development web server"
20− _ <- forkIO $ run p m f
22+ _ <- forkIO $ run p m f $ folders y
2123 go
2224 where
25+ folders False = []
26+ folders True = ["hamlet", "cassius", "julius"]
2327 go = do
2428 x <- getLine
2529 case x of