packages feed

nested-routes 0.0.1.1 → 0.0.2

raw patch · 4 files changed

+159/−98 lines, 4 filesdep +blaze-htmldep +bytestringdep +lucidPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: blaze-html, bytestring, lucid

API changes (from Hackage documentation)

- Web.Routes.Nested: HandlerT :: WriterT (MergeRooted Text (Verbs Response)) m a -> HandlerT m a
- Web.Routes.Nested: instance Applicative m => Applicative (HandlerT m)
- Web.Routes.Nested: instance Functor m => Functor (HandlerT m)
- Web.Routes.Nested: instance Monad m => Monad (HandlerT m)
- Web.Routes.Nested: instance MonadIO m => MonadIO (HandlerT m)
- Web.Routes.Nested: instance MonadTrans HandlerT
- Web.Routes.Nested: newtype HandlerT m a
- Web.Routes.Nested: runHandler :: HandlerT m a -> WriterT (MergeRooted Text (Verbs Response)) m a
- Web.Routes.Nested.VerbListener: instance Applicative m => Applicative (VerbListenerT r m)
- Web.Routes.Nested.VerbListener: instance Eq a => Eq (Verbs a)
- Web.Routes.Nested.VerbListener: instance Foldable Verbs
- Web.Routes.Nested.VerbListener: instance Functor Verbs
- Web.Routes.Nested.VerbListener: instance Functor m => Functor (VerbListenerT r m)
- Web.Routes.Nested.VerbListener: instance Monad m => Monad (VerbListenerT r m)
- Web.Routes.Nested.VerbListener: instance MonadIO m => MonadIO (VerbListenerT r m)
- Web.Routes.Nested.VerbListener: instance MonadTrans (VerbListenerT r)
- Web.Routes.Nested.VerbListener: instance Monoid (Verbs a)
- Web.Routes.Nested.VerbListener: instance Show a => Show (Verbs a)
- Web.Routes.Nested.VerbListener: instance Traversable Verbs
+ Web.Routes.Nested: instance Applicative m => Applicative (HandlerT z m)
+ Web.Routes.Nested: instance Functor m => Functor (HandlerT z m)
+ Web.Routes.Nested: instance Monad m => Monad (HandlerT z m)
+ Web.Routes.Nested: instance MonadIO m => MonadIO (HandlerT z m)
+ Web.Routes.Nested: instance MonadTrans (HandlerT z)
+ Web.Routes.Nested.FileExtListener: blaze :: Monad m => Html -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener: builder :: Monad m => Builder -> RequestHeaders -> FileExt -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener: bytestring :: Monad m => ByteString -> RequestHeaders -> FileExt -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener: lucid :: Monad m => HtmlT m () -> FileExtListenerT Response m ()
+ Web.Routes.Nested.VerbListener: delete :: Monad m => FileExtListenerT Response m a -> VerbListenerT z Response m ()
+ Web.Routes.Nested.VerbListener: instance Applicative m => Applicative (VerbListenerT z r m)
+ Web.Routes.Nested.VerbListener: instance Foldable (Verbs z)
+ Web.Routes.Nested.VerbListener: instance Functor (Verbs z)
+ Web.Routes.Nested.VerbListener: instance Functor m => Functor (VerbListenerT z r m)
+ Web.Routes.Nested.VerbListener: instance Monad m => Monad (VerbListenerT z r m)
+ Web.Routes.Nested.VerbListener: instance MonadIO m => MonadIO (VerbListenerT z r m)
+ Web.Routes.Nested.VerbListener: instance MonadTrans (VerbListenerT z r)
+ Web.Routes.Nested.VerbListener: instance Monoid (Verbs z a)
+ Web.Routes.Nested.VerbListener: instance Traversable (Verbs z)
+ Web.Routes.Nested.VerbListener: post :: (Monad m, MonadIO m) => (ByteString -> z) -> FileExtListenerT Response m a -> VerbListenerT z Response m ()
+ Web.Routes.Nested.VerbListener: put :: (Monad m, MonadIO m) => (ByteString -> z) -> FileExtListenerT Response m a -> VerbListenerT z Response m ()
+ Web.Routes.Nested.VerbListener: type ReqBodyT z = ByteString -> z
- Web.Routes.Nested: handle :: Monad m => [Text] -> VerbListenerT Response m () -> HandlerT m ()
+ Web.Routes.Nested: handle :: Monad m => [Text] -> VerbListenerT z Response m () -> HandlerT z m ()
- Web.Routes.Nested: route :: (Functor m, Monad m) => HandlerT m a -> Request -> (Response -> m b) -> m b
+ Web.Routes.Nested: route :: (Functor m, Monad m, MonadIO m) => HandlerT z m a -> Request -> (Response -> m b) -> m b
- Web.Routes.Nested.VerbListener: VerbListenerT :: WriterT (Verbs r) m a -> VerbListenerT r m a
+ Web.Routes.Nested.VerbListener: VerbListenerT :: WriterT (Verbs z r) m a -> VerbListenerT z r m a
- Web.Routes.Nested.VerbListener: Verbs :: Map (FileExt, Verb) a -> Verbs a
+ Web.Routes.Nested.VerbListener: Verbs :: Map Verb (Maybe (ReqBodyT z), FileExts a) -> Verbs z a
- Web.Routes.Nested.VerbListener: get :: Monad m => FileExtListenerT Response m a -> VerbListenerT Response m ()
+ Web.Routes.Nested.VerbListener: get :: Monad m => FileExtListenerT Response m a -> VerbListenerT z Response m ()
- Web.Routes.Nested.VerbListener: newtype VerbListenerT r m a
+ Web.Routes.Nested.VerbListener: newtype VerbListenerT z r m a
- Web.Routes.Nested.VerbListener: newtype Verbs a
+ Web.Routes.Nested.VerbListener: newtype Verbs z a
- Web.Routes.Nested.VerbListener: runVerbListenerT :: VerbListenerT r m a -> WriterT (Verbs r) m a
+ Web.Routes.Nested.VerbListener: runVerbListenerT :: VerbListenerT z r m a -> WriterT (Verbs z r) m a
- Web.Routes.Nested.VerbListener: unVerbs :: Verbs a -> Map (FileExt, Verb) a
+ Web.Routes.Nested.VerbListener: unVerbs :: Verbs z a -> Map Verb (Maybe (ReqBodyT z), FileExts a)

Files

nested-routes.cabal view
@@ -1,5 +1,5 @@ Name:                   nested-routes-Version:                0.0.1.1+Version:                0.0.2 Author:                 Athan Clark <athan.clark@gmail.com> Maintainer:             Athan Clark <athan.clark@gmail.com> License:                BSD3@@ -27,6 +27,9 @@                       , pseudo-trie >= 0.0.4                       , text                       , aeson+                      , blaze-html+                      , lucid+                      , bytestring  Test-Suite spec   Type:                 exitcode-stdio-1.0
src/Web/Routes/Nested.hs view
@@ -6,7 +6,12 @@ {-# LANGUAGE TypeOperators              #-} {-# LANGUAGE OverloadedStrings          #-} -module Web.Routes.Nested where+module Web.Routes.Nested+  ( module Web.Routes.Nested.FileExtListener+  , module Web.Routes.Nested.VerbListener+  , handle+  , route+  ) where  import           Web.Routes.Nested.FileExtListener import           Web.Routes.Nested.VerbListener@@ -29,20 +34,21 @@ import qualified Data.Text                         as T import qualified Data.Map.Lazy                     as M -newtype HandlerT m a = HandlerT-  { runHandler :: WriterT (MergeRooted T.Text (Verbs Response)) m a }++newtype HandlerT z m a = HandlerT+  { runHandler :: WriterT (MergeRooted T.Text (Verbs z Response)) m a }   deriving (Functor) -deriving instance Applicative m => Applicative (HandlerT m)-deriving instance Monad m =>       Monad       (HandlerT m)-deriving instance MonadIO m =>     MonadIO     (HandlerT m)-deriving instance                  MonadTrans   HandlerT+deriving instance Applicative m => Applicative (HandlerT z m)+deriving instance Monad m =>       Monad       (HandlerT z m)+deriving instance MonadIO m =>     MonadIO     (HandlerT z m)+deriving instance                  MonadTrans  (HandlerT z)   handle :: Monad m =>           [T.Text]-       -> VerbListenerT Response m ()-       -> HandlerT m ()+       -> VerbListenerT z Response m ()+       -> HandlerT z m () handle ts vl = do   vfrs <- lift $ execWriterT $ runVerbListenerT vl @@ -51,32 +57,56 @@       [] -> MergeRooted $ Rooted (Just vfrs) []       _  -> MergeRooted $ Rooted Nothing [Rest (NE.fromList ts) vfrs] -route :: (Functor m, Monad m) =>-         HandlerT m a+route :: (Functor m, Monad m, MonadIO m) =>+         HandlerT z m a       -> Request -> (Response -> m b) -> m b route h req respond = do   trie <- unMergeRooted <$> (execWriterT $ runHandler h)   let mMethod = httpMethodToMSym $ requestMethod req-      mFileext = possibleExts $ T.pack $ getFileExt $-                                T.unpack $ last $ pathInfo req+      mFileext = case pathInfo req of+                   [] -> Just Html+                   xs -> possibleExts $ getFileExt $ last xs    case (mFileext, mMethod) of-    (Just f, Just m) -> case R.lookup (pathInfo req) trie of-      Just map -> case M.lookup (f,m) $ unVerbs map of-        Just r  -> respond r+    (Just f, Just v) -> let cleanedPathInfo = applyToLast trimFileExt (pathInfo req) in+                        case R.lookup cleanedPathInfo trie of+      Just vmap -> case M.lookup v $ unVerbs vmap of+        Just (mreqbodyf,femap) ->+          case lookupMin f $ unFileExts femap of+            Just r -> do+              case mreqbodyf of+                Nothing    -> respond r+                Just reqbf -> do+                  body <- liftIO $ strictRequestBody req+                  return $ reqbf body+                  respond r+            Nothing -> respond notFound         Nothing -> respond notFound       Nothing  -> respond notFound     _ -> respond notFound    where-    getFileExt :: String -> String-    getFileExt s = case foldr go Nothing s of-                     Nothing -> ""-                     Just x  -> x+    lookupMin k map | all (k <) (M.keys map) = M.lookup (minimum $ M.keys map) map+                    | otherwise              = M.lookup k map++    getFileExt :: T.Text -> T.Text+    getFileExt s =+      let mfound = foldl go Nothing $ T.unpack s in+      case mfound of+        Nothing -> T.pack ""+        Just x  -> T.pack x       where-        go '.' _         = Just "."-        go x   (Just xs) = Just $ xs ++ [x]-        go x   Nothing   = Nothing+        go Nothing x | x == '.'  = Just "."+                     | otherwise = Nothing+        go (Just xs) x = Just $ xs ++ [x]++    applyToLast :: (a -> a) -> [a] -> [a]+    applyToLast f [] = []+    applyToLast f (x:[]) = f x : []+    applyToLast f (x:xs) = x : applyToLast f xs++    trimFileExt :: T.Text -> T.Text+    trimFileExt s = T.pack $ takeWhile (/= '.') $ T.unpack s      httpMethodToMSym :: Method -> Maybe Verb     httpMethodToMSym x | x == methodGet    = Just Get
src/Web/Routes/Nested/FileExtListener.hs view
@@ -10,7 +10,12 @@ import qualified Data.Text               as T import qualified Data.Text.Lazy          as LT import qualified Data.Text.Lazy.Encoding as LT-import           Network.HTTP.Types      (status200)+import qualified Data.ByteString.Lazy    as B+import qualified Data.ByteString.Builder as BU+import qualified Text.Blaze.Html         as H+import qualified Text.Blaze.Html.Renderer.Text as H+import qualified Lucid.Base              as L+import           Network.HTTP.Types      (status200, RequestHeaders) import           Network.Wai  import           Control.Applicative@@ -27,11 +32,16 @@              | Text   deriving (Show, Eq, Ord) + possibleExts :: T.Text -> Maybe FileExt-possibleExts x | x `elem` ["", ".htm", ".html"] = Just Html-               | x `elem` [".json"]             = Just Json-               | x `elem` [".txt"]              = Just Text-               | otherwise                      = Nothing+possibleExts x | x `elem` htmls = Just Html+               | x `elem` jsons = Just Json+               | x `elem` texts = Just Text+               | otherwise      = Nothing+  where+    htmls = ["", ".htm", ".html"]+    jsons = [".json"]+    texts = [".txt"]  newtype FileExts a = FileExts { unFileExts :: Map FileExt a }   deriving (Show, Eq, Functor, Traversable)@@ -51,55 +61,63 @@   json :: (A.ToJSON j, Monad m) =>-        j-     -> FileExtListenerT Response m ()+        j -> FileExtListenerT Response m () json i =   let r = responseLBS status200 [("Content-Type", "application/json")] $-            A.encode i-  in+            A.encode i in   FileExtListenerT $ tell $     FileExts $ singleton Json r + jsonp :: (A.ToJSON j, Monad m) =>-         j-      -> FileExtListenerT Response m ()+         j -> FileExtListenerT Response m () jsonp i =   let r = responseLBS status200 [("Content-Type", "application/javascript")] $-            A.encode i-  in+            A.encode i in   FileExtListenerT $ tell $     FileExts $ singleton Json r + text :: (Monad m) =>-        LT.Text-     -> FileExtListenerT Response m ()+        LT.Text -> FileExtListenerT Response m () text i =   let r = responseLBS status200 [("Content-Type", "text/plain")] $-            LT.encodeUtf8 i-  in+            LT.encodeUtf8 i in   FileExtListenerT $ tell $     FileExts $ singleton Text r------ blaze :: Html -> Response--- blaze = HR.renderHtml------ lucid :: Monad m => HtmlT m () -> Response--- lucid = L.renderTextT --- json :: ToJSON a => a -> Response--- json = lazy-bytestring . encode --- text :: T.Text -> Response--- text = bytestring . TR.encodeUtf8+blaze :: (Monad m ) =>+         H.Html -> FileExtListenerT Response m ()+blaze i =+  let r = responseLBS status200 [("Content-Type", "text/html")] $+            LT.encodeUtf8 $ H.renderHtml i in+  FileExtListenerT $ tell $+    FileExts $ singleton Html r --- lazy-text :: LT.Text -> Response--- lazy-text = lazy-bytestring . LTR.encodeUtf8------ builder :: Builder -> Response--- builder = responseBuilder------ bytestring :: B.ByteString -> Response--- bytestring = lazy-bytestring . LB.fromStrict------ lazy-bytestring :: LB.ByteString -> Response--- lazy-bytestring = responseLBS++lucid :: (Monad m) =>+         L.HtmlT m () -> FileExtListenerT Response m ()+lucid i = do+  i' <- lift $ L.renderBST i+  let r = responseLBS status200 [("Content-Type", "text/html")] $ i'+  FileExtListenerT $ tell $+    FileExts $ singleton Html r+++builder :: (Monad m) =>+           BU.Builder -> RequestHeaders+        -> FileExt -> FileExtListenerT Response m ()+builder i hs e =+  let r = responseBuilder status200 hs i in+  FileExtListenerT $ tell $+    FileExts $ singleton e r+++bytestring :: (Monad m) =>+              B.ByteString -> RequestHeaders+           -> FileExt -> FileExtListenerT Response m ()+bytestring i hs e =+  let r = responseLBS status200 hs i in+  FileExtListenerT $ tell $+    FileExts $ singleton e r
src/Web/Routes/Nested/VerbListener.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveFunctor              #-} {-# LANGUAGE DeriveTraversable          #-}+{-# LANGUAGE DeriveFoldable             #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ScopedTypeVariables        #-} {-# LANGUAGE StandaloneDeriving         #-}@@ -17,6 +18,7 @@ import           Data.Foldable import           Data.Traversable import           Data.Map.Lazy+import qualified Data.ByteString.Lazy                 as BL   data Verb = Get@@ -25,52 +27,60 @@           | Delete   deriving (Show, Eq, Ord) -newtype Verbs a = Verbs { unVerbs :: Map (FileExt, Verb) a }-  deriving (Show, Eq, Functor, Traversable)+type ReqBodyT z = BL.ByteString -> z -deriving instance Monoid      (Verbs a)-deriving instance Foldable     Verbs+newtype Verbs z a = Verbs { unVerbs :: Map Verb (Maybe (ReqBodyT z), FileExts a) }+  deriving (Functor, Traversable) +deriving instance             Monoid    (Verbs z a)+deriving instance             Foldable  (Verbs z) -newtype VerbListenerT r m a =-  VerbListenerT { runVerbListenerT :: WriterT (Verbs r) m a }+newtype VerbListenerT z r m a =+  VerbListenerT { runVerbListenerT :: WriterT (Verbs z r) m a }     deriving (Functor) -deriving instance Applicative m => Applicative (VerbListenerT r m)-deriving instance Monad m =>       Monad       (VerbListenerT r m)-deriving instance MonadIO m =>     MonadIO     (VerbListenerT r m)-deriving instance                  MonadTrans  (VerbListenerT r)+deriving instance Applicative m => Applicative (VerbListenerT z r m)+deriving instance Monad m =>       Monad       (VerbListenerT z r m)+deriving instance MonadIO m =>     MonadIO     (VerbListenerT z r m)+deriving instance                  MonadTrans  (VerbListenerT z r)   get :: (Monad m) =>        FileExtListenerT Response m a-    -> VerbListenerT Response m ()+    -> VerbListenerT z Response m () get flistener = do-  (fileexts :: FileExts Response) <--      lift $ execWriterT $ runFileExtListenerT flistener-  let new = foldrWithKey (\k -> insert (k, Get)) empty $ unFileExts fileexts+  (fileexts :: FileExts Response) <- lift $ execWriterT $+                                     runFileExtListenerT flistener+  let new = singleton Get (Nothing, fileexts)+  VerbListenerT $ tell $ Verbs new -  VerbListenerT $ tell $ Verbs $ new --- post :: MonadIO m =>---         (ByteString -> m ())---      -> FileExtListener ()---      -> VerbListener ()--- post fl =---   VerbListener $ tell $---     Verbs [(Post, fl)]------ put :: MonadIO m =>---        (ByteString -> m ())---     -> FileExtListener ()---     -> VerbListener ()--- put fl =---   VerbListener $ tell $---     Verbs [(Put, fl)]+post :: (Monad m, MonadIO m) =>+        (BL.ByteString -> z)+     -> FileExtListenerT Response m a+     -> VerbListenerT z Response m ()+post handle flistener = do+  (fileexts :: FileExts Response) <- lift $ execWriterT $+                                     runFileExtListenerT flistener+  let new = singleton Post (Just handle, fileexts)+  VerbListenerT $ tell $ Verbs new --- delete :: (Monad m) =>---           FileExtListenerT m ()---        -> VerbListenerT m ()--- delete fl =---   VerbListenerT $ tell $---     Verbs [(Post, fl)]++put :: (Monad m, MonadIO m) =>+       (BL.ByteString -> z)+    -> FileExtListenerT Response m a+    -> VerbListenerT z Response m ()+put handle flistener = do+  (fileexts :: FileExts Response) <- lift $ execWriterT $+                                     runFileExtListenerT flistener+  let new = singleton Put (Just handle, fileexts)+  VerbListenerT $ tell $ Verbs new++delete :: (Monad m) =>+          FileExtListenerT Response m a+       -> VerbListenerT z Response m ()+delete flistener = do+  (fileexts :: FileExts Response) <- lift $ execWriterT $+                                     runFileExtListenerT flistener+  let new = singleton Delete (Nothing, fileexts)+  VerbListenerT $ tell $ Verbs new