Rewrite docs ()

This commit is contained in:
Chris Wong 2020-11-02 12:53:27 +13:00 committed by GitHub
parent 39a866d946
commit 3af1397541
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 254 additions and 200 deletions

View file

@ -1,4 +1,4 @@
slugs := index getting-started basic-syntax dynamic-content partials control-structures traits web-frameworks faq
slugs := index getting-started text-escaping elements-attributes splices-toggles control-structures partials render-trait web-frameworks faq
slug_to_md = content/$(1).md
slug_to_html = site/$(1).html

View file

@ -1,163 +0,0 @@
# Basic syntax
The next few sections will outline the syntax used by Maud templates.
## Literals `""`
Literal strings use the same syntax as Rust. Wrap them in double quotes, and use a backslash for escapes.
```rust
html! {
"Oatmeal, are you crazy?"
}
```
### Escaping and `PreEscaped`
By default, HTML special characters are escaped automatically. Wrap the string in `(PreEscaped())` to disable this escaping. (See the section on [dynamic content] to learn more about how this works.)
```rust
use maud::PreEscaped;
html! {
"<script>alert(\"XSS\")</script>" // &lt;script&gt;...
(PreEscaped("<script>alert(\"XSS\")</script>")) // <script>...
}
```
[dynamic content]: dynamic-content.md
If the string is long, or contains many special characters, then it may be worth using [raw strings] instead:
```rust
use maud::PreEscaped;
html! {
(PreEscaped(r#"
<script>
alert("Look ma, no backslashes!");
</script>
"#))
}
```
[raw strings]: https://doc.rust-lang.org/reference/tokens.html#raw-string-literals
If you want to add a `<!DOCTYPE html>` declaration to your page, you may use the `maud::DOCTYPE` constant instead of writing it out by hand:
```rust
use maud::DOCTYPE;
html! {
(DOCTYPE) // <!DOCTYPE html>
}
```
## Elements `p`
Write an element using curly braces: `p { ... }`.
Terminate a void element using a semicolon: `br;`. Note that the result will be rendered with HTML syntax `<br>` not `<br />`.
```rust
html! {
h1 { "Poem" }
p {
"Rock, you are a rock."
br;
"Gray, you are gray,"
br;
"Like a rock, which you are."
br;
"Rock."
}
}
```
Maud also supports ending a void element with a slash: `br /`. This syntax is [deprecated][#96] and should not be used in new code.
[#96]: https://github.com/lambda-fairy/maud/pull/96
Before version 0.18, Maud allowed the curly braces to be omitted. This syntax was [removed][#137] and now causes an error instead.
[#137]: https://github.com/lambda-fairy/maud/pull/137
## Non-standard HTML ("custom") elements
Maud is not restricted to writing well-known HTML elements like `h1`, `p`, `span`, etc. If you want to use [custom elements][custom-elements], you can.
```rust
html! {
blog-post {
title { "My blog" }
}
}
```
[custom-elements]: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
## Non-empty attributes `id="yay"`
```rust
html! {
ul {
li {
a href="about:blank" { "Apple Bloom" }
}
li class="lower-middle" {
"Sweetie Belle"
}
li dir="rtl" {
"Scootaloo "
small { "(also a chicken)" }
}
}
}
```
Add attributes using the syntax: `attr="value"`. You can attach any number of attributes to an element. The values must be quoted: they are parsed as string literals.
## Empty attributes `checked?` `disabled?[foo]`
Declare an empty attribute using a `?` suffix: `checked?`.
```rust
html! {
form {
input type="checkbox" name="cupcakes" checked?;
" "
label for="cupcakes" { "Do you like cupcakes?" }
}
}
```
To toggle an attribute based on a boolean flag, use a `?[]` suffix instead: `checked?[foo]`. This will check the value of `foo` at runtime, inserting the attribute only if `foo` equals `true`.
```rust
let allow_editing = true;
html! {
p contenteditable?[allow_editing] {
"Edit me, I "
em { "dare" }
" you."
}
}
```
## Classes and IDs `.foo` `#bar`
Add classes and IDs to an element using `.foo` and `#bar` syntax. The tag will default to `div` if an element begins with a class or ID. You can chain multiple classes and IDs together, and mix and match them with other attributes:
```rust
html! {
.container#main {
input.big.scary.bright-red type="button" value="Launch Party Cannon";
}
}
```
To toggle a class based on a boolean flag, use a `[]` suffix: `.foo[is_foo]`. This will check the value of `is_foo` at runtime, inserting that class value `foo` in the class attribute only if `is_foo` is `true`.
```rust
let cuteness = 95;
html! {
p.cute[cuteness > 50] { "Squee!" }
}
```

View file

@ -28,7 +28,7 @@ html! {
}
```
`@if let` is supported as well. It works as you'd expect:
`@if let` is supported as well:
```rust
let user = Some("Pinkie Pie");

View file

@ -0,0 +1,101 @@
# Elements and attributes
## Elements: `p`
Write an element using curly braces: `p { ... }`.
Terminate a void element using a semicolon: `br;`. Note that the result will be rendered with HTML syntax `<br>` not `<br />`.
```rust
html! {
h1 { "Poem" }
p {
"Rock, you are a rock."
br;
"Gray, you are gray,"
br;
"Like a rock, which you are."
br;
"Rock."
}
}
```
Maud also supports ending a void element with a slash: `br /`. This syntax is [deprecated][#96] and should not be used in new code.
[#96]: https://github.com/lambda-fairy/maud/pull/96
Before version 0.18, Maud allowed the curly braces to be omitted. This syntax was [removed][#137] and now causes an error instead.
[#137]: https://github.com/lambda-fairy/maud/pull/137
## Custom elements
Maud also supports [custom elements].
```rust
html! {
blog-post {
title { "My blog" }
}
}
```
[custom elements]: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
## Non-empty attributes: `title="yay"`
Add attributes using the syntax: `attr="value"`. You can attach any number of attributes to an element. The values must be quoted: they are parsed as string literals.
```rust
html! {
ul {
li {
a href="about:blank" { "Apple Bloom" }
}
li class="lower-middle" {
"Sweetie Belle"
}
li dir="rtl" {
"Scootaloo "
small { "(also a chicken)" }
}
}
}
```
## Empty attributes: `checked?`
Declare an empty attribute using a `?` suffix: `checked?`.
```rust
html! {
form {
input type="checkbox" name="cupcakes" checked?;
" "
label for="cupcakes" { "Do you like cupcakes?" }
}
}
```
## Classes and IDs: `.foo` `#bar`
Add classes and IDs to an element using `.foo` and `#bar` syntax. You can chain multiple classes and IDs together, and mix and match them with other attributes:
```rust
html! {
input#cannon.big.scary.bright-red type="button" value="Launch Party Cannon";
}
```
## Implicit `div` elements
If the element name is omitted, but there is a class or ID, then it is assumed to be a `div`.
```rust
html! {
#main {
"Main content!"
}
}
```

View file

@ -2,7 +2,9 @@
## What is the origin of the name "Maud"?
Maud is named after a [character](http://mlp.wikia.com/wiki/Maud_Pie) from *My Little Pony: Friendship is Magic*. It does not refer to the [poem](https://en.wikipedia.org/wiki/Maud_and_other_poems) by Alfred Tennyson, though other people have brought that up in the past.
Maud is named after a [character] from *My Little Pony: Friendship is Magic*.
It does not refer to the [poem] by Alfred Tennyson,
though other people have brought that up in the past.
Here are some reasons why I chose this name:
@ -10,28 +12,63 @@ Here are some reasons why I chose this name:
* The library is efficient and austere, like the character;
* Google used to maintain a site called ["HTML5 Rocks"](https://techcrunch.com/2010/06/22/html5rocks-google/), and Maud (the character) is a geologist.
* Google used to maintain a site called ["HTML5 Rocks"],
and Maud (the character) is a geologist.
[character]: http://mlp.wikia.com/wiki/Maud_Pie
[poem]: https://en.wikipedia.org/wiki/Maud_and_other_poems
["HTML5 Rocks"]: https://techcrunch.com/2010/06/22/html5rocks-google/
## Why does `html!` always allocate a `String`? Wouldn't it be more efficient if it wrote to a handle directly?
Good question! In fact, Maud did work this way in the past.
Sadly, that kind of thing didn't work out that well in practice. Having to pass the handle around made templates hard to compose, which is important in any non-trivial project. Furthermore, Iron (and other middleware frameworks) likes to take ownership of the response body, so we'd need to do some closure gymnastics to get everything to work together. To put the nail in the coffin, benchmarks showed that a `String` based solution is actually faster than one which avoids allocations.
But it's hard to support buffer reuse in an ergonomic way.
The approaches I tried
either involved too much boilerplate,
or caused mysterious lifetime issues,
or both.
Moreover, Maud's allocation pattern—with small, short-lived buffers—follow the fast path in modern allocators.
These reasons are why I changed `html!` to return a `String` in version 0.11.
For these reasons, I changed `html!` to return a `String` in version 0.11.
That said,
Rust has changed a lot since then,
and some of those old assumptions
might no longer hold today.
So this decision could be revisited
prior to the 1.0 release.
## Why is Maud written as a procedural macro? Can't it use `macro_rules!` instead?
This is certainly possible, and in fact the [Horrorshow](https://github.com/Stebalien/horrorshow-rs) library works this way.
This is certainly possible, and indeed the [Horrorshow] library works this way.
I use procedural macros because they are more flexible. There are some syntax constructs in Maud that cannot be parsed with `macro_rules!`; better diagnostics are a bonus as well.
I use procedural macros because they are more flexible.
There are some syntax constructs in Maud that are hard to parse with `macro_rules!`;
better diagnostics are a bonus as well.
[Horrorshow]: https://github.com/Stebalien/horrorshow-rs
## Maud has had a lot of releases so far. When will it reach 1.0?
I plan to make a 1.0 release when the library can be used on stable Rust.
I originally planned to cut a 1.0
after implementing stable support.
But now that's happened,
I've realized that there are a couple design questions
that I'd like to resolve
before marking that milestone.
Expect a blog post on this topic Very Soon®.
## Why doesn't Maud implement [context-aware escaping](https://security.googleblog.com/2009/03/reducing-xss-by-way-of-automatic.html)?
## Why doesn't Maud implement [context-aware escaping]?
If a project follows best practices in separating HTML and CSS/JavaScript, then context-aware escaping is unnecessary.
I agree that context-aware escaping is very important,
especially for the kind of small-scale development
that Maud is used for.
But it's a complex feature,
with security implications,
so I want to take the time
to get it right.
Google uses context-aware escaping because it has a large, decades-old code base, much of it written before these best practices were well known. Any project that uses Maud is neither large nor decades-old, and so should not have the need for this feature.
Please follow [#181] for the latest developments!
[context-aware escaping]: https://security.googleblog.com/2009/03/reducing-xss-by-way-of-automatic.html
[#181]: https://github.com/lambda-fairy/maud/issues/181

View file

@ -1,18 +1,5 @@
# Getting started
## Which version of Rust?
While Maud works well
on both stable and [nightly] versions
of Rust,
the error messages are slightly better
on nightly.
For this reason,
it is recommended to develop using nightly Rust
but test and deploy using stable.
[nightly]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
## Add Maud to your project
Once Rust is set up,
@ -33,7 +20,6 @@ maud = "*"
Then save the following to `src/main.rs`:
```rust
extern crate maud;
use maud::html;
fn main() {
@ -56,3 +42,16 @@ Run this program with `cargo run`, and you should get the following:
```
Congrats you've written your first Maud program!
## Which version of Rust?
While Maud works well
on both stable and [nightly] versions
of Rust,
the error messages are slightly better
on nightly.
For this reason,
it is recommended to develop using nightly Rust,
but test and deploy using stable.
[nightly]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html

View file

@ -33,11 +33,8 @@ pub fn page(title: &str, greeting_box: Markup) -> Markup {
html! {
// Add the header markup to the page
(header(title))
body {
h1 { "Hello World" }
h1 { (title) }
(greeting_box)
}
// Add the footer markup to the page
(footer())
}
}

View file

@ -1,6 +1,6 @@
# The `Render` trait
By default, a `(splice)` is rendered using the [`std::fmt::Display`][Display] trait, with any HTML special characters escaped automatically.
By default, a [`(splice)`](splices-toggles.md) is rendered using the [`std::fmt::Display`][Display] trait, with any HTML special characters escaped automatically.
To change this behavior, implement the [`Render`][Render] trait for your type. Then, when a value of this type is used in a template, Maud will call your custom code instead.

View file

@ -1,6 +1,8 @@
# Dynamic content
# Splices and toggles
Use `(foo)` syntax to splice in the value of `foo` at runtime. Any HTML special characters are escaped by default.
## Splices: `(foo)`
Use `(foo)` syntax to insert the value of `foo` at runtime. Any HTML special characters are escaped by default.
```rust
let best_pony = "Pinkie Pie";
@ -27,7 +29,7 @@ html! {
}
```
## Splices in attributes
### Splices in attributes
Splices work in attributes as well.
@ -51,11 +53,11 @@ html! {
}
```
## What can be spliced?
### What can be spliced?
You can splice any value that implements [`std::fmt::Display`][Display]. Most primitive types (such as `str` and `i32`) implement this trait, so they should work out of the box.
To change this behavior for some type, you can implement the [`Render`][Render] trait by hand. The [`PreEscaped`][PreEscaped] wrapper type, which outputs its argument without escaping, works this way. See the [traits](./traits.md) section for details.
To change this behavior for some type, you can implement the [`Render`][Render] trait by hand. The [`PreEscaped`][PreEscaped] wrapper type, which outputs its argument without escaping, works this way. See the [traits](render-trait.md) section for details.
```rust
use maud::PreEscaped;
@ -69,3 +71,29 @@ html! {
[Display]: http://doc.rust-lang.org/std/fmt/trait.Display.html
[Render]: https://docs.rs/maud/*/maud/trait.Render.html
[PreEscaped]: https://docs.rs/maud/*/maud/struct.PreEscaped.html
## Toggles: `[foo]`
Use `[foo]` syntax to show or hide something based on a boolean expression `foo`.
This works on empty attributes:
```rust
let allow_editing = true;
html! {
p contenteditable?[allow_editing] {
"Edit me, I "
em { "dare" }
" you."
}
}
```
And classes:
```rust
let cuteness = 95;
html! {
p.cute[cuteness > 50] { "Squee!" }
}
```

View file

@ -0,0 +1,55 @@
# Text and escaping
## Text
Literal strings use the same syntax as Rust. Wrap them in double quotes, and use a backslash for escapes.
```rust
html! {
"Oatmeal, are you crazy?"
}
```
## Raw strings
If the string is long, or contains many special characters, then it may be worth using [raw strings] instead:
```rust
html! {
pre {
r#"
Rocks, these are my rocks.
Sediments make me sedimental.
Smooth and round,
Asleep in the ground.
Shades of brown
And gray.
"#
}
}
```
[raw strings]: https://doc.rust-lang.org/reference/tokens.html#raw-string-literals
## Escaping and `PreEscaped`
By default, HTML special characters are escaped automatically. Wrap the string in `(PreEscaped())` to disable this escaping. (See the section on [splices](splices-toggles.md) to learn more about how this works.)
```rust
use maud::PreEscaped;
html! {
"<script>alert(\"XSS\")</script>" // &lt;script&gt;...
(PreEscaped("<script>alert(\"XSS\")</script>")) // <script>...
}
```
## The `DOCTYPE` constant
If you want to add a `<!DOCTYPE html>` declaration to your page, you may use the `maud::DOCTYPE` constant instead of writing it out by hand:
```rust
use maud::DOCTYPE;
html! {
(DOCTYPE) // <!DOCTYPE html>
}
```