Skip to content

Commit ff4ee78

Browse files
committed
Merge pull request #22 from jasonzoladz/master
Added Guide to library
2 parents 50963f0 + 530e456 commit ff4ee78

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

GUIDE.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# A Guide to `purescript-routing`
2+
3+
A couple notes upfront:
4+
5+
* This library facilitates hash-based routing. If you're looking to do pushstate routing with the [history](https://developer.mozilla.org/en-US/docs/Web/API/History_API) object, then you are in the wrong place.
6+
* Routes are declared using [applicative](https://pursuit.purescript.org/packages/purescript-prelude/0.1.4/docs/Prelude#t:Applicative) syntax. If you're not yet comfortable with applicatives, see this [chapter](http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors) or this [paper](http://www.staff.city.ac.uk/~ross/papers/Applicative.pdf).
7+
8+
####Usage
9+
10+
First, define some locations:
11+
12+
```purescript
13+
14+
module SolarSystemRoutes where
15+
16+
import Prelude
17+
import Control.Alt ((<|>))
18+
import Control.Apply
19+
import Data.Functor
20+
import Data.Map
21+
import Routing
22+
import Routing.Match
23+
import Routing.Match.Class
24+
import Routing.Hash
25+
import Data.Int (floor)
26+
27+
data Locations
28+
= Home
29+
| Jupiter -- The place where Boys go to get 'stupider'.
30+
| Mars -- Girls seek fame here.
31+
| MPC Int -- Minor Planet Circular (poor Pluto, et al.)
32+
| Moon String -- They're not all made of cheese.
33+
| PlanetWithAttributes (Map String String) -- Looking for a new home?
34+
35+
```
36+
We'll use `Locations` instead of `Routes` to avoid confusion with the `Route` (singular) type exposed by the library. But let's discuss the types later. For now, we'll see it in action:
37+
38+
```purescript
39+
40+
homeSlash :: Match Unit
41+
homeSlash = lit ""
42+
43+
int :: Match Int
44+
int = floor <$> num
45+
46+
home :: Match Locations
47+
home = Home <$ oneSlash
48+
49+
jupiter :: Match Locations
50+
jupiter = Jupiter <$ (homeSlash *> lit "jupiter")
51+
52+
mars :: Match Locations
53+
mars = Mars <$ (homeSlash *> lit "mars" *> lit "hollywood")
54+
55+
mpc :: Match Locations
56+
mpc = MPC <$> (homeSlash *> lit "mpc" *> int)
57+
58+
moon :: Match Locations
59+
moon = Moon <$> (homeSlash *> lit "moon" *> str)
60+
61+
planetWithAttributes :: Match Locations
62+
planetWithAttributes = PlanetWithAttributes <$> (homeSlash *> lit "planet" *> params)
63+
64+
```
65+
66+
So what's all this? `purescript-routing` provides a `Match` type that is an instance of the `MatchClass`. The `MatchClass` encompasses types that provide the route-matching primitives:
67+
* `lit`: A function that takes a string, and returns a `Match` that can be used to match the provided string.
68+
* `str`: A `Match` that captures a string.
69+
* `num`: A `Match` that captures a javascript number.
70+
* `param`: A function that takes a parameter key (i.e., `String`), and returns a `Match` that will pluck the value associated with the key from the query parameter block.
71+
* `params`: A `Match` that captures the query parameters.
72+
* `bool`: A `Match` for `true` or `false`.
73+
74+
75+
(Note that `lit ""` -- aliased above as `homeSlash` -- matches a single url forward-slash.)
76+
77+
`Match` is functor. So we can map over it. Above, for example, we map the `floor` function over `num :: Match Number` and we get `int :: Match Int`. We map our `Locations` constructors over other `Match`s to yield a `Match Locations`.
78+
79+
`Match` is a newtype that looks like this:
80+
81+
```purescript
82+
newtype Match a = Match (Route -> V (Free MatchError) (Tuple Route a))
83+
```
84+
85+
Don't worry too much about the scary type for now. Essentially, `Match` wraps a function that takes a `Route` (represented as a `List RoutePart`, where `RouteParts` is either the stuff between url slashes or the query parameters) and returns a `V` functor. `V` stands for 'validation' and you can think of it as an `Either` that can return more than one error upon failure. So, in essence, something of type `Match Locations` is a function from `Route -> Either Errors (Tuple (Rest-of-RoutePart-List) Locations)`.
86+
87+
The real magic happens in `Match`'s `Apply (i.e., <*>)` implementation.
88+
89+
The `Apply` instance for `Match` provides the plumbing that allows you to compose your routes. The `<*>`, `*>` and `<*` operators are your friends. Like any other applicative, `(leftMatch *> rightMatch)` performs the left `Match`, discards a successful result, and returns the value of the right `Match`. `(leftMatch <* rightMatch)` does the converse. So, `MPC <$> (homeSlash *> lit "mpc" *> int)` maps the `MPC` constructor function over a `Match` that will parse a slash (and discard it), parse the literal string "mpc" (discarding it too), and return the parsed integer.
90+
91+
Routes are combined using `Match`'s `Alt (<|>)` instance.
92+
93+
```purescript
94+
routing :: Match Locations
95+
routing =
96+
jupiter <|>
97+
mars <|>
98+
mpc <|>
99+
moon <|>
100+
home
101+
```
102+
`jupiter (<|>) mars` means "try Jupiter, and if that fails, try Mars". Like many routing DSLs, this means that more general routes come after more specific ones. Thus, `home` (i.e., "/") comes last (unless you only ever want to go home).
103+
104+
We've got routes. Now what do we do with them?
105+
106+
`purescript-routing` provides a couple functions that help you make use of your `Match`s.
107+
108+
```purescript
109+
matches :: forall e a. Match a -> (Maybe a -> a -> Eff e Unit) -> Eff e Unit
110+
```
111+
112+
and
113+
114+
```purescript
115+
matchesAff :: forall e a. Match a -> Aff e (Tuple (Maybe a) a)
116+
```
117+
118+
`matches` and `matchesAff` provide access to the stream of hash changes. You provide a function with your `routing` (e.g., your composed `Match Locations`), and `matches` (or `matchesAff`) maps it over the stream.
119+
120+
In our example, we'll run in `Eff`:
121+
122+
```purescript
123+
main = do
124+
matches routing (\old new -> someAction old new)
125+
--- other stuff ---
126+
where
127+
someAction :: forall e. Maybe Locations -> Locations -> Eff e Unit
128+
someAction = ...
129+
```
130+
The previous route (`old`) is a `Maybe Locations` and the `new` is of type `Locations`. The `someAction` you take is up to you, but you'll probably want to do something with the `new` value.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ bower install purescript-routing
1414

1515
# Module documentation
1616

17-
Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-routing).
17+
- The [guide](GUIDE.md) provides an overview of library's usage and implementation.
1818

19+
- Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-routing).

0 commit comments

Comments
 (0)