The routing mechanism has been designed so that you can write code like the following:
routes :: [View]
routes = [
empty //-> indexView $ decs
, "posts/" <+/> empty //-> postsView $ []
, intParam //-> viewWithIntParam $ []
, stringParam //-> viewWithStringParam $ []
, intParam </+> "test/" //-> viewWithIntParam $ []
, "test/" <+/> intParam //-> viewWithIntParam $ []
, anyParam </> anyParam //-> viewWithIntAndStringParam $ []
, intParam </> stringParam </> intParam //-> viewWithIntStringInt $ []
]
where:
postsView, indexView :: Request -> IO (Maybe Response) (i.e. View)
viewWithStringParam :: String -> Request -> IO (Maybe Response)
viewWithIntParam :: Int -> Request -> IO (Maybe Response)
viewWithIntAndStringParam :: Int -> String -> Request -> IO (Maybe Response)
viewWithIntStringInt :: Int -> String -> Int -> Request -> IO (Maybe Response)
decs :: [View -> View]
These would correspond to URLs like the following:
/
/posts/
/123/ 123 captured
/abc7/ "abc7" captured
/123/test/ 123 captured
/test/123/ 123 captured
/123/abc7/ 123 and "abc7" captured
/123/abc7/456/ 123, "abc7" and 456 captured
The right hand argument of //-> is a 'view like' function, of type
View OR a -> View OR a -> b -> View etc,
The left hand argument of //-> is a 'matcher' - it parses the
path of the Request, optionally capturing parameters and returning
a function that will adapt the right hand argument so that it has
type View.
Matchers can be composed using </>. To match a fixed string
without capturing, use fixedString thestring. The operators
</+> amd <+/> are useful for combining fixed strings with other
matchers. To match just a fixed string, you can use
"thestring/" <+/> empty
instead of:
fixedString "thestring/"
The result of the //-> operator needs to be applied to a list of
'view decorator' functions, (which may be an empty list)
e.g. 'decs' above. These decorators take a View and return a
View, or alternatively they can be considered to take a View and a
Request and return an IO (Maybe Response). These means they can be
used to do pre-processing of the request, and post-processing of
the response.
The routing mechanism is extensible in the types of parameters that
can be captured. The easiest way is to define instances of
Param, and then use anyParam. For more complex needs, for
example if you do not want the component to end in a forward slash,
just define your own matchers.
When defining routes as above, choosing anyParam instead of
intParam or stringParam will produce exactly the same result.
With anyParam, the type will be determined by type inference.
With stringParam etc., you are repeating the type information,
which is a DRY violation, but it may be useful for clarity, and you
will get a compilation error in the case of any mismatch.
NB. The Request object trims any leading slash on the path to normalise
it, and also to simplify this parsing stage, so do not attempt to match
an initial leading slash.
|