Compare commits

...

6 Commits

Author SHA1 Message Date
Greg Weber
28ea173472 Merge remote-tracking branch 'origin/master' into static-pages 2012-02-10 08:54:35 -08:00
Greg Weber
2feecf317f fixup slashes 2012-02-10 08:13:12 -08:00
Greg Weber
4a2bff1c78 Merge remote-tracking branch 'origin/master' into static-pages 2012-02-09 20:49:51 -08:00
Greg Weber
277ae5585a don't bother with TH for static page routes 2012-02-09 17:38:25 -08:00
Greg Weber
708b731dd1 Merge remote-tracking branch 'origin/master' into static-pages 2012-02-09 15:15:53 -08:00
Greg Weber
df23b8f876 support static page route parsing 2012-02-05 19:46:07 -08:00
3 changed files with 67 additions and 18 deletions

View File

@ -7,15 +7,17 @@ module Yesod.Routes.Parse
, parseRoutesNoCheck , parseRoutesNoCheck
, parseRoutesFileNoCheck , parseRoutesFileNoCheck
, parseType , parseType
, parseRoutePaths
) where ) where
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
import Data.Maybe import Data.Maybe
import Data.Char (isUpper) import Data.Char (isUpper, isSpace)
import Language.Haskell.TH.Quote import Language.Haskell.TH.Quote
import qualified System.IO as SIO import qualified System.IO as SIO
import Yesod.Routes.TH import Yesod.Routes.TH
import Yesod.Routes.Overlap (findOverlapNames) import Yesod.Routes.Overlap (findOverlapNames)
import System.FilePath.Posix ((</>))
-- | A quasi-quoter to parse a string into a list of 'Resource's. Checks for -- | A quasi-quoter to parse a string into a list of 'Resource's. Checks for
-- overlapping routes, failing if present; use 'parseRoutesNoCheck' to skip the -- overlapping routes, failing if present; use 'parseRoutesNoCheck' to skip the
@ -49,24 +51,61 @@ readUtf8File fp = do
-- | Same as 'parseRoutes', but performs no overlap checking. -- | Same as 'parseRoutes', but performs no overlap checking.
parseRoutesNoCheck :: QuasiQuoter parseRoutesNoCheck :: QuasiQuoter
parseRoutesNoCheck = QuasiQuoter parseRoutesNoCheck = QuasiQuoter
{ quoteExp = lift . resourcesFromString { quoteExp = lift . resourcesFromString }
}
-- | Convert a multi-line string to a set of route paths.
-- like normal route parsing, but there are just route paths, no route constructors or HTTP methods
-- This can be used as a DSL for generating route paths that
-- * closely matches your current routes file
-- * allows indentation to imply prefixes
--
-- This is a partial function which calls 'error' on invalid input.
parseRoutePaths :: String -> [String]
parseRoutePaths = parseRoutesFromString staticPageRoute
where
staticPageRoute :: String -> [String] -> Maybe String
staticPageRoute r [] = Just $ stripEndSlash r
staticPageRoute r rest = error $ "line starting with: " ++ r ++ "\ndid not expect: " ++ show rest
stripEndSlash r = if last r == '/' then init r else r
-- | Convert a multi-line string to a set of resources. See documentation for -- | Convert a multi-line string to a set of resources. See documentation for
-- the format of this string. This is a partial function which calls 'error' on -- the format of this string. This is a partial function which calls 'error' on
-- invalid input. -- invalid input.
resourcesFromString :: String -> [Resource String] resourcesFromString :: String -> [Resource String]
resourcesFromString = resourcesFromString =
mapMaybe go . lines parseRoutesFromString resourceFromLine
resourceFromLine :: String -> [String] -> Maybe (Resource String)
resourceFromLine fullRoute (constr:rest) =
let (pieces, mmulti) = piecesFromString $ fullRoute
disp = dispatchFromString rest mmulti
in Just $ Resource constr pieces disp
resourceFromLine _ [] = Nothing -- an indenter: there should be indented routes afterwards
-- | used by 'resourcesFromString' and 'staticPageRoutesFromString'
parseRoutesFromString :: (String -- ^ route pattern
-> [String] -- ^ extra
-> Maybe a)
-> String -- ^ unparsed routes
-> [a]
parseRoutesFromString mkRoute =
catMaybes . (parseLines $ error "first route cannot be indented") . lines
where where
go s = indents :: String -> Int
case takeWhile (/= "--") $ words s of indents = length . takeWhile isSpace
(pattern:constr:rest) ->
let (pieces, mmulti) = piecesFromString $ drop1Slash pattern parseLines noIndent (l:ls) =
disp = dispatchFromString rest mmulti case takeWhile (/= "--") $ words l of
in Just $ Resource constr pieces disp (route:rest) ->
[] -> Nothing let (newNoIndent, fullRoute) =
_ -> error $ "Invalid resource line: " ++ s if indents l == 0
-- important: the check is done lazily
then (route, route)
else (noIndent, noIndent </> route)
in mkRoute (dropPreSlash fullRoute) rest : parseLines newNoIndent ls
[] -> parseLines noIndent ls
parseLines _ [] = []
dispatchFromString :: [String] -> Maybe String -> Dispatch String dispatchFromString :: [String] -> Maybe String -> Dispatch String
dispatchFromString rest mmulti dispatchFromString rest mmulti
@ -78,9 +117,9 @@ dispatchFromString [_, _] Just{} =
error "Subsites cannot have a multipiece" error "Subsites cannot have a multipiece"
dispatchFromString rest _ = error $ "Invalid list of methods: " ++ show rest dispatchFromString rest _ = error $ "Invalid list of methods: " ++ show rest
drop1Slash :: String -> String dropPreSlash :: String -> String
drop1Slash ('/':x) = x dropPreSlash ('/':x) = x
drop1Slash x = x dropPreSlash x = x
piecesFromString :: String -> ([(CheckOverlap, Piece String)], Maybe String) piecesFromString :: String -> ([(CheckOverlap, Piece String)], Maybe String)
piecesFromString "" = ([], Nothing) piecesFromString "" = ([], Nothing)

View File

@ -10,16 +10,17 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
import Test.Hspec.Monadic import Test.Hspec.Monadic
import Test.Hspec.HUnit () import Test.Hspec.HUnit ()
import Test.HUnit ((@?=)) import Test.HUnit ((@?=), (@=?))
import Data.Text (Text, pack, unpack, singleton) import Data.Text (Text, pack, unpack, singleton)
import Yesod.Routes.Dispatch hiding (Static, Dynamic) import Yesod.Routes.Dispatch hiding (Static, Dynamic)
import Yesod.Routes.Class hiding (Route) import Yesod.Routes.Class hiding (Route)
import qualified Yesod.Routes.Class as YRC import qualified Yesod.Routes.Class as YRC
import qualified Yesod.Routes.Dispatch as D import qualified Yesod.Routes.Dispatch as D
import Yesod.Routes.Parse (parseRoutesNoCheck) import Yesod.Routes.Parse (parseRoutesNoCheck, parseRoutePaths)
import Yesod.Routes.Overlap (findOverlapNames) import Yesod.Routes.Overlap (findOverlapNames)
import Yesod.Routes.TH hiding (Dispatch) import Yesod.Routes.TH hiding (Dispatch)
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
import Data.List (intercalate)
class ToText a where class ToText a where
toText :: a -> Text toText :: a -> Text
@ -224,7 +225,15 @@ thDispatchAlias master sub toMaster app404 handler405 method0 pieces0 =
-} -}
main :: IO () main :: IO ()
main = hspecX $ do main = hspecX $ do
describe "parseRoutePaths" $
it "lists static page routes" $
["pages","pages/about","pages/data","pages/faq"] @=? parseRoutePaths (intercalate "\n" [
"/pages/"
," about"
," data"
," faq"
])
describe "justRoot" $ do describe "justRoot" $ do
it "dispatches correctly" $ test justRoot [] @?= Just 1 it "dispatches correctly" $ test justRoot [] @?= Just 1
it "fails correctly" $ test justRoot ["foo"] @?= Nothing it "fails correctly" $ test justRoot ["foo"] @?= Nothing

View File

@ -19,6 +19,7 @@ library
, containers >= 0.2 && < 0.5 , containers >= 0.2 && < 0.5
, template-haskell , template-haskell
, path-pieces >= 0.1 && < 0.2 , path-pieces >= 0.1 && < 0.2
, filepath >= 1
exposed-modules: Yesod.Routes.Dispatch exposed-modules: Yesod.Routes.Dispatch
Yesod.Routes.TH Yesod.Routes.TH