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)
|
* [2. Show Data](#2-show-data)
|
||||||
* [3. Accept Input](#3-accept-input)
|
* [3. Accept Input](#3-accept-input)
|
||||||
* [4. Nested Data](#4-nested-data)
|
* [4. Nested Data](#4-nested-data)
|
||||||
* [5. Tasks](#5-tasks)
|
* [5. Calling APIs](#5-calling-apis)
|
||||||
* [6. Modules and Events](#6-modules-and-events)
|
* [6. Tasks](#6-tasks)
|
||||||
* [7. Calling APIs](#7-calling-apis)
|
* [7. Modules and Events](#7-modules-and-events)
|
||||||
|
|
||||||
### 1. Pages and Templates
|
### 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).
|
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`).
|
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", "/",
|
resource("home", "/",
|
||||||
.get = {
|
.get = {
|
||||||
|
fetch(
|
||||||
|
{"https://api.quotes.dev/random", "quote"},
|
||||||
|
{"https://api.weather.dev/now", "weather"}
|
||||||
|
),
|
||||||
mustache("home", "home_s"),
|
mustache("home", "home_s"),
|
||||||
respond("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).
|
`.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.
|
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", "/",
|
resource("home", "/",
|
||||||
.get = {
|
.get = {
|
||||||
|
fetch(
|
||||||
|
{"https://api.quotes.dev/random", "quote"},
|
||||||
|
{"https://api.weather.dev/now", "weather"}
|
||||||
|
),
|
||||||
mustache("home", "home_s"),
|
mustache("home", "home_s"),
|
||||||
respond("home_s")
|
respond("home_s")
|
||||||
}
|
}
|
||||||
@@ -602,6 +668,12 @@ Add an Activity link to the shared nav:
|
|||||||
<main>
|
<main>
|
||||||
{{$body}}
|
{{$body}}
|
||||||
<h1>Welcome</h1>
|
<h1>Welcome</h1>
|
||||||
|
{{#weather}}
|
||||||
|
<p>{{city}}: {{precision:temp_c:0}}°C, {{conditions}}</p>
|
||||||
|
{{/weather}}
|
||||||
|
{{#quote}}
|
||||||
|
<blockquote>{{content}}, {{author}}</blockquote>
|
||||||
|
{{/quote}}
|
||||||
{{/body}}
|
{{/body}}
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</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).
|
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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user