{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -- | Use your Rowdy route definitions with Yesod web applications. module Rowdy.Yesod ( module Rowdy.Yesod , (//) , (/:) , Endpoint(..) , PathPiece(..) , Type(..) , Verb(..) ) where import Data.Foldable (traverse_) import Data.Typeable (Proxy (..), Typeable) import Yesod.Routes.TH.Types import Rowdy import Rowdy.Yesod.Internal -- | Convert a 'RouteDsl' into a representation that Yesod can use. -- -- @ -- mkYesod "App" $ toYesod $ do -- get "RootR" -- "users" // do -- resource "UserIndex" [get, post] -- -- etc... -- @ -- -- GHC freaks out if you try to use a type defined in the same module as the -- route. Ensure that all types you use in the route are defined in an imported -- module. -- -- @since 0.0.1.0 toYesod :: Dsl () -> [ResourceTree String] toYesod = routeTreeToResourceTree . runRouteDsl -- | We specialize the 'RouteDsl' type to this as a shorthand. type Dsl = RouteDsl String PathPiece Endpoint -- | We support the most common HTTP verbs. Each of 'get', 'put', 'post', and -- 'delete' are given a @String@ that represents the resource they are acting -- for. The generated route type uses that resource as a constructor. The -- generated dispatcher expects to see functions with the lowercase verb as -- a prefix to the resource name. As an example: -- -- @ -- get "HelloR" -- @ -- -- Will create a route type @HelloR@ and expect a handler @getHelloR@ to be -- defined. -- -- @since 0.0.1.0 get, put, post, delete :: String -> Dsl () get = doVerb Get put = doVerb Put post = doVerb Post delete = doVerb Delete -- | Create an endpoint with the given named resource and verb. -- -- @since 0.0.1.0 doVerb :: Verb -> String -> Dsl () doVerb v s = terminal (MkResource v s) -- | Create a subsite with the given @name@, @type@, and accessor function name -- to get the underlying application. -- -- @since 0.0.1.0 subsite :: String -> String -> String -> Dsl () subsite name thing func = terminal (MkSubsite name thing func) -- | Capture a dynamic path piece and parse it as the provided type. This -- function is intended to be used with the @TypeApplications@ language -- extension. -- -- @ -- "users" // do -- resource "UserIndexR" [get, post] -- capture @UserId $ do -- resource "UserR" [get, put, delete] -- "posts" // do -- resource "PostR" [get, post] -- @ -- -- @since 0.0.1.0 capture :: forall typ. Typeable typ => PathPiece capture = captureP (Proxy @typ) -- | A version of 'capture' that accepts an explicit 'Proxy' argument. Use this -- if you don't like the @TypeApplications@ syntax, or have a proxy on hand -- already. -- -- @since 0.0.1.0 captureP :: forall typ. Typeable typ => Proxy typ -> PathPiece captureP = Capture . Type -- | Define a number of handlers for the named resource. The following code -- block: -- -- @ -- do 'get' "HelloR" -- 'put' "HelloR" -- 'post' "HelloR" -- 'delete' "HelloR" -- @ -- -- is equivalent to this shorter form: -- -- @ -- 'resource' "HelloR" ['get', 'put', 'post', 'delete'] -- @ -- -- @since 0.0.1.0 resource :: String -> [String -> Dsl ()] -> Dsl () resource = traverse_ . flip id -- | Attach a route attribute to every element in the given DSL. -- -- @since 0.0.1.0 attr :: String -> Dsl () -> Dsl () attr = pathComponent . Attr -- | An infix operator alias for 'attr'. -- -- @ -- "admin" // "admin" /! do -- 'resource' "AdminR" ['get', 'put', 'post'] -- @ -- -- @since 0.0.1.0 (/!) :: String -> Dsl () -> Dsl () (/!) = attr infixr 8 /! -- | Provide an inline attribute to the given route. -- -- @ -- get "HelloR" ! "friendly" -- @ (!) :: Dsl () -> String -> Dsl () (!) = flip attr infixl 8 !