packages feed

nested-routes 0.3.2.1 → 1.0

raw patch · 16 files changed

+457/−367 lines, 16 filesdep +claydep +compositiondep +shakespearePVP ok

version bump matches the API change (PVP)

Dependencies added: clay, composition, shakespeare, wai-util, witherable

API changes (from Hackage documentation)

- Web.Routes.Nested: handleLit :: (Monad m, Functor m, cleanxs ~ OnlyJusts xs, HasResult childType (EitherResponse z m), ExpectArity cleanxs childType, Singleton (UrlChunks xs) childType (RUPTrie Text result), Extrude (UrlChunks xs) (RUPTrie Text childType) (RUPTrie Text result), (ArityMinusTypeList childType cleanxs) ~ result, childType ~ TypeListToArity cleanxs result, LastIsNothing xs) => UrlChunks xs -> childType -> Maybe (HandlerT z childType m ()) -> HandlerT z result m ()
- Web.Routes.Nested: handleParse :: (Monad m, Functor m, cleanxs ~ OnlyJusts xs, HasResult childType (EitherResponse z m), ExpectArity cleanxs childType, Singleton (UrlChunks xs) childType (RUPTrie Text result), Extrude (UrlChunks xs) (RUPTrie Text childType) (RUPTrie Text result), (ArityMinusTypeList childType cleanxs) ~ result, childType ~ TypeListToArity cleanxs result, LastIsJust xs) => UrlChunks xs -> childType -> Maybe (HandlerT z childType m ()) -> HandlerT z result m ()
- Web.Routes.Nested: notFoundLit :: (Monad m, Functor m, cleanxs ~ OnlyJusts xs, HasResult childType (EitherResponse z m), ExpectArity cleanxs childType, Singleton (UrlChunks xs) childType (RUPTrie Text result), Extrude (UrlChunks xs) (RUPTrie Text childType) (RUPTrie Text result), (ArityMinusTypeList childType cleanxs) ~ result, childType ~ TypeListToArity cleanxs result, LastIsNothing xs) => UrlChunks xs -> childType -> Maybe (HandlerT z childType m ()) -> HandlerT z result m ()
- Web.Routes.Nested: notFoundParse :: (Monad m, Functor m, cleanxs ~ OnlyJusts xs, HasResult childType (EitherResponse z m), ExpectArity cleanxs childType, Singleton (UrlChunks xs) childType (RUPTrie Text result), Extrude (UrlChunks xs) (RUPTrie Text childType) (RUPTrie Text result), (ArityMinusTypeList childType cleanxs) ~ result, childType ~ TypeListToArity cleanxs result, LastIsJust xs) => UrlChunks xs -> childType -> Maybe (HandlerT z childType m ()) -> HandlerT z result m ()
- Web.Routes.Nested: type EitherResponse z m = Either (VerbListenerT z (FileExtListenerT Response m ()) m ()) (VerbListenerT z Response m ())
- Web.Routes.Nested.Types: instance [overlap ok] (Extrude (UrlChunks xs) trie0 trie1, Extend (EitherUrlChunk x) trie1 trie2) => Extrude (UrlChunks (x : xs)) trie0 trie2
- Web.Routes.Nested.Types: instance [overlap ok] (Singleton (UrlChunks xs) a trie0, Extend (EitherUrlChunk x) trie0 trie1) => Singleton (UrlChunks (x : xs)) a trie1
- Web.Routes.Nested.Types: instance [overlap ok] Extend (EitherUrlChunk 'Nothing) (RUPTrie Text a) (RUPTrie Text a)
- Web.Routes.Nested.Types: instance [overlap ok] Extend (EitherUrlChunk ('Just r)) (RUPTrie Text (r -> a)) (RUPTrie Text a)
- Web.Routes.Nested.Types: instance [overlap ok] Extrude (UrlChunks '[]) (RUPTrie Text a) (RUPTrie Text a)
- Web.Routes.Nested.Types: instance [overlap ok] Singleton (UrlChunks '[]) a (RUPTrie Text a)
- Web.Routes.Nested.Types: instance [overlap ok] ToL (UrlChunks '[])
- Web.Routes.Nested.Types: instance [overlap ok] ToL (UrlChunks xs) => ToL (UrlChunks ('Nothing : xs))
- Web.Routes.Nested.Types: instance [overlap ok] ToNE (UrlChunks '['Nothing])
- Web.Routes.Nested.Types: instance [overlap ok] ToNE (UrlChunks xs) => ToNE (UrlChunks ('Nothing : xs))
+ Web.Routes.Nested: handle :: (Monad m, Functor m, cleanxs ~ OnlyJusts xs, HasResult childType (ActionT z m ()), ExpectArity cleanxs childType, Singleton (UrlChunks xs) childType (RUPTrie Text result), Extrude (UrlChunks xs) (RUPTrie Text childType) (RUPTrie Text result), (ArityMinusTypeList childType cleanxs) ~ result, childType ~ TypeListToArity cleanxs result, LastIsNothing xs) => UrlChunks xs -> childType -> Maybe (HandlerT z childType m ()) -> HandlerT z result m ()
+ Web.Routes.Nested: notFound :: (Monad m, Functor m, cleanxs ~ OnlyJusts xs, HasResult childType (ActionT z m ()), ExpectArity cleanxs childType, Singleton (UrlChunks xs) childType (RUPTrie Text result), Extrude (UrlChunks xs) (RUPTrie Text childType) (RUPTrie Text result), (ArityMinusTypeList childType cleanxs) ~ result, childType ~ TypeListToArity cleanxs result) => UrlChunks xs -> childType -> Maybe (HandlerT z childType m ()) -> HandlerT z result m ()
+ Web.Routes.Nested: type ActionT z m a = VerbListenerT z (FileExtListenerT Response m a) m a
+ Web.Routes.Nested.FileExtListener.Cassius: cassius :: Monad m => Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Cassius: cassiusHeaders :: Monad m => RequestHeaders -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Cassius: cassiusOnly :: Css -> Response
+ Web.Routes.Nested.FileExtListener.Cassius: cassiusOnlyHeaders :: RequestHeaders -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Cassius: cassiusOnlyStatus :: Status -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Cassius: cassiusOnlyStatusHeaders :: Status -> RequestHeaders -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Cassius: cassiusStatus :: Monad m => Status -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Cassius: cassiusStatusHeaders :: Monad m => Status -> RequestHeaders -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Clay: clay :: Monad m => Config -> [App] -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Clay: clayHeaders :: Monad m => Config -> [App] -> RequestHeaders -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Clay: clayOnly :: Config -> [App] -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Clay: clayOnlyHeaders :: Config -> [App] -> RequestHeaders -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Clay: clayOnlyStatus :: Config -> [App] -> Status -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Clay: clayOnlyStatusHeaders :: Config -> [App] -> Status -> RequestHeaders -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Clay: clayStatus :: Monad m => Config -> [App] -> Status -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Clay: clayStatusHeaders :: Monad m => Config -> [App] -> Status -> RequestHeaders -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Julius: julius :: Monad m => Javascript -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Julius: juliusHeaders :: Monad m => RequestHeaders -> Javascript -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Julius: juliusOnly :: Javascript -> Response
+ Web.Routes.Nested.FileExtListener.Julius: juliusOnlyHeaders :: RequestHeaders -> Javascript -> Response
+ Web.Routes.Nested.FileExtListener.Julius: juliusOnlyStatus :: Status -> Javascript -> Response
+ Web.Routes.Nested.FileExtListener.Julius: juliusOnlyStatusHeaders :: Status -> RequestHeaders -> Javascript -> Response
+ Web.Routes.Nested.FileExtListener.Julius: juliusStatus :: Monad m => Status -> Javascript -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Julius: juliusStatusHeaders :: Monad m => Status -> RequestHeaders -> Javascript -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Lucius: lucius :: Monad m => Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Lucius: luciusHeaders :: Monad m => RequestHeaders -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Lucius: luciusOnly :: Css -> Response
+ Web.Routes.Nested.FileExtListener.Lucius: luciusOnlyHeaders :: RequestHeaders -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Lucius: luciusOnlyStatus :: Status -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Lucius: luciusOnlyStatusHeaders :: Status -> RequestHeaders -> Css -> Response
+ Web.Routes.Nested.FileExtListener.Lucius: luciusStatus :: Monad m => Status -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Lucius: luciusStatusHeaders :: Monad m => Status -> RequestHeaders -> Css -> FileExtListenerT Response m ()
+ Web.Routes.Nested.FileExtListener.Types: [Css] :: FileExt
+ Web.Routes.Nested.FileExtListener.Types: [JavaScript] :: FileExt
+ Web.Routes.Nested.Types: instance (Extrude (UrlChunks xs) trie0 trie1, Extend (EitherUrlChunk x) trie1 trie2) => Extrude (UrlChunks (x : xs)) trie0 trie2
+ Web.Routes.Nested.Types: instance (Singleton (UrlChunks xs) a trie0, Extend (EitherUrlChunk x) trie0 trie1) => Singleton (UrlChunks (x : xs)) a trie1
+ Web.Routes.Nested.Types: instance Extend (EitherUrlChunk 'Nothing) (RUPTrie Text a) (RUPTrie Text a)
+ Web.Routes.Nested.Types: instance Extend (EitherUrlChunk ('Just r)) (RUPTrie Text (r -> a)) (RUPTrie Text a)
+ Web.Routes.Nested.Types: instance Extrude (UrlChunks '[]) (RUPTrie Text a) (RUPTrie Text a)
+ Web.Routes.Nested.Types: instance Singleton (UrlChunks '[]) a (RUPTrie Text a)
+ Web.Routes.Nested.Types: instance ToL (UrlChunks '[])
+ Web.Routes.Nested.Types: instance ToL (UrlChunks xs) => ToL (UrlChunks ('Nothing : xs))
+ Web.Routes.Nested.Types: instance ToNE (UrlChunks '['Nothing])
+ Web.Routes.Nested.Types: instance ToNE (UrlChunks xs) => ToNE (UrlChunks ('Nothing : xs))
+ Web.Routes.Nested.Types.UrlChunks: instance (x ~ 'Nothing) => IsString (EitherUrlChunk x)
- Web.Routes.Nested: route :: (Functor m, Monad m, MonadIO m) => HandlerT z (EitherResponse z m) m a -> Request -> (Response -> IO ResponseReceived) -> m ResponseReceived
+ Web.Routes.Nested: route :: (Functor m, Monad m, MonadIO m) => HandlerT z (ActionT z m ()) m a -> Request -> (Response -> IO ResponseReceived) -> m ResponseReceived

Files

nested-routes.cabal view
@@ -1,5 +1,5 @@ Name:                   nested-routes-Version:                0.3.2.1+Version:                1.0 Author:                 Athan Clark <athan.clark@gmail.com> Maintainer:             Athan Clark <athan.clark@gmail.com> License:                BSD3@@ -25,19 +25,23 @@   > router = route handlers   >   where   >     handlers = do-  >       handleLit o-  >         (Left $ get $ text "home")+  >       handle o+  >         (get $ text "home")   >         Nothing-  >       handleLit (l "foo" </> l "bar" </> o)-  >         (Left $ get $ text "foobar") $ Just $-  >         handleParse (p ("baz",double) </> o)-  >           (\d -> Right $ get $ textOnly $ LT.pack (show d) `LT.append` " bazs")+  >       handle ("foo" </> "bar")+  >         (get $ text "foobar") $ Just $+  >         handle (p ("baz", double) </> o)+  >           (\d -> get $ text $ LT.pack (show d) <> " bazs")   >           Nothing-  >       handleParse (p ("num",double) </> o)-  >         (\d -> Right $ get $ textOnly $ LT.pack $ show d) $ Just $-  >         handleLit (l "bar" </> o)-  >            (\d -> Left $ get $ text $ (LT.pack $ show d) `LT.append` " bars")+  >       handle (p ("num",double) </> o)+  >         (\d -> get $ text $ LT.pack $ show d) $ Just $ do+  >         handle "bar"+  >            (\d -> get $ do+  >                     text $ (LT.pack $ show d) <> " bars")+  >                     json $ (LT.pack $ show d) <> " bars!")   >            Nothing+  >         handle (r ("email", mkRegex "(^[-a-zA-Z0-9_.]+@[-a-zA-Z0-9]+\\.[-a-zA-Z0-9.]+$)") </> o)+  >            (\d e -> get $ textOnly $ (LT.pack $ show d) <> " " <> (LT.pack $ show e)   .   The route specification syntax is a little strange right now - @l@ specifies   a "literal chunk" of a handlable url (ie - @l \"foo\" \<\/\> l \"bar\" \<\/\> o@ would@@ -47,56 +51,50 @@   the end of a url string, and can be used alone in a handler to capture requests   to the root path.   .-  Each route being handled needs some kind of content - that's where the @Either@-  stuff comes in to play. For every parsed url chunk, the route expects a function+  Each route being handled needs some kind of content. For every parsed url chunk,+  the route expects a function   of arity matching 1-for-1 with the parsed contents. For example, @\d -> ...@ in the   demonstration above is such a function, where @d :: Double@.   .-  We use the @Either@ for a subtle reason - literal url strings may have a file-  extension, while url strings ending with a parser would not. @get@, @post@, etc.-  are all monadic expressions, accumulating a @Map@ for HTTP verbs, likewise with-  @text@, @lucid@, @json@, @bytestring@ etc., where they may also match a particular-  file extension. @textOnly@ and the other @-Only@ variants are not monadic, and-  simply give us a convenient unwrapper. Basically, url paths ending with a literal-  chunk are @Left@ and contain a @VerbListenerT z (FileExtListenerT Response m ()) m ()@,-  while paths ending with a parser are @Right@ and contain @VerbListenerT z Response m ()@.+  Internally, we match against both the file extension and Accept headers in the+  HTTP request - the Accept header may override the file extension.   .   When we test our application:   .-  >  λ> curl localhost:3000/+  >  λ> curl localhost:3000/ -H "Accept: text/plain, */*"   >  ↪ "home"   .   requests may end with index   .-  >  λ> curl localhost:3000/index+  >  λ> curl localhost:3000/index -H "Accept: text/plain, */*"   >  ↪ "home"   .   and specify the file extension   .-  >  λ> curl localhost:3000/index.txt+  >  λ> curl localhost:3000/index.txt -H "Accept: text/plain, */*"   >  ↪ "home"   .   each responding with the "closest" available file type   .-  >  λ> curl localhost:3000/index.html+  >  λ> curl localhost:3000/index.html -H "Accept: text/html, */*"   >  ↪ "home"   .-  >  λ> curl localhost:3000/foo/bar+  >  λ> curl localhost:3000/foo/bar -H "Accept: text/plain, */*"   >  ↪ "foobar"   .-  >  λ> curl localhost:3000/foo/bar.txt+  >  λ> curl localhost:3000/foo/bar.txt -H "Accept: text/plain, */*"   >  ↪ "foobar"   .-  >  λ> curl localhost:3000/foo/bar/5678.5678+  >  λ> curl localhost:3000/foo/bar/5678.5678 -H "Accept: text/plain, */*"   >  ↪ "5678.5678 bazs"   .-  >  λ> curl localhost:3000/1234.1234+  >  λ> curl localhost:3000/1234.1234 -H "Accept: text/plain, */*"   >  ↪ "1234.1234"   .-  >  λ> curl localhost:3000/2e5+  >  λ> curl localhost:3000/2e5 -H "Accept: text/plain, */*"   >  ↪ "200000.0"   .-  >  λ> curl localhost:3000/1234.1234/bar+  >  λ> curl localhost:3000/1234.1234/bar -H "Accept: text/plain, */*"   >  ↪ "1234.1234 bars"  Cabal-Version:          >= 1.10@@ -118,13 +116,20 @@                         Web.Routes.Nested.FileExtListener.Blaze                         Web.Routes.Nested.FileExtListener.ByteString                         Web.Routes.Nested.FileExtListener.Lucid+                        Web.Routes.Nested.FileExtListener.Clay+                        Web.Routes.Nested.FileExtListener.Julius+                        Web.Routes.Nested.FileExtListener.Lucius+                        Web.Routes.Nested.FileExtListener.Cassius   Build-Depends:        base >= 4.6 && < 5                       , wai                       , wai-extra+                      , wai-util                       , http-types                       , http-media                       , mtl                       , transformers+                      , witherable+                      , composition                       , semigroups                       , constraints                       , containers@@ -132,6 +137,8 @@                       , aeson                       , blaze-html                       , lucid+                      , shakespeare+                      , clay                       , bytestring                       , attoparsec                       , regex-compat
src/Web/Routes/Nested.hs view
@@ -23,16 +23,15 @@   , module Web.Routes.Nested.VerbListener   , module Web.Routes.Nested.Types   , HandlerT (..)-  , EitherResponse-  , handleLit-  , handleParse-  , notFoundLit-  , notFoundParse+  , ActionT+  , handle+  , notFound   , route   ) where  import           Web.Routes.Nested.Types import           Web.Routes.Nested.FileExtListener+import           Web.Routes.Nested.FileExtListener.Types (FileExt) import           Web.Routes.Nested.VerbListener  import           Network.HTTP.Types@@ -48,9 +47,12 @@ import qualified Data.Trie.Pred.Unified            as P import qualified Data.Text                         as T import qualified Data.Map.Lazy                     as M+import qualified Data.ByteString                   as B import qualified Data.ByteString.Lazy              as BL import           Data.Maybe                        (fromMaybe) import           Data.Constraint+import           Data.Witherable+import           Data.List  import Data.Function.Poly @@ -66,9 +68,7 @@ instance MonadTrans (HandlerT z x) where   lift ma = HandlerT $ lift ma --type EitherResponse z m = Either (VerbListenerT z (FileExtListenerT Response m ()) m ())-                                 (VerbListenerT z Response m ())+type ActionT z m a = VerbListenerT z (FileExtListenerT Response m a) m a  type family LastIsNothing (xs :: [Maybe *]) :: Constraint where   LastIsNothing '[] = ()@@ -80,107 +80,53 @@   LastIsJust (x ': xs) = LastIsJust xs  -- | For routes ending with a literal.-handleLit :: ( Monad m-             , Functor m-             , cleanxs ~ OnlyJusts xs-             , HasResult childType (EitherResponse z m)-             , ExpectArity cleanxs childType-             , Singleton (UrlChunks xs)-                 childType-                 (RUPTrie T.Text result)-             , Extrude (UrlChunks xs)-                 (RUPTrie T.Text childType)-                 (RUPTrie T.Text result)-             , (ArityMinusTypeList childType cleanxs) ~ result-             , childType ~ TypeListToArity cleanxs result-             , LastIsNothing xs-             ) =>-             UrlChunks xs -- ^ Path to match against-          -> childType -- ^ Possibly a function, ending in @EitherResponse z m@-          -> Maybe (HandlerT z childType m ()) -- ^ Potential child routes-          -> HandlerT z result m ()-handleLit ts vl Nothing =-  HandlerT $ tell (singleton ts vl, mempty)-handleLit ts vl (Just cs) = do-  ((Rooted _ ctrie),_) <- lift $ execWriterT $ runHandler cs-  HandlerT $ tell (extrude ts $ Rooted (Just vl) ctrie, mempty)----- | For routes ending with a parser.-handleParse :: ( Monad m-               , Functor m-               , cleanxs ~ OnlyJusts xs-               , HasResult childType (EitherResponse z m)-               , ExpectArity cleanxs childType-               , Singleton (UrlChunks xs)-                   childType-                   (RUPTrie T.Text result)-               , Extrude (UrlChunks xs)-                   (RUPTrie T.Text childType)-                   (RUPTrie T.Text result)-               , (ArityMinusTypeList childType cleanxs) ~ result-               , childType ~ TypeListToArity cleanxs result-               , LastIsJust xs-               ) =>-               UrlChunks xs-            -> childType-            -> Maybe (HandlerT z childType m ())-            -> HandlerT z result m ()-handleParse ts vl Nothing =+handle :: ( Monad m+          , Functor m+          , cleanxs ~ OnlyJusts xs+          , HasResult childType (ActionT z m ())+          , ExpectArity cleanxs childType+          , Singleton (UrlChunks xs)+              childType+              (RUPTrie T.Text result)+          , Extrude (UrlChunks xs)+              (RUPTrie T.Text childType)+              (RUPTrie T.Text result)+          , (ArityMinusTypeList childType cleanxs) ~ result+          , childType ~ TypeListToArity cleanxs result+          , LastIsNothing xs+          ) =>+          UrlChunks xs -- ^ Path to match against+       -> childType -- ^ Possibly a function, ending in @ActionT z m ()@.+       -> Maybe (HandlerT z childType m ()) -- ^ Potential child routes+       -> HandlerT z result m ()+handle ts vl Nothing =   HandlerT $ tell (singleton ts vl, mempty)-handleParse ts vl (Just cs) = do+handle ts vl (Just cs) = do   ((Rooted _ ctrie),_) <- lift $ execWriterT $ runHandler cs   HandlerT $ tell (extrude ts $ Rooted (Just vl) ctrie, mempty)  -notFoundLit :: ( Monad m-               , Functor m-               , cleanxs ~ OnlyJusts xs-               , HasResult childType (EitherResponse z m)-               , ExpectArity cleanxs childType-               , Singleton (UrlChunks xs)-                   childType-                   (RUPTrie T.Text result)-               , Extrude (UrlChunks xs)-                   (RUPTrie T.Text childType)-                   (RUPTrie T.Text result)-               , (ArityMinusTypeList childType cleanxs) ~ result-               , childType ~ TypeListToArity cleanxs result-               , LastIsNothing xs-               ) =>-               UrlChunks xs-            -> childType-            -> Maybe (HandlerT z childType m ())-            -> HandlerT z result m ()-notFoundLit ts vl Nothing = do-  HandlerT $ tell (mempty, singleton ts vl)-notFoundLit ts vl (Just cs) = do-  ((Rooted _ ctrie),_) <- lift $ execWriterT $ runHandler cs-  HandlerT $ tell (mempty, extrude ts $ Rooted (Just vl) ctrie)---notFoundParse :: ( Monad m-                 , Functor m-                 , cleanxs ~ OnlyJusts xs-                 , HasResult childType (EitherResponse z m)-                 , ExpectArity cleanxs childType-                 , Singleton (UrlChunks xs)-                     childType-                     (RUPTrie T.Text result)-                 , Extrude (UrlChunks xs)-                     (RUPTrie T.Text childType)-                     (RUPTrie T.Text result)-                 , (ArityMinusTypeList childType cleanxs) ~ result-                 , childType ~ TypeListToArity cleanxs result-                 , LastIsJust xs-                 ) =>-                 UrlChunks xs-              -> childType-              -> Maybe (HandlerT z childType m ())-              -> HandlerT z result m ()-notFoundParse ts vl Nothing = do+notFound :: ( Monad m+            , Functor m+            , cleanxs ~ OnlyJusts xs+            , HasResult childType (ActionT z m ())+            , ExpectArity cleanxs childType+            , Singleton (UrlChunks xs)+                childType+                (RUPTrie T.Text result)+            , Extrude (UrlChunks xs)+                (RUPTrie T.Text childType)+                (RUPTrie T.Text result)+            , (ArityMinusTypeList childType cleanxs) ~ result+            , childType ~ TypeListToArity cleanxs result+            ) =>+            UrlChunks xs+         -> childType+         -> Maybe (HandlerT z childType m ())+         -> HandlerT z result m ()+notFound ts vl Nothing = do   HandlerT $ tell (mempty, singleton ts vl)-notFoundParse ts vl (Just cs) = do+notFound ts vl (Just cs) = do   ((Rooted _ ctrie),_) <- lift $ execWriterT $ runHandler cs   HandlerT $ tell (mempty, extrude ts $ Rooted (Just vl) ctrie) @@ -190,22 +136,23 @@          , Monad m          , MonadIO m          ) =>-         HandlerT z (EitherResponse z m) m a -- ^ Assembled @handle@ calls+         HandlerT z (ActionT z m ()) m a -- ^ Assembled @handle@ calls       -> Request       -> (Response -> IO ResponseReceived) -> m ResponseReceived route h req respond = do-  -- liftIO $ print $ (return . parseContentType) =<< (Prelude.lookup ("Accept" :: HeaderName) $ requestHeaders req)   (rtrie, nftrie) <- execWriterT $ runHandler h   let mMethod  = httpMethodToMSym $ requestMethod req       mFileext = case pathInfo req of                          [] -> Just Html                          xs -> toExt $ T.pack $ dropWhile (/= '.') $ T.unpack $ last xs-      meitherNotFound = P.lookupNearestParent (pathInfo req) nftrie+      mnftrans = P.lookupNearestParent (pathInfo req) nftrie+      acceptBS = Prelude.lookup ("Accept" :: HeaderName) $ requestHeaders req+      fe = fromMaybe Html mFileext -  notFoundBasic <- handleNotFound (Just Html) Get meitherNotFound+  notFoundBasic <- handleNotFound acceptBS Html Get mnftrans    maybe (liftIO $ respond404 notFoundBasic) (\v -> do-    menf <- handleNotFound mFileext v meitherNotFound+    menf <- handleNotFound acceptBS fe v mnftrans     let cleanedPathInfo = applyToLast trimFileExt $ pathInfo req         fail = liftIO $ respond404 menf @@ -213,10 +160,10 @@         [] -> fail         _  -> case trimFileExt $ last $ pathInfo req of           "index" -> maybe fail-                       (\eitherM -> continue mFileext v eitherM menf)+                       (\foundM -> continue acceptBS fe v foundM menf)                        (P.lookup (init $ pathInfo req) rtrie)           _ -> fail-      ) (\eitherM -> continue mFileext v eitherM menf)+      ) (\foundM -> continue acceptBS fe v foundM menf)       (P.lookup cleanedPathInfo rtrie)     ) mMethod @@ -226,48 +173,43 @@       handleNotFound :: MonadIO m =>-                      Maybe FileExt+                      Maybe B.ByteString+                   -> FileExt                    -> Verb-                   -> Maybe (EitherResponse z m)+                   -> Maybe (ActionT z m ())                    -> m (Maybe Response)-    handleNotFound mf v meitherNotFound =-      let handleEither (Left litmonad) =-            onJustM (\f -> do-                vmapLit <- execWriterT $ runVerbListenerT litmonad-                onJustM (\(_, femonad) -> do-                    femap <- execWriterT $ runFileExtListenerT femonad-                    return $ lookupMin f $ unFileExts femap) $-                  M.lookup v $ unVerbs vmapLit) mf-          handleEither (Right predmonad) = do-            vmapPred <- execWriterT $ runVerbListenerT predmonad-            onJustM (\(_, r) -> return $ Just r) $ M.lookup v $ unVerbs vmapPred+    handleNotFound acceptBS f v mnfcomp =+      let handleEither nfcomp = do+            vmapLit <- execWriterT $ runVerbListenerT nfcomp+            onJustM (\(_, femonad) -> do+                femap <- execWriterT $ runFileExtListenerT femonad+                return $ lookupProper acceptBS f $ unFileExts femap+              ) $ M.lookup v $ unVerbs vmapLit       in-      onJustM handleEither meitherNotFound+      onJustM handleEither mnfcomp       continue :: MonadIO m =>-                Maybe FileExt+                Maybe B.ByteString+             -> FileExt              -> Verb-             -> EitherResponse z m+             -> ActionT z m ()              -> Maybe Response              -> m ResponseReceived-    continue mf v eitherM mnfResp = case eitherM of-       Left litmonad -> maybe (liftIO $ respond404 mnfResp) (\f -> do-                          vmapLit <- execWriterT $ runVerbListenerT litmonad-                          continueLit f v (unVerbs vmapLit) mnfResp)-                        mf-       Right predmonad -> do-         vmapPred <- execWriterT $ runVerbListenerT predmonad-         continuePred v (unVerbs vmapPred) mnfResp+    continue acceptBS f v foundM mnfResp = do+      vmapLit <- execWriterT $ runVerbListenerT foundM+      continueMap acceptBS f v (unVerbs vmapLit) mnfResp -    continueLit :: MonadIO m =>-                   FileExt+    continueMap :: MonadIO m =>+                   Maybe B.ByteString+                -> FileExt                 -> Verb                 -> M.Map Verb (Maybe (ReaderT BL.ByteString m z, Maybe BodyLength), FileExtListenerT Response m ())                 -> Maybe Response                 -> m ResponseReceived-    continueLit f v vmap mnfResp =-      let fail = liftIO $ respond404 mnfResp in+    continueMap acceptBS f v vmap mnfResp = do+      let fail = liftIO $ respond404 mnfResp+       maybe fail (\(mreqbodyf, femonad) -> do           femap <- execWriterT $ runFileExtListenerT femonad           maybe fail (\r -> do@@ -286,33 +228,8 @@                               liftIO $ respond r                       else fail                     _ -> fail) $-            lookupMin f $ unFileExts femap) $ M.lookup v vmap---    continuePred :: MonadIO m =>-                    Verb-                 -> M.Map Verb (Maybe (ReaderT BL.ByteString m z, Maybe BodyLength), Response)-                 -> Maybe Response-                 -> m ResponseReceived-    continuePred v vmap mnfResp =-      let fail = liftIO $ respond404 mnfResp in-      maybe fail (\(mreqbodyf, r) ->-          case mreqbodyf of-            Nothing              -> liftIO $ respond r-            Just (reqbf,Nothing) -> do-              body <- liftIO $ strictRequestBody req-              (runReaderT $ reqbf) body-              liftIO $ respond r-            Just (reqbf,Just bl) -> do-              case requestBodyLength req of-                KnownLength bl' ->-                  if bl' <= bl-                  then do body <- liftIO $ strictRequestBody req-                          (runReaderT $ reqbf) body-                          liftIO $ respond r-                  else fail-                _ -> fail-          ) $ M.lookup v vmap+            lookupProper acceptBS f $ unFileExts femap) $+        M.lookup v vmap       respond404 :: Maybe Response -> IO ResponseReceived@@ -321,21 +238,46 @@     plain404 :: Response     plain404 = responseLBS status404 [("Content-Type","text/plain")] "404" -    lookupMin :: Ord k => k -> M.Map k a -> Maybe a-    lookupMin k map | all (k <) (M.keys map) = M.lookup (minimum $ M.keys map) map-                    | otherwise              = M.lookup k map--    lookupProper :: FileExt -> M.Map FileExt a -> Maybe a-    lookupProper k map = case M.lookup k map of-      Nothing -> case M.lookup (feSequence k !! 0) map of-        Nothing -> M.lookup (feSequence k !! 1) map-        Just x  -> Just x-      Just x  -> Just x+    lookupProper :: Maybe B.ByteString -> FileExt -> M.Map FileExt a -> Maybe a+    lookupProper maccept k map =+      let+        attempts = maybe+                     [Html,Text,Json,JavaScript,Css]+                     (\accept -> possibleFileExts k accept)+                     maccept+      in+      foldr (go map) Nothing attempts       where-        feSequence Html = [Text, Json]-        feSequence Json = [Text, Html]-        feSequence Text = [Json, Html]+        go map x Nothing = M.lookup x map+        go _ _  (Just y) = Just y +    possibleFileExts :: FileExt -> B.ByteString -> [FileExt]+    possibleFileExts fe accept =+      let computed = sortFE fe $ nub $ concat $+            catMaybes [ mapAccept [ ("application/json" :: B.ByteString, [Json])+                                  , ("application/javascript" :: B.ByteString, [Json,JavaScript])+                                  ] accept+                      , mapAccept [ ("text/html" :: B.ByteString, [Html])+                                  ] accept+                      , mapAccept [ ("text/plain" :: B.ByteString, [Text])+                                  ] accept+                      , mapAccept [ ("text/css" :: B.ByteString, [Css])+                                  ] accept+                      ]++          wildcard = concat $+            catMaybes [ mapAccept [ ("*/*" :: B.ByteString, [Html,Text,Json,JavaScript,Css])+                                  ] accept+                      ]+      in+      if length wildcard /= 0 then wildcard else computed++    sortFE Html       xs = [Html, Text]             `intersect` xs+    sortFE JavaScript xs = [JavaScript, Text]       `intersect` xs+    sortFE Json       xs = [Json, JavaScript, Text] `intersect` xs+    sortFE Css        xs = [Css, Text]              `intersect` xs+    sortFE Text       xs = [Text]                   `intersect` xs+     applyToLast :: (a -> a) -> [a] -> [a]     applyToLast _ [] = []     applyToLast f (x:[]) = f x : []@@ -346,7 +288,9 @@                     then T.pack $ takeWhile (/= '.') $ T.unpack s                     else s       where-        possibleExts = [".html",".htm",".txt",".json"]+        possibleExts = [ ".html",".htm",".txt",".json",".lucid"+                       , ".julius",".css",".cassius",".lucius"+                       ]         endsWithAny s xs = (dropWhile (/= '.') s) `elem` xs      httpMethodToMSym :: Method -> Maybe Verb
src/Web/Routes/Nested/FileExtListener.hs view
@@ -2,7 +2,7 @@   ( module X   ) where -import Web.Routes.Nested.FileExtListener.Types as X+import Web.Routes.Nested.FileExtListener.Types as X hiding (FileExt) import Web.Routes.Nested.FileExtListener.Builder as X import Web.Routes.Nested.FileExtListener.ByteString as X import Web.Routes.Nested.FileExtListener.Blaze as X
src/Web/Routes/Nested/FileExtListener/Blaze.hs view
@@ -4,6 +4,7 @@ module Web.Routes.Nested.FileExtListener.Blaze where  import           Web.Routes.Nested.FileExtListener.Types+import           Web.Routes.Nested.FileExtListener.ByteString  import           Data.Map import qualified Data.Text.Lazy.Encoding                 as LT@@ -16,51 +17,35 @@ import           Control.Monad.Writer  -+-- | Uses @Html@ as the key in the map, and @"text/html"@ as the content type. 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--blazeHeaders :: Monad m => RequestHeaders -> H.Html -> FileExtListenerT Response m ()-blazeHeaders hs i =-  let r = responseLBS status200 hs $-            LT.encodeUtf8 $ H.renderHtml i in-  FileExtListenerT $ tell $-    FileExts $ singleton Html r+blaze = blazeStatusHeaders status200 [("Content-Type", "text/html")]  blazeStatus :: Monad m => Status -> H.Html -> FileExtListenerT Response m ()-blazeStatus s i =-  let r = responseLBS s [("Content-Type", "text/html")] $-            LT.encodeUtf8 $ H.renderHtml i in-  FileExtListenerT $ tell $-    FileExts $ singleton Html r+blazeStatus s = blazeStatusHeaders s [("Content-Type", "text/html")] +blazeHeaders :: Monad m => RequestHeaders -> H.Html -> FileExtListenerT Response m ()+blazeHeaders = blazeStatusHeaders status200+ blazeStatusHeaders :: Monad m => Status -> RequestHeaders -> H.Html -> FileExtListenerT Response m () blazeStatusHeaders s hs i =-  let r = responseLBS s hs $-            LT.encodeUtf8 $ H.renderHtml i in+  let r = blazeOnlyStatusHeaders s hs i in   FileExtListenerT $ tell $     FileExts $ singleton Html r    blazeOnly :: H.Html -> Response-blazeOnly i =-  responseLBS status200 [("Content-Type", "text/html")] $ LT.encodeUtf8 $ H.renderHtml i+blazeOnly = blazeOnlyStatusHeaders status200 [("Content-Type", "text/html")]  blazeOnlyHeaders :: RequestHeaders -> H.Html -> Response-blazeOnlyHeaders hs i =-  responseLBS status200 hs $ LT.encodeUtf8 $ H.renderHtml i+blazeOnlyHeaders = blazeOnlyStatusHeaders status200  blazeOnlyStatus :: Status -> H.Html -> Response-blazeOnlyStatus s i =-  responseLBS s [("Content-Type", "text/html")] $ LT.encodeUtf8 $ H.renderHtml i+blazeOnlyStatus s = blazeOnlyStatusHeaders s [("Content-Type", "text/html")]  blazeOnlyStatusHeaders :: Status -> RequestHeaders -> H.Html -> Response blazeOnlyStatusHeaders s hs i =-  responseLBS s hs $ LT.encodeUtf8 $ H.renderHtml i+  bytestringOnlyStatus s hs $ LT.encodeUtf8 $ H.renderHtml i  
src/Web/Routes/Nested/FileExtListener/Builder.hs view
@@ -17,21 +17,18 @@ -- | A builder is ambiguous, therefore we require @RequestHeaders@ and a @FileExt@ to be explicitly -- supplied. builder :: Monad m => FileExt -> RequestHeaders -> BU.Builder -> FileExtListenerT Response m ()-builder e hs i =-  let r = responseBuilder status200 hs i in-  FileExtListenerT $ tell $-    FileExts $ singleton e r+builder e  = builderStatus e status200  builderStatus :: Monad m => FileExt -> Status -> RequestHeaders -> BU.Builder -> FileExtListenerT Response m () builderStatus e s hs i =-  let r = responseBuilder s hs i in+  let r = builderOnlyStatus s hs i in   FileExtListenerT $ tell $     FileExts $ singleton e r    builderOnly :: RequestHeaders -> BU.Builder -> Response-builderOnly = responseBuilder status200+builderOnly = builderOnlyStatus status200  -- | The exact same thing as @Network.Wai.responseBuilder@. builderOnlyStatus :: Status -> RequestHeaders -> BU.Builder -> Response
src/Web/Routes/Nested/FileExtListener/ByteString.hs view
@@ -10,28 +10,25 @@ import           Network.HTTP.Types                      (RequestHeaders,                                                           Status, status200) import           Network.Wai+import qualified Network.Wai.Util                        as U  import           Control.Monad.Writer   -- | @ByteString@ is ambiguous - we need to know what @RequestHeaders@ and @FileExt@ should be associated. bytestring :: Monad m => FileExt -> RequestHeaders -> B.ByteString -> FileExtListenerT Response m ()-bytestring e hs i =-  let r = responseLBS status200 hs i in-  FileExtListenerT $ tell $-    FileExts $ singleton e r+bytestring e = bytestringStatus e status200  bytestringStatus :: Monad m => FileExt -> Status -> RequestHeaders -> B.ByteString -> FileExtListenerT Response m ()-bytestringStatus e s hs i =-  let r = responseLBS s hs i in+bytestringStatus e s hs i = do+  r <- lift $ U.bytestring s hs i   FileExtListenerT $ tell $     FileExts $ singleton e r   bytestringOnly :: RequestHeaders -> B.ByteString -> Response-bytestringOnly = responseLBS status200+bytestringOnly = bytestringOnlyStatus status200  -- | The exact same thing as @Network.Wai.responseLBS@. bytestringOnlyStatus :: Status -> RequestHeaders -> B.ByteString -> Response bytestringOnlyStatus = responseLBS-
+ src/Web/Routes/Nested/FileExtListener/Cassius.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE OverloadedStrings #-}++++module Web.Routes.Nested.FileExtListener.Cassius where++import           Web.Routes.Nested.FileExtListener.Types as FE+import           Web.Routes.Nested.FileExtListener.ByteString++import           Data.Map+import           Text.Cassius+import qualified Data.Text.Lazy                          as LT+import qualified Data.Text.Lazy.Encoding                 as LT+import           Network.HTTP.Types                      (RequestHeaders,+                                                          Status, status200)+import           Network.Wai++import           Control.Monad.Writer+++-- | Uses @cassius@ as the key in the map, and @"cassius/plain"@ as the content type.+cassius :: Monad m => Css -> FileExtListenerT Response m ()+cassius = cassiusStatusHeaders status200 [("Content-Type", "cassius/css")]++cassiusStatus :: Monad m => Status -> Css -> FileExtListenerT Response m ()+cassiusStatus s = cassiusStatusHeaders s [("Content-Type", "cassius/css")]++cassiusHeaders :: Monad m => RequestHeaders -> Css -> FileExtListenerT Response m ()+cassiusHeaders = cassiusStatusHeaders status200++cassiusStatusHeaders :: Monad m => Status -> RequestHeaders -> Css -> FileExtListenerT Response m ()+cassiusStatusHeaders s hs i =+  let r = cassiusOnlyStatusHeaders s hs i in+  FileExtListenerT $ tell $+    FileExts $ singleton Css r+++++cassiusOnly :: Css -> Response+cassiusOnly = cassiusOnlyStatusHeaders status200 [("Content-Type", "cassius/css")]++cassiusOnlyStatus :: Status -> Css -> Response+cassiusOnlyStatus s = cassiusOnlyStatusHeaders s [("Content-Type", "cassius/css")]++cassiusOnlyHeaders :: RequestHeaders -> Css -> Response+cassiusOnlyHeaders = cassiusOnlyStatusHeaders status200++cassiusOnlyStatusHeaders :: Status -> RequestHeaders -> Css -> Response+cassiusOnlyStatusHeaders s hs i = bytestringOnlyStatus s hs $ LT.encodeUtf8 $ renderCss i+++++
+ src/Web/Routes/Nested/FileExtListener/Clay.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE OverloadedStrings #-}++++module Web.Routes.Nested.FileExtListener.Clay where++import           Web.Routes.Nested.FileExtListener.Types+import           Web.Routes.Nested.FileExtListener.ByteString++import           Data.Map+import           Clay.Render+import           Clay.Stylesheet+import qualified Data.Text.Lazy                          as LT+import qualified Data.Text.Lazy.Encoding                 as LT+import           Network.HTTP.Types                      (RequestHeaders,+                                                          Status, status200)+import           Network.Wai++import           Control.Monad.Writer+++-- | Uses @Text@ as the key in the map, and @"text/css"@ as the content type.+clay :: Monad m => Config -> [App] -> Css -> FileExtListenerT Response m ()+clay c as = clayStatusHeaders c as status200 [("Content-Type", "text/css")]++clayStatus :: Monad m =>Config -> [App] ->  Status -> Css -> FileExtListenerT Response m ()+clayStatus c as s = clayStatusHeaders c as s [("Content-Type", "text/css")]++clayHeaders :: Monad m => Config -> [App] -> RequestHeaders -> Css -> FileExtListenerT Response m ()+clayHeaders c as = clayStatusHeaders c as status200++clayStatusHeaders :: Monad m => Config -> [App] -> Status -> RequestHeaders -> Css -> FileExtListenerT Response m ()+clayStatusHeaders c as s hs i =+  let r = clayOnlyStatusHeaders c as s hs i in+  FileExtListenerT $ tell $+    FileExts $ singleton Css r+++++clayOnly :: Config -> [App] -> Css -> Response+clayOnly c as = clayOnlyStatusHeaders c as status200 [("Content-Type", "text/css")]++clayOnlyStatus :: Config -> [App] -> Status -> Css -> Response+clayOnlyStatus c as s = clayOnlyStatusHeaders c as s [("Content-Type", "text/css")]++clayOnlyHeaders :: Config -> [App] -> RequestHeaders -> Css -> Response+clayOnlyHeaders c as = clayOnlyStatusHeaders c as status200++clayOnlyStatusHeaders :: Config -> [App] -> Status -> RequestHeaders -> Css -> Response+clayOnlyStatusHeaders c as s hs i = bytestringOnlyStatus s hs $ LT.encodeUtf8 $ renderWith c as i+++++
src/Web/Routes/Nested/FileExtListener/Json.hs view
@@ -4,6 +4,7 @@ module Web.Routes.Nested.FileExtListener.Json where  import           Web.Routes.Nested.FileExtListener.Types+import           Web.Routes.Nested.FileExtListener.ByteString  import qualified Data.Aeson                              as A import           Data.Map@@ -15,58 +16,38 @@   -+-- | Uses @Json@ as the key in the map, and @"application/json"@ as the content type. json :: ( A.ToJSON j         , Monad m ) =>         j -> FileExtListenerT Response m ()-json i =-  let r = responseLBS status200 [("Content-Type", "application/json")] $-            A.encode i in-  FileExtListenerT $ tell $-    FileExts $ singleton Json r+json = jsonStatusHeaders status200 [("Content-Type", "application/json")]  jsonStatus :: ( A.ToJSON j         , Monad m ) =>         Status -> j -> FileExtListenerT Response m ()-jsonStatus s i =-  let r = responseLBS s [("Content-Type", "application/json")] $-            A.encode i in-  FileExtListenerT $ tell $-    FileExts $ singleton Json r+jsonStatus s = jsonStatusHeaders s [("Content-Type", "application/json")] +-- | Uses @Json@ as the key in the map, and @"application/javascript"@ as the content type. jsonp :: ( A.ToJSON j         , Monad m ) =>         j -> FileExtListenerT Response m ()-jsonp i =-  let r = responseLBS status200 [("Content-Type", "application/javascript")] $-            A.encode i in-  FileExtListenerT $ tell $-    FileExts $ singleton Json r+jsonp = jsonStatusHeaders status200 [("Content-Type", "application/javascript")]  jsonpStatus :: ( A.ToJSON j         , Monad m ) =>         Status -> j -> FileExtListenerT Response m ()-jsonpStatus s i =-  let r = responseLBS s [("Content-Type", "application/javascript")] $-            A.encode i in-  FileExtListenerT $ tell $-    FileExts $ singleton Json r+jsonpStatus s = jsonStatusHeaders s [("Content-Type", "application/javascript")]  jsonHeaders :: ( A.ToJSON j         , Monad m ) =>         RequestHeaders -> j -> FileExtListenerT Response m ()-jsonHeaders hs i =-  let r = responseLBS status200 hs $-            A.encode i in-  FileExtListenerT $ tell $-    FileExts $ singleton Json r+jsonHeaders = jsonStatusHeaders status200  jsonStatusHeaders :: ( A.ToJSON j         , Monad m ) =>         Status -> RequestHeaders -> j -> FileExtListenerT Response m () jsonStatusHeaders s hs i =-  let r = responseLBS s hs $-            A.encode i in+  let r = jsonOnlyStatusHeaders s hs i in   FileExtListenerT $ tell $     FileExts $ singleton Json r @@ -75,31 +56,26 @@  jsonOnly :: A.ToJSON j =>             j -> Response-jsonOnly i =-  responseLBS status200 [("Content-Type", "application/json")] $ A.encode i+jsonOnly = jsonOnlyStatusHeaders status200 [("Content-Type", "application/json")]  jsonOnlyStatus :: A.ToJSON j =>             Status -> j -> Response-jsonOnlyStatus s i =-  responseLBS s [("Content-Type", "application/json")] $ A.encode i+jsonOnlyStatus s = jsonOnlyStatusHeaders s [("Content-Type", "application/json")]  jsonpOnly :: A.ToJSON j =>             j -> Response-jsonpOnly i =-  responseLBS status200 [("Content-Type", "application/javascript")] $ A.encode i+jsonpOnly = jsonOnlyStatusHeaders status200 [("Content-Type", "application/javascript")]  jsonpOnlyStatus :: A.ToJSON j =>             Status -> j -> Response-jsonpOnlyStatus s i =-  responseLBS s [("Content-Type", "application/javascript")] $ A.encode i+jsonpOnlyStatus s = jsonOnlyStatusHeaders s [("Content-Type", "application/javascript")]  jsonOnlyHeaders :: A.ToJSON j =>             RequestHeaders -> j -> Response-jsonOnlyHeaders hs i =-  responseLBS status200 hs $ A.encode i+jsonOnlyHeaders = jsonOnlyStatusHeaders status200  jsonOnlyStatusHeaders :: A.ToJSON j =>             Status -> RequestHeaders -> j -> Response jsonOnlyStatusHeaders s hs i =-  responseLBS s hs $ A.encode i+  bytestringOnlyStatus s hs $ A.encode i 
+ src/Web/Routes/Nested/FileExtListener/Julius.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE OverloadedStrings #-}++++module Web.Routes.Nested.FileExtListener.Julius where++import           Web.Routes.Nested.FileExtListener.Types+import           Web.Routes.Nested.FileExtListener.ByteString++import           Data.Map+import           Text.Julius+import qualified Data.Text.Lazy                          as LT+import qualified Data.Text.Lazy.Encoding                 as LT+import           Network.HTTP.Types                      (RequestHeaders,+                                                          Status, status200)+import           Network.Wai++import           Control.Monad.Writer+++-- | Uses @julius@ as the key in the map, and @"application/javascript"@ as the content type.+julius :: Monad m => Javascript -> FileExtListenerT Response m ()+julius = juliusStatusHeaders status200 [("Content-Type", "application/javascript")]++juliusStatus :: Monad m => Status -> Javascript -> FileExtListenerT Response m ()+juliusStatus s = juliusStatusHeaders s [("Content-Type", "application/javascript")]++juliusHeaders :: Monad m => RequestHeaders -> Javascript -> FileExtListenerT Response m ()+juliusHeaders = juliusStatusHeaders status200++juliusStatusHeaders :: Monad m => Status -> RequestHeaders -> Javascript -> FileExtListenerT Response m ()+juliusStatusHeaders s hs i =+  let r = juliusOnlyStatusHeaders s hs i in+  FileExtListenerT $ tell $+    FileExts $ singleton JavaScript r+++++juliusOnly :: Javascript -> Response+juliusOnly = juliusOnlyStatusHeaders status200 [("Content-Type", "application/javascript")]++juliusOnlyStatus :: Status -> Javascript -> Response+juliusOnlyStatus s = juliusOnlyStatusHeaders s [("Content-Type", "application/javascript")]++juliusOnlyHeaders :: RequestHeaders -> Javascript -> Response+juliusOnlyHeaders = juliusOnlyStatusHeaders status200++juliusOnlyStatusHeaders :: Status -> RequestHeaders -> Javascript -> Response+juliusOnlyStatusHeaders s hs i = bytestringOnlyStatus s hs $ LT.encodeUtf8 $ renderJavascript i+++++
src/Web/Routes/Nested/FileExtListener/Lucid.hs view
@@ -4,6 +4,7 @@ module Web.Routes.Nested.FileExtListener.Lucid where  import           Web.Routes.Nested.FileExtListener.Types+import           Web.Routes.Nested.FileExtListener.ByteString  import           Data.Map import qualified Lucid.Base                              as L@@ -14,36 +15,23 @@ import           Control.Monad.Writer  -+-- | Uses the @Html@ key in the map, and @"text/html"@ as the content type. 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+lucid = lucidStatusHeaders status200 [("Content-Type", "text/html")]  lucidStatus :: Monad m =>          Status -> L.HtmlT m () -> FileExtListenerT Response m ()-lucidStatus s i = do-  i' <- lift $ L.renderBST i-  let r = responseLBS s [("Content-Type", "text/html")] i'-  FileExtListenerT $ tell $-    FileExts $ singleton Html r+lucidStatus s = lucidStatusHeaders s [("Content-Type", "text/html")]  lucidHeaders :: Monad m =>          RequestHeaders -> L.HtmlT m () -> FileExtListenerT Response m ()-lucidHeaders hs i = do-  i' <- lift $ L.renderBST i-  let r = responseLBS status200 hs i'-  FileExtListenerT $ tell $-    FileExts $ singleton Html r+lucidHeaders = lucidStatusHeaders status200  lucidStatusHeaders :: Monad m =>          Status -> RequestHeaders -> L.HtmlT m () -> FileExtListenerT Response m () lucidStatusHeaders s hs i = do-  i' <- lift $ L.renderBST i-  let r = responseLBS s hs i'+  r <- lift $ lucidOnlyStatusHeaders s hs i   FileExtListenerT $ tell $     FileExts $ singleton Html r @@ -52,31 +40,16 @@  lucidOnly :: Monad m =>              L.HtmlT m () -> m Response-lucidOnly i = do-  i' <- L.renderBST i-  return $ responseLBS status200 [("Content-Type", "text/html")] i'+lucidOnly = lucidOnlyStatusHeaders status200 [("Content-Type", "text/html")]  lucidOnlyStatus :: Monad m =>              Status -> L.HtmlT m () -> m Response-lucidOnlyStatus s i = do-  i' <- L.renderBST i-  return $ responseLBS s [("Content-Type", "text/html")] i'+lucidOnlyStatus s = lucidOnlyStatusHeaders s [("Content-Type", "text/html")]  lucidOnlyHeaders :: Monad m =>              RequestHeaders -> L.HtmlT m () -> m Response-lucidOnlyHeaders hs i = do-  i' <- L.renderBST i-  return $ responseLBS status200 hs i'+lucidOnlyHeaders = lucidOnlyStatusHeaders status200  lucidOnlyStatusHeaders :: Monad m =>              Status -> RequestHeaders -> L.HtmlT m () -> m Response-lucidOnlyStatusHeaders s hs i = do-  i' <- L.renderBST i-  return $ responseLBS s hs i'-------+lucidOnlyStatusHeaders s hs i = liftM (bytestringOnlyStatus s hs) $ L.renderBST i
+ src/Web/Routes/Nested/FileExtListener/Lucius.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE OverloadedStrings #-}++++module Web.Routes.Nested.FileExtListener.Lucius where++import           Web.Routes.Nested.FileExtListener.Types as FE+import           Web.Routes.Nested.FileExtListener.ByteString++import           Data.Map+import           Text.Lucius+import qualified Data.Text.Lazy                          as LT+import qualified Data.Text.Lazy.Encoding                 as LT+import           Network.HTTP.Types                      (RequestHeaders,+                                                          Status, status200)+import           Network.Wai++import           Control.Monad.Writer+++-- | Uses @lucius@ as the key in the map, and @"lucius/css"@ as the content type.+lucius :: Monad m => Css -> FileExtListenerT Response m ()+lucius = luciusStatusHeaders status200 [("Content-Type", "lucius/css")]++luciusStatus :: Monad m => Status -> Css -> FileExtListenerT Response m ()+luciusStatus s = luciusStatusHeaders s [("Content-Type", "lucius/css")]++luciusHeaders :: Monad m => RequestHeaders -> Css -> FileExtListenerT Response m ()+luciusHeaders = luciusStatusHeaders status200++luciusStatusHeaders :: Monad m => Status -> RequestHeaders -> Css -> FileExtListenerT Response m ()+luciusStatusHeaders s hs i =+  let r = luciusOnlyStatusHeaders s hs i in+  FileExtListenerT $ tell $+    FileExts $ singleton FE.Css r+++++luciusOnly :: Css -> Response+luciusOnly = luciusOnlyStatusHeaders status200 [("Content-Type", "lucius/css")]++luciusOnlyStatus :: Status -> Css -> Response+luciusOnlyStatus s = luciusOnlyStatusHeaders s [("Content-Type", "lucius/css")]++luciusOnlyHeaders :: RequestHeaders -> Css -> Response+luciusOnlyHeaders = luciusOnlyStatusHeaders status200++luciusOnlyStatusHeaders :: Status -> RequestHeaders -> Css -> Response+luciusOnlyStatusHeaders s hs i = bytestringOnlyStatus s hs $ LT.encodeUtf8 $ renderCss i+++++
src/Web/Routes/Nested/FileExtListener/Text.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE OverloadedStrings #-}  + module Web.Routes.Nested.FileExtListener.Text where  import           Web.Routes.Nested.FileExtListener.Types+import           Web.Routes.Nested.FileExtListener.ByteString  import           Data.Map import qualified Data.Text.Lazy                          as LT@@ -12,35 +14,23 @@                                                           Status, status200) import           Network.Wai +import           Data.Composition import           Control.Monad.Writer  -+-- | Uses @Text@ as the key in the map, and @"text/plain"@ as the content type. text :: Monad m => LT.Text -> FileExtListenerT Response m ()-text i =-  let r = responseLBS status200 [("Content-Type", "text/plain")] $-            LT.encodeUtf8 i in-  FileExtListenerT $ tell $-    FileExts $ singleton Text r+text = textStatusHeaders status200 [("Content-Type", "text/plain")]  textStatus :: Monad m => Status -> LT.Text -> FileExtListenerT Response m ()-textStatus s i =-  let r = responseLBS s [("Content-Type", "text/plain")] $-            LT.encodeUtf8 i in-  FileExtListenerT $ tell $-    FileExts $ singleton Text r+textStatus s = textStatusHeaders s [("Content-Type", "text/plain")]  textHeaders :: Monad m => RequestHeaders -> LT.Text -> FileExtListenerT Response m ()-textHeaders hs i =-  let r = responseLBS status200 hs $-            LT.encodeUtf8 i in-  FileExtListenerT $ tell $-    FileExts $ singleton Text r+textHeaders = textStatusHeaders status200  textStatusHeaders :: Monad m => Status -> RequestHeaders -> LT.Text -> FileExtListenerT Response m () textStatusHeaders s hs i =-  let r = responseLBS s hs $-            LT.encodeUtf8 i in+  let r = textOnlyStatusHeaders s hs i in   FileExtListenerT $ tell $     FileExts $ singleton Text r @@ -48,22 +38,13 @@   textOnly :: LT.Text -> Response-textOnly i =-  responseLBS status200 [("Content-Type", "text/plain")] $ LT.encodeUtf8 i+textOnly = textOnlyStatusHeaders status200 [("Content-Type", "text/plain")]  textOnlyStatus :: Status -> LT.Text -> Response-textOnlyStatus s i =-  responseLBS s [("Content-Type", "text/plain")] $ LT.encodeUtf8 i+textOnlyStatus s = textOnlyStatusHeaders s [("Content-Type", "text/plain")]  textOnlyHeaders :: RequestHeaders -> LT.Text -> Response-textOnlyHeaders hs i =-  responseLBS status200 hs $ LT.encodeUtf8 i+textOnlyHeaders = textOnlyStatusHeaders status200  textOnlyStatusHeaders :: Status -> RequestHeaders -> LT.Text -> Response-textOnlyStatusHeaders s hs i =-  responseLBS s hs $ LT.encodeUtf8 i-----+textOnlyStatusHeaders s hs i = bytestringOnlyStatus s hs $ LT.encodeUtf8 i
src/Web/Routes/Nested/FileExtListener/Types.hs view
@@ -19,20 +19,26 @@   data FileExt = Html+             | Css+             | JavaScript              | Json              | Text   deriving (Show, Eq, Ord)   toExt :: T.Text -> Maybe FileExt-toExt x | x `elem` htmls = Just Html-        | x `elem` jsons = Just Json-        | x `elem` texts = Just Text-        | otherwise      = Nothing+toExt x | x `elem` htmls       = Just Html+        | x `elem` csss        = Just Css+        | x `elem` javascripts = Just JavaScript+        | x `elem` jsons       = Just Json+        | x `elem` texts       = Just Text+        | otherwise            = Nothing   where-    htmls = ["", ".htm", ".html"]-    jsons = [".json"]-    texts = [".txt"]+    htmls       = [".htm", ".html"]+    csss        = [".css"]+    javascripts = [".js", ".javascript"]+    jsons       = [".json"]+    texts       = [".txt"]  newtype FileExts a = FileExts { unFileExts :: Map FileExt a }   deriving (Show, Eq, Functor, Traversable)
src/Web/Routes/Nested/Types.hs view
@@ -7,7 +7,6 @@   , RankNTypes   , FlexibleInstances   , UndecidableInstances-  , OverlappingInstances   , MultiParamTypeClasses   , FunctionalDependencies   , ConstraintKinds
src/Web/Routes/Nested/Types/UrlChunks.hs view
@@ -10,6 +10,7 @@  import Data.Attoparsec.Text import Text.Regex+import Data.String (IsString (..)) import qualified Data.Text as T  @@ -21,6 +22,9 @@  l :: T.Text -> EitherUrlChunk 'Nothing l = (:=)++instance x ~ 'Nothing => IsString (EitherUrlChunk x) where+  fromString = l . T.pack  p :: (T.Text, Parser r) -> EitherUrlChunk ('Just r) p = (:~)