MaCH repo
This commit is contained in:
@@ -88,9 +88,9 @@ Builds a todo app one concept at a time. See the [Reference](#reference) for ful
|
||||
* [2. Show Data](#2-show-data)
|
||||
* [3. Accept Input](#3-accept-input)
|
||||
* [4. Nested Data](#4-nested-data)
|
||||
* [5. Tasks](#5-tasks)
|
||||
* [6. Modules and Events](#6-modules-and-events)
|
||||
* [7. Calling APIs](#7-calling-apis)
|
||||
* [5. Calling APIs](#5-calling-apis)
|
||||
* [6. Tasks](#6-tasks)
|
||||
* [7. Modules and Events](#7-modules-and-events)
|
||||
|
||||
### 1. Pages and Templates
|
||||
|
||||
@@ -404,7 +404,65 @@ Register the migration and add a `todo` resource:
|
||||
|
||||
Both queries in one `sqlite_query()` call run concurrently. `join()` lifts `comments` inside each `todo_data` record, so the template reaches `{{#comments}}` from within `{{#todo_data}}`. `.must_exist = true` returns 404 when the id matches nothing. See [join](#join) and [query](#query).
|
||||
|
||||
### 5. Tasks
|
||||
### 5. Calling APIs
|
||||
|
||||
`fetch()` calls external HTTP services like a query calls a database. JSON parses into context tables; multiple items in one `fetch()` run concurrently.
|
||||
|
||||
Show the responses on the home page:
|
||||
|
||||
**`home.mustache.html`**
|
||||
```diff
|
||||
<html>
|
||||
<body>
|
||||
<nav><a href='{{url:home}}'>Home</a> · <a href='{{url:todos}}'>My Todos</a></nav>
|
||||
<main>
|
||||
- {{$body}}
|
||||
- <h1>Welcome</h1>
|
||||
- {{/body}}
|
||||
+ {{$body}}
|
||||
+ <h1>Welcome</h1>
|
||||
+ {{#weather}}
|
||||
+ <p>{{city}}: {{precision:temp_c:0}}°C, {{conditions}}</p>
|
||||
+ {{/weather}}
|
||||
+ {{#quote}}
|
||||
+ <blockquote>{{content}}, {{author}}</blockquote>
|
||||
+ {{/quote}}
|
||||
+ {{/body}}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Fetch both services concurrently before rendering:
|
||||
|
||||
**`main.c`**
|
||||
```diff
|
||||
#include <mach.h>
|
||||
#include <sqlite.h>
|
||||
|
||||
mach(main){
|
||||
sqlite_database(
|
||||
.name = "todos_db",
|
||||
.connect = "file:todos.db?mode=rwc",
|
||||
.migrations = {"create_todos_table", "create_comments_table"},
|
||||
.seeds = {"seed_todos"}
|
||||
);
|
||||
|
||||
resource("home", "/",
|
||||
.get = {
|
||||
+ fetch(
|
||||
+ {"https://api.quotes.dev/random", "quote"},
|
||||
+ {"https://api.weather.dev/now", "weather"}
|
||||
+ ),
|
||||
mustache("home", "home_s"),
|
||||
respond("home_s")
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Both requests run concurrently under one `fetch()` call. The JSON parses into context tables the template walks with `{{#quote}}` and `{{#weather}}`. `fetch()` also supports other verbs, headers, request bodies, and interpolated URLs. See [fetch](#fetch).
|
||||
|
||||
### 6. Tasks
|
||||
|
||||
A task is a named, reusable pipeline. Define it once with optional `.cron`; dispatch durable background runs with `dispatch_task("name")` (from `dispatch.h`).
|
||||
|
||||
@@ -455,6 +513,10 @@ Register the migration, define the tasks, dispatch them from the POST:
|
||||
+
|
||||
resource("home", "/",
|
||||
.get = {
|
||||
fetch(
|
||||
{"https://api.quotes.dev/random", "quote"},
|
||||
{"https://api.weather.dev/now", "weather"}
|
||||
),
|
||||
mustache("home", "home_s"),
|
||||
respond("home_s")
|
||||
}
|
||||
@@ -494,7 +556,7 @@ Register the migration, define the tasks, dispatch them from the POST:
|
||||
|
||||
`.cron` and `dispatch_task(...)` both run the task on a task reactor, off the request reactors, so the POST returns immediately. Dispatched tasks are durable: a crash mid-task resumes on the next boot. To hand values to a task, list them under `.accepts`; `notify_new_todo` pulls in `title` that way. `dispatch_task()` comes from `dispatch.h`. See [Task Pipelines](#task-pipelines).
|
||||
|
||||
### 6. Modules and Events
|
||||
### 7. Modules and Events
|
||||
|
||||
Split features into modules that talk through pub/sub events. A module is a folder with a matching `.c` file (`todos/todos.c`) declaring `mach(todos){ ... }`, which registers the module's resources, databases, tasks, and subscribers. Assets in the folder belong to the module. `main.c` composes modules by `#include`ing each `.c` file; the include is all that is needed.
|
||||
|
||||
@@ -554,6 +616,10 @@ This step moves todos into its own module and adds an `activity` module that rec
|
||||
-
|
||||
resource("home", "/",
|
||||
.get = {
|
||||
fetch(
|
||||
{"https://api.quotes.dev/random", "quote"},
|
||||
{"https://api.weather.dev/now", "weather"}
|
||||
),
|
||||
mustache("home", "home_s"),
|
||||
respond("home_s")
|
||||
}
|
||||
@@ -602,6 +668,12 @@ Add an Activity link to the shared nav:
|
||||
<main>
|
||||
{{$body}}
|
||||
<h1>Welcome</h1>
|
||||
{{#weather}}
|
||||
<p>{{city}}: {{precision:temp_c:0}}°C, {{conditions}}</p>
|
||||
{{/weather}}
|
||||
{{#quote}}
|
||||
<blockquote>{{content}}, {{author}}</blockquote>
|
||||
{{/quote}}
|
||||
{{/body}}
|
||||
</main>
|
||||
</body>
|
||||
@@ -739,65 +811,6 @@ mach(activity){
|
||||
|
||||
When the POST calls `emit("todo_created")`, MACH propagates the keys named in `publish(...).with` (`title`) to every subscriber. The `activity` module writes its row with no direct link to the publisher. Events are durable: undelivered ones replay after a crash. Adding a third subscriber is a new file with its own `subscribe(...)`; the publisher does not change. See [Modules and Composition](#modules-and-composition) and [Event Pipelines](#event-pipelines).
|
||||
|
||||
### 7. Calling APIs
|
||||
|
||||
`fetch()` calls external HTTP services like a query calls a database. JSON parses into context tables; multiple items in one `fetch()` run concurrently.
|
||||
|
||||
Show the responses on the home page:
|
||||
|
||||
**`home.mustache.html`**
|
||||
```diff
|
||||
<html>
|
||||
<body>
|
||||
<nav><a href='{{url:home}}'>Home</a> · <a href='{{url:todos}}'>My Todos</a> · <a href='{{url:activity}}'>Activity</a></nav>
|
||||
<main>
|
||||
- {{$body}}
|
||||
- <h1>Welcome</h1>
|
||||
- {{/body}}
|
||||
+ {{$body}}
|
||||
+ <h1>Welcome</h1>
|
||||
+ {{#weather}}
|
||||
+ <p>{{city}}: {{precision:temp_c:0}}°C, {{conditions}}</p>
|
||||
+ {{/weather}}
|
||||
+ {{#quote}}
|
||||
+ <blockquote>{{content}}, {{author}}</blockquote>
|
||||
+ {{/quote}}
|
||||
+ {{/body}}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Fetch both services concurrently before rendering:
|
||||
|
||||
**`main.c`**
|
||||
```diff
|
||||
#include <mach.h>
|
||||
#include "todos/todos.c"
|
||||
#include "activity/activity.c"
|
||||
|
||||
mach(main){
|
||||
- resource("home", "/",
|
||||
- .get = {
|
||||
- mustache("home", "home_s"),
|
||||
- respond("home_s")
|
||||
- }
|
||||
- );
|
||||
+ resource("home", "/",
|
||||
+ .get = {
|
||||
+ fetch(
|
||||
+ {"https://api.quotes.dev/random", "quote"},
|
||||
+ {"https://api.weather.dev/now", "weather"}
|
||||
+ ),
|
||||
+ mustache("home", "home_s"),
|
||||
+ respond("home_s")
|
||||
+ }
|
||||
+ );
|
||||
}
|
||||
```
|
||||
|
||||
Both requests run concurrently under one `fetch()` call. The JSON parses into context tables the template walks with `{{#quote}}` and `{{#weather}}`. `fetch()` also supports other verbs, headers, request bodies, and interpolated URLs. See [fetch](#fetch).
|
||||
|
||||
---
|
||||
## Reference
|
||||
|
||||
|
||||
Reference in New Issue
Block a user