-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: 📝 wrote tutorial on building first app
Supplemental repository for this also exists now (arctic-hen7/perseus-starter-app).
- Loading branch information
1 parent
0911cc8
commit 19f0458
Showing
5 changed files
with
277 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Setting up the App Itself | ||
|
||
Okay, you're over the hump! Now it's time to put your template into a functional app and get stuff happening in your browser! | ||
|
||
## Defining a Router | ||
|
||
Perseus needs to know where to put your final pages, which can be defined with a router, which is defined through [Sycamore](https://github.com/sycamore-rs/sycamore) (which handles that side of things). | ||
|
||
In `src/lib.rs`, replace everything other than `mod templates;` with the following: | ||
|
||
```rust | ||
use perseus::define_app; | ||
|
||
#[derive(perseus::Route)] | ||
pub enum Route { | ||
#[to("/")] | ||
Index, | ||
#[not_found] | ||
NotFound, | ||
} | ||
``` | ||
|
||
This imports a macro we'll use in a moment to define your app, and then it sets up an `enum` for each of your pages. Notice that the `NotFound` page is special, and note that Perseus will pretty much handle it for you. | ||
|
||
All we've done for this simple app is defined an `Index` variant that will be served at the `/` path, the root of your app. Thus, it will be your landing page! But Perseus still needs you to connect that variant and the template we created in the last section. | ||
|
||
## Error Pages | ||
|
||
But first, let's define some custom error pages for if your users go to a page that doesn't exist. To keep everything clean, we'll do this in a new file. Create `src/error_pages.rs` and put the following inside (making sure to add `mod error_pages;` to the top of `lib.rs`): | ||
|
||
```rust | ||
use perseus::ErrorPages; | ||
use sycamore::template; | ||
|
||
pub fn get_error_pages() -> ErrorPages { | ||
let mut error_pages = ErrorPages::new(Box::new(|_, _, _| { | ||
template! { | ||
p { "Another error occurred." } | ||
} | ||
})); | ||
error_pages.add_page( | ||
404, | ||
Box::new(|_, _, _| { | ||
template! { | ||
p { "Page not found." } | ||
} | ||
}), | ||
); | ||
error_pages.add_page( | ||
400, | ||
Box::new(|_, _, _| { | ||
template! { | ||
p { "Client error occurred..." } | ||
} | ||
}), | ||
); | ||
|
||
error_pages | ||
} | ||
``` | ||
|
||
Here's what this code does: | ||
|
||
1. Import the Perseus [`ErrorPages`](https://docs.rs/perseus/0.1.2/perseus/shell/struct.ErrorPages.html) `struct`, and the Sycamore templating macro for writing pseudo-HTML. | ||
2. Define a single function that will get all your error pages (you'll call this in `lib.rs`). | ||
3. Create a new instance of `ErrorPages` with the required fallback page. Error pages in Perseus are based on HTTP status codes (but you can create your own beyond this system if you need), and there are *a lot* of them, so the fallback page is used for all the status codes that you don't explicitly handle. | ||
4. Add two new error pages, one for 404 (page not found) and another for 400 (generic client error). Note that the functions we provide have to be `Box`ed (so Rust can allocate the memory properly), and they'll also be provided three arguments, which you'll want to use in a production app. They are: the URL that caused the problem, the HTTP status code, and the error message that was the payload of the request. | ||
|
||
You can read more about error pages [here](https://arctic-hen7.github.io/perseus/error_pages.html). | ||
|
||
## Setting up Some HTML | ||
|
||
Perseus is just a web framework, and it needs some good old HTML to cling to, so you'll need to create an `index.html` file in the root of your project (*next* to `src/`). Then put the following inside: | ||
|
||
```html | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Perseus Starter App</title> | ||
<!-- Importing this runs Perseus --> | ||
<script src="/.perseus/bundle.js" defer></script> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
</body> | ||
</html> | ||
``` | ||
|
||
This is all pretty typical, and you shouldn't have to worry about it. If you want to include analytics or some other script in your app, you can do it here, this HTML will be on every page of your app (including errors). | ||
|
||
Note the single `<script>` that imports from `/.perseus/bundle.js`. That's the stuff that Rollup generates, and the CLI will serve it automatically. We also `defer` it for best practice. | ||
|
||
## Defining Your App | ||
|
||
And now for the magic. Perseus does a ton of stuff to initialize your app, all of which can be abstracted through the `define_app!` macro that we imported earlier. Add the following to `lib.rs` (underneath your `Route` definition) to use it: | ||
|
||
```rust,no_run,no_playground | ||
define_app! { | ||
root: "#root", | ||
route: Route, | ||
router: { | ||
Route::Index => [ | ||
"index".to_string(), | ||
templates::index::template_fn() | ||
] | ||
}, | ||
error_pages: crate::error_pages::get_error_pages(), | ||
templates: [ | ||
crate::templates::index::get_template::<G>() | ||
] | ||
} | ||
``` | ||
|
||
*Note: these properties currently **must** be in order, otherwise they won't work.* | ||
|
||
And again, here's an explanation: | ||
|
||
1. Define the CSS selector at which Perseus will render. This will be the `<div id="root"></div>` we defined in `index.html` in this case. | ||
2. Tell Perseus about the `Route` `enum` you defined earlier. | ||
3. Handle each of your roots with `match`-like syntax. You'll need to handle every variant of your `Route` `enum` here except the `NotFound` variant, which will use your 404 error page (or the fallback if you didn't define one for that status code). Each route that goes to the path at which it will be rendered (which may seem pointless, but it's *really* useful for more complex pages) and the `template_fn` helper function we defined in the last section. | ||
4. Tell Perseus about your error pages. | ||
5. Declare each of your templates, using that `get_template` helper function we defined in the last section. Notice the ambient `G` parameter here, which that function also took. That lets Perseus control whether it renders your page for the server (as for server-side rendering) or for the client (in the browser), and it needs to do both. | ||
|
||
## Ship It. | ||
|
||
It's time to run your app for the first time! If you're using an editor like VS Code with a Rust plugin, then you shouldn't have any compile-time errors in your code (if you do, that's a problem...). | ||
|
||
We'll use the Perseus CLI to take care of everything from here. Go into your app's root folder (with `src/` and `Cargo.toml`) in a terminal, and run this command: | ||
|
||
``` | ||
perseus serve | ||
``` | ||
|
||
This will take quite a while the first time, because Perseus is literally creating another crate in the background (check out the new `.perseus/` folder if you're interested) and building all its dependencies. Also note that your `.gitignore` has been added to to ignore the `.perseus/` folder, which can be rebuilt on any machine with the Perseus CLI installed, so there's no point having it in Git. | ||
|
||
When all 5 steps have been completed, your app will be available in the browser! Go to <http://localhost:8080> and you should see `Welcome to the app!` on your screen! If so, then congratulations, you've just created your first ever Perseus app! (And don't worry, running it will be much faster next time.) | ||
|
||
You can see all the code put together [here](https://github.com/arctic-hen7/perseus-starter-app). | ||
|
||
## Further Reading | ||
|
||
Now that you've created your first app with Perseus, you're more than ready to venture into the rest of the documentation, which will explain each aspect of what you've achieved here in further detail, and will guide you in building more complex apps, particularly in the [rendering strategies](https://arctic-hen7.github.io/perseus/strategies/intro.html) section, which explains the real magic of Perseus' rendering. | ||
|
||
So go forth and build something amazing! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# Building Your First App | ||
|
||
This section is a tutorial on building your first app with Perseus! This will cover the basic usage of the Perseus CLI to build/serve your app, the setup of a routing system, and the creation of basic templates by teaching you by example how to create a simple site with an index page and an about page. | ||
This section is a tutorial on building your first app with Perseus! This will cover the basic usage of the Perseus CLI to build/serve your app, the setup of a routing system, and the creation of basic templates by teaching you by example how to create a simple site with a landing page. | ||
|
||
If learning by reading isn't really your thing, or you'd like a reference, you can see all the code in the `basic` example [here](https://github.com/arctic-hen7/perseus/tree/main/examples/basic). | ||
If learning by reading isn't really your thing, or you'd like a reference, you can see all the code in [this repository](https://github.com/arctic-hen7/perseus-starter-app) (each commit being for each of the sections in this tutorial). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,76 @@ | ||
# Installation and Setup | ||
|
||
TODO | ||
Perseus aims to be as easy to use as possible with the default settings, but you will need to install a few things first. | ||
|
||
## Rust and Cargo | ||
|
||
Make sure you've got Rust and Cargo. If you haven't yet, install them from [here](https://doc.rust-lang.org/stable/book/ch01-01-installation.html), and you might want to check out the [Rust book](https://doc.rust-lang.org/book) if you're unfamiliar with the language. | ||
|
||
After you've done this, make sure everything works by running `cargo` in a terminal. You should get a help page! | ||
|
||
## Build Tools | ||
|
||
Perseus is built on top of WASM (WebAssembly), which basically lets you use programming languages other than JavaScript to build websites/webapps. That tech is *really* complicated, and you'll need two particular build tools to make your code work. | ||
|
||
The first one is [`wasm-pack`](), which helps to compile your Rust code to WebAssembly (sort of like how you'd compile code normally, but specially for the browser). You can install it with this command: | ||
|
||
``` | ||
cargo install wasm-pack | ||
``` | ||
|
||
Now, you should be able to type `wasm-pack` in your terminal to get another help page! | ||
|
||
The next tool is one you might be familiar with if you're coming from the JavaScript world: [Rollup](https://rollupjs.org). Rollup is a bundling tool for JavaScript, and it works really nicely with WASM. If you loathe JavaScript with a passion, don't worry, the only JavaScript in Perseus just invokes your (infinitely superior) Rust code! You can install Rollup with the following command: | ||
|
||
``` | ||
npm i -g rollup | ||
``` | ||
or | ||
``` | ||
yarn global add rollup | ||
``` | ||
|
||
Both of those assume you have either `npm` or `yarn` installed. If not, you can check out other installation options [here](https://rollupjs.org/guide/en/#installation). | ||
|
||
## Perseus CLI | ||
|
||
Perseus is easiest to use with the official CLI, which will let you effortlessly develop apps and serve them locally. It's not quite designed for production serving yet, but development on that is actively underway! | ||
|
||
You can install the CLI with the following command: | ||
|
||
``` | ||
cargo install perseus-cli | ||
``` | ||
|
||
And it should be available as `perseus` in your terminal! Type `perseus -v` to check that everything works (you should see a version number). | ||
|
||
Note that the Perseus CLI will not work unless you've installed Cargo, `wasm-pack` and Rollup. If you've installed them at different paths, you can set the `PERSEUS_CARGO_PATH`/`PERSEUS_WASM_PACK_PATH`/`PERSEUS_ROLLUP_PATH` environment variables to define those. The default paths are `cargo`, `wasm-pack`, and `rollup` respectively. | ||
|
||
## Setting Up a New Project | ||
|
||
Okay! Now that you've got all the build tools and the CLI set up, you can create a new project with this command in the directory where you want your app: | ||
|
||
``` | ||
cargo init --lib | ||
``` | ||
|
||
You should now have an `src/` directory and a `Cargo.toml` file, which is what we'll edit first. You need quite a few dependencies for Perseus to work, which you can set up by adding the following to your `Cargo.toml` under the `[dependencies]` line: | ||
|
||
```toml | ||
# Perseus itself, which we (amazingly) need for a Perseus app | ||
perseus = "0.1" | ||
# Sycamore, the library Perseus depends on for lower-leve reactivity primitivity | ||
sycamore = { version = "0.5.1", features = ["ssr"] } | ||
sycamore-router = "0.5.1" | ||
# Serde, which lets you work with representations of data, like JSON | ||
serde = { version = "1", features = ["derive"] } | ||
serde_json = "1" | ||
``` | ||
|
||
Now run the following command to install and build everything for the first time (this will take a little while): | ||
|
||
``` | ||
cargo build | ||
``` | ||
|
||
If that all works, then congratulations, you've just created the scaffold of a Perseus app! It's time to make your app do something now! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Writing Your First Template | ||
|
||
Now it's time to write your first template! Templates in Perseus are the things that create pages (e.g. you might have an about page template and a blog post template). You can read more about them [here](https://arctic-hen7.github.io/perseus/arch.html#templates-and-pages). | ||
|
||
*Note: templates used to be called pages in Perseus, so some of the older examples may still use that term. Don't worry about it, just be aware of the inconsistency! We're cleaning it up gradually!* | ||
|
||
## Setting up Templates | ||
|
||
1. Create a new folder under `src/` called `templates`. This is where all your templates will be housed. | ||
2. Add a file that declares that as a module (that's how Cargo handles subfolders) called `mod.rs` under `src/templates/`. | ||
3. Declare the module by adding `mod templates;` to the top of `src/lib.rs` (which Cargo should've created for you). | ||
|
||
## Writing an Index Page | ||
|
||
|
||
Let's write the landing page of your app! Create a new file under `src/templates/` called `index.rs`, and then declare it by adding `pub mod index;` to the top of `src/templates/mod.rs` (`pub` because it should be publicly accessible by `lib.rs`). | ||
|
||
Now add the following to that file: | ||
|
||
```rust | ||
use perseus::Template; | ||
use std::sync::Arc; | ||
use sycamore::prelude::{component, template, GenericNode, Template as SycamoreTemplate}; | ||
|
||
#[component(IndexPage<G>)] | ||
pub fn index_page() -> SycamoreTemplate<G> { | ||
template! { | ||
p { "Welcome to the app!" } | ||
} | ||
} | ||
|
||
pub fn template_fn<G: GenericNode>() -> perseus::template::TemplateFn<G> { | ||
Arc::new(|_| { | ||
template! { | ||
IndexPage() | ||
} | ||
}) | ||
} | ||
|
||
pub fn get_template<G: GenericNode>() -> Template<G> { | ||
Template::new("index").template(template_fn()) | ||
} | ||
``` | ||
|
||
This code needs a lot of explaining, so here goes! | ||
|
||
1. Import everything we need. That's the type for a Perseus template, an [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) (magical Rust type wizardry if you're a beginner!), and some stuff from [Sycamore](https://github.com/sycamore-rs/sycamore) that lets us make an interface. | ||
2. Define the component that will render your final page with the `index_page` function (annotated so that it becomes `IndexPage` with Sycamore's internal magic). Notice that this outputs a `SycamoreTemplate`, which is basically a Rust way of writing HTML. The syntax is a little weird, but you'll get used to it pretty quickly (and there's [some work](https://github.com/sycamore-rs/sycamore/issues/23) happening to possibly make it more HTML-like in future). All this little `template!` does is render a `p` HTML element (paragraph) and will it with the text `Welcome to the app!`. | ||
3. Define the template function in `template_fn`. This is probably the weirdest part of Perseus, but it'll make more sense with more complex pages. Basically, the template function will be given any props your template takes (this template doesn't take any though, hence the `_`), and then it lets you render your template with them. **Props are what turn a generic template into a unique page.** | ||
4. Finally define a utility function for using this called `get_template`. This actually defines the Perseus template, providing the root path at which it will be rendered as a page (`/index`, without the leading forward slash written). That needs to know about the template function, which we provide with `.template()`. Later, this is where you get to add cool rendering strategies like [revalidation](https://arctic-hen7.github.io/perseus/strategies/revalidation.html), [SSR](https://arctic-hen7.github.io/perseus/strategies/request_state.html), and [incremental generation](https://arctic-hen7.github.io/perseus/strategies/incremental.html)! | ||
|
||
If you understand all that, then you understand how to write a basic template in Perseus! Well done! If not, don't worry, but you might want to go over the above explanation a few times until you're understanding the basics. If you're feeling clueless, reach out on the [Gitter support channel](https://gitter.im/perseus-framework/support) for help! |