Skip to content

Commit f16fe42

Browse files
committed
dos2unix
1 parent 7364647 commit f16fe42

File tree

1 file changed

+190
-190
lines changed

1 file changed

+190
-190
lines changed

extra/ROUTER.md

Lines changed: 190 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,190 @@
1-
Included is a router (in the orbit of Single-Page Applications) that is written entirely in Scala.
2-
3-
Features
4-
========
5-
* Type-safety.
6-
* Links to routes are guaranteed to be valid.
7-
* Routes for different pages or routing rule sets cannot be used in the wrong context.
8-
* Rules
9-
* Routes to views.
10-
* Redirection routes.
11-
* Dynamic routes. (eg. `/person/123`)
12-
* URL re-writing / translation rules. (eg. can remove trailing slashes from URL.)
13-
* Choose to redirect or render custom view when route is invalid / not found.
14-
* Route views can be intercepted and modified. (eg. to add page headers, footers, a nav breadcrumb.)
15-
* URL and view are always kept in sync.
16-
* Routes are bookmarkable.
17-
* Uses HTML5 History API.
18-
* Routing logic is deterministic and unit-testable.
19-
20-
21-
Caution
22-
=======
23-
24-
* If you want routes starting with slashes, you will need to configure your server appropriately.
25-
There's no point having `www.blah.com/foo` have routes like `/bar` if when the server receives a request for `www.blah.com/foo/bar` it doesn't know to use the same endpoint as `www.blah.com/foo`.
26-
If you don't have that control, begin with a `#` instead, like `#foo`.
27-
* If you use Internet Explorer v0.1 ~ v9, the HTML5 API won't be available. But that's ok, there's no need to code like our homo heidelbergensis ancestors, just download and use a polyfill.
28-
29-
Tutorial
30-
========
31-
32-
The friendliest way to create a router is to use the `RoutingRules` DSL.
33-
Create an object that extends `RoutingRules` and provide the only mandatory method, `notFound`.
34-
35-
```scala
36-
object MyPage extends RoutingRules {
37-
override val notFound = render( <.h1("404!!") )
38-
}
39-
```
40-
41-
#### Root view
42-
43-
Now lets wire up a view for the root route.
44-
Assuming you have a React component called `RootComponent` somewhere, add this to your `RoutingRules` object:
45-
46-
```scala
47-
val root = register(rootLocation(RootComponent))
48-
```
49-
50-
#### Redirect 404 to root view
51-
52-
Instead of showing a 404 when an invalid route is accessed, lets redirect to the root view.
53-
All that's needed is
54-
```scala
55-
override val notFound = redirect(root, Redirect.Replace)
56-
```
57-
58-
`Redirect.Replace` means that the window URL is replaced with the new URL without the old URL going into history.
59-
Use `Redirect.Push` to store the old URL in browser history when the redirect occurs.
60-
61-
Also, be careful that you don't refer to a `val` that hasn't been initialised yet (ie. a forward reference).
62-
Order your rules appropriately or use lazy vals.
63-
64-
So now we have:
65-
```scala
66-
object MyPage extends RoutingRules {
67-
val root = register(rootLocation(RootComponent))
68-
override val notFound = redirect(root, Redirect.Replace)
69-
}
70-
```
71-
72-
#### Adding static routes
73-
74-
Routes can either render a view, or redirect. Here are examples of both.
75-
76-
```scala
77-
// Wire a route #hello to a view
78-
val hello = register(location("#hello", HelloComponent))
79-
80-
// Redirect #hey to #hello
81-
register(redirection("#hey", hello, Redirect.Replace))
82-
```
83-
84-
#### Links
85-
86-
To create safe links to routes, you'll need access to a `Router` in your component's render function.
87-
`Router` has a method `link` that creates links to valid routes.
88-
89-
The `render()` method in your `RoutingRules` accepts:
90-
* plain old `ReactElement`
91-
* `Router => ReactElement`
92-
* Components with the `Router` as their props type.
93-
94-
Putting this altogether we can have:
95-
```scala
96-
object MyPage extends RoutingRules {
97-
val root = register(rootLocation(RootComponent))
98-
val hello = register(location("#hello", <.h1("Hello!") ))
99-
override val notFound = redirect(root, Redirect.Replace)
100-
}
101-
102-
val RootComponent = ReactComponentB[MyPage.Router]("Root")
103-
.render(router =>
104-
<.div(
105-
<.h2("Router Demonstration"),
106-
<.div(router.link(MyPage.root) ("The 'root' route")),
107-
<.div(router.link(MyPage.hello)("The 'hello' route")))
108-
).build
109-
```
110-
111-
Note that the `Router` type is prefixed as `ReactComponentB[MyPage.Router]` and not `ReactComponentB[Router]`.
112-
Routers are not interchangable between routing rule sets.
113-
114-
#### Renmdering your Router
115-
116-
Before a `Router` can be created it needs to the base URL, which is the prefix portion of the URL that is the same for all your pages routes.
117-
118-
It needn't be absolute at compile-time, but it needs to be absolute at runtime. `BaseUrl.fromWindowOrigin` will give you the protocol, domain and port at runtime, after which you should append a path if necessary. Example: `BaseUrl.fromWindowOrigin / "my_page"`
119-
120-
Once you have a your `BaseUrl`, call `<RoutingRules>.router(baseUrl)` to get a standard React component of your router.
121-
122-
Note: You can enable console logging by providing a `Router.consoleLogger` to `router()`.
123-
124-
Example:
125-
```scala
126-
val baseUrl = BaseUrl.fromWindowOrigin / "my_page"
127-
val component = MyPage.router(baseUrl, Router.consoleLogger)
128-
```
129-
130-
#### Dynamic Routes
131-
132-
Use `register( parser {...} ... )` to register dynamic routes.
133-
134-
`parser` takes a partial function that attempts to match a portion of a URL and capture some part of it.
135-
If successful, you can create a dynamic response using the captured part.
136-
137-
Unlike static routes, if you want to create a link to a dynamic page you need to specify an additional function that generates the dynamic route path. Use `.dynLink` in your routing rules for this purpose.
138-
139-
Examples:
140-
```scala
141-
object MyPage extends RoutingRules {
142-
...
143-
144-
// This example matches /name/<anything>
145-
146-
private val namePathMatch = "^/name/(.+)$".r
147-
register(parser { case namePathMatch(n) => n }.location(n => NameComponent(n)))
148-
val name = dynLink[String](n => s"/name/$n")
149-
150-
// This example matches /person/<number>
151-
// and redirects on /person/<not-a-number>
152-
153-
private val personPathMatch = "^/person/(.+)$".r
154-
register(parser { case personPathMatch(p) => p }.thenMatch {
155-
case matchNumber(idStr) => render(PersonComponent(PersonId(idStr.toLong)))
156-
case _ /* non-numeric id */ => redirect(root, Redirect.Push)
157-
})
158-
val person = dynLink[PersonId](id => s"/person/${id.value}")
159-
}
160-
```
161-
162-
Note that we don't store the results of `register` for dynamic routes. This is why `dynLink` is necessary.
163-
164-
#### View interception
165-
166-
To customise all views rendered by a routing rule set, override the `interceptRender` method and follow the types.
167-
168-
This example adds a back button to all pages except the root:
169-
```scala
170-
override protected def interceptRender(i: InterceptionR): ReactElement =
171-
if (i.loc == root)
172-
i.element
173-
else
174-
<.div(
175-
<.div(i.router.link(root)("Back", ^.cls := "back")),
176-
i.element)
177-
```
178-
179-
### URL rewriting
180-
181-
`RoutingRules` comes with a built-in (although inactive) URL rewriting rule called `removeTrailingSlashes`.
182-
It can be installed via `register()` and is a good example of how to create dynamic matching rules.
183-
184-
Its implementation is simple:
185-
```scala
186-
def removeTrailingSlashes: DynamicRoute = {
187-
val regex = "^(.*?)/+$".r
188-
parser { case regex(p) => p }.redirection(p => (Path(p), Redirect.Replace))
189-
}
190-
```
1+
Included is a router (in the orbit of Single-Page Applications) that is written entirely in Scala.
2+
3+
Features
4+
========
5+
* Type-safety.
6+
* Links to routes are guaranteed to be valid.
7+
* Routes for different pages or routing rule sets cannot be used in the wrong context.
8+
* Rules
9+
* Routes to views.
10+
* Redirection routes.
11+
* Dynamic routes. (eg. `/person/123`)
12+
* URL re-writing / translation rules. (eg. can remove trailing slashes from URL.)
13+
* Choose to redirect or render custom view when route is invalid / not found.
14+
* Route views can be intercepted and modified. (eg. to add page headers, footers, a nav breadcrumb.)
15+
* URL and view are always kept in sync.
16+
* Routes are bookmarkable.
17+
* Uses HTML5 History API.
18+
* Routing logic is deterministic and unit-testable.
19+
20+
21+
Caution
22+
=======
23+
24+
* If you want routes starting with slashes, you will need to configure your server appropriately.
25+
There's no point having `www.blah.com/foo` have routes like `/bar` if when the server receives a request for `www.blah.com/foo/bar` it doesn't know to use the same endpoint as `www.blah.com/foo`.
26+
If you don't have that control, begin with a `#` instead, like `#foo`.
27+
* If you use Internet Explorer v0.1 ~ v9, the HTML5 API won't be available. But that's ok, there's no need to code like our homo heidelbergensis ancestors, just download and use a polyfill.
28+
29+
Tutorial
30+
========
31+
32+
The friendliest way to create a router is to use the `RoutingRules` DSL.
33+
Create an object that extends `RoutingRules` and provide the only mandatory method, `notFound`.
34+
35+
```scala
36+
object MyPage extends RoutingRules {
37+
override val notFound = render( <.h1("404!!") )
38+
}
39+
```
40+
41+
#### Root view
42+
43+
Now lets wire up a view for the root route.
44+
Assuming you have a React component called `RootComponent` somewhere, add this to your `RoutingRules` object:
45+
46+
```scala
47+
val root = register(rootLocation(RootComponent))
48+
```
49+
50+
#### Redirect 404 to root view
51+
52+
Instead of showing a 404 when an invalid route is accessed, lets redirect to the root view.
53+
All that's needed is
54+
```scala
55+
override val notFound = redirect(root, Redirect.Replace)
56+
```
57+
58+
`Redirect.Replace` means that the window URL is replaced with the new URL without the old URL going into history.
59+
Use `Redirect.Push` to store the old URL in browser history when the redirect occurs.
60+
61+
Also, be careful that you don't refer to a `val` that hasn't been initialised yet (ie. a forward reference).
62+
Order your rules appropriately or use lazy vals.
63+
64+
So now we have:
65+
```scala
66+
object MyPage extends RoutingRules {
67+
val root = register(rootLocation(RootComponent))
68+
override val notFound = redirect(root, Redirect.Replace)
69+
}
70+
```
71+
72+
#### Adding static routes
73+
74+
Routes can either render a view, or redirect. Here are examples of both.
75+
76+
```scala
77+
// Wire a route #hello to a view
78+
val hello = register(location("#hello", HelloComponent))
79+
80+
// Redirect #hey to #hello
81+
register(redirection("#hey", hello, Redirect.Replace))
82+
```
83+
84+
#### Links
85+
86+
To create safe links to routes, you'll need access to a `Router` in your component's render function.
87+
`Router` has a method `link` that creates links to valid routes.
88+
89+
The `render()` method in your `RoutingRules` accepts:
90+
* plain old `ReactElement`
91+
* `Router => ReactElement`
92+
* Components with the `Router` as their props type.
93+
94+
Putting this altogether we can have:
95+
```scala
96+
object MyPage extends RoutingRules {
97+
val root = register(rootLocation(RootComponent))
98+
val hello = register(location("#hello", <.h1("Hello!") ))
99+
override val notFound = redirect(root, Redirect.Replace)
100+
}
101+
102+
val RootComponent = ReactComponentB[MyPage.Router]("Root")
103+
.render(router =>
104+
<.div(
105+
<.h2("Router Demonstration"),
106+
<.div(router.link(MyPage.root) ("The 'root' route")),
107+
<.div(router.link(MyPage.hello)("The 'hello' route")))
108+
).build
109+
```
110+
111+
Note that the `Router` type is prefixed as `ReactComponentB[MyPage.Router]` and not `ReactComponentB[Router]`.
112+
Routers are not interchangable between routing rule sets.
113+
114+
#### Renmdering your Router
115+
116+
Before a `Router` can be created it needs to the base URL, which is the prefix portion of the URL that is the same for all your pages routes.
117+
118+
It needn't be absolute at compile-time, but it needs to be absolute at runtime. `BaseUrl.fromWindowOrigin` will give you the protocol, domain and port at runtime, after which you should append a path if necessary. Example: `BaseUrl.fromWindowOrigin / "my_page"`
119+
120+
Once you have a your `BaseUrl`, call `<RoutingRules>.router(baseUrl)` to get a standard React component of your router.
121+
122+
Note: You can enable console logging by providing a `Router.consoleLogger` to `router()`.
123+
124+
Example:
125+
```scala
126+
val baseUrl = BaseUrl.fromWindowOrigin / "my_page"
127+
val component = MyPage.router(baseUrl, Router.consoleLogger)
128+
```
129+
130+
#### Dynamic Routes
131+
132+
Use `register( parser {...} ... )` to register dynamic routes.
133+
134+
`parser` takes a partial function that attempts to match a portion of a URL and capture some part of it.
135+
If successful, you can create a dynamic response using the captured part.
136+
137+
Unlike static routes, if you want to create a link to a dynamic page you need to specify an additional function that generates the dynamic route path. Use `.dynLink` in your routing rules for this purpose.
138+
139+
Examples:
140+
```scala
141+
object MyPage extends RoutingRules {
142+
...
143+
144+
// This example matches /name/<anything>
145+
146+
private val namePathMatch = "^/name/(.+)$".r
147+
register(parser { case namePathMatch(n) => n }.location(n => NameComponent(n)))
148+
val name = dynLink[String](n => s"/name/$n")
149+
150+
// This example matches /person/<number>
151+
// and redirects on /person/<not-a-number>
152+
153+
private val personPathMatch = "^/person/(.+)$".r
154+
register(parser { case personPathMatch(p) => p }.thenMatch {
155+
case matchNumber(idStr) => render(PersonComponent(PersonId(idStr.toLong)))
156+
case _ /* non-numeric id */ => redirect(root, Redirect.Push)
157+
})
158+
val person = dynLink[PersonId](id => s"/person/${id.value}")
159+
}
160+
```
161+
162+
Note that we don't store the results of `register` for dynamic routes. This is why `dynLink` is necessary.
163+
164+
#### View interception
165+
166+
To customise all views rendered by a routing rule set, override the `interceptRender` method and follow the types.
167+
168+
This example adds a back button to all pages except the root:
169+
```scala
170+
override protected def interceptRender(i: InterceptionR): ReactElement =
171+
if (i.loc == root)
172+
i.element
173+
else
174+
<.div(
175+
<.div(i.router.link(root)("Back", ^.cls := "back")),
176+
i.element)
177+
```
178+
179+
### URL rewriting
180+
181+
`RoutingRules` comes with a built-in (although inactive) URL rewriting rule called `removeTrailingSlashes`.
182+
It can be installed via `register()` and is a good example of how to create dynamic matching rules.
183+
184+
Its implementation is simple:
185+
```scala
186+
def removeTrailingSlashes: DynamicRoute = {
187+
val regex = "^(.*?)/+$".r
188+
parser { case regex(p) => p }.redirection(p => (Path(p), Redirect.Replace))
189+
}
190+
```

0 commit comments

Comments
 (0)