mirror of
https://github.com/urbit/developers.urbit.org.git
synced 2024-08-17 01:30:31 +03:00
Merge pull request #1 from nospur-sontud/nospur-sontud-patch-1
Nospur sontud patch 1
This commit is contained in:
commit
8e58fa5dd9
@ -165,7 +165,7 @@ Since we're being quite thorough in this article, let's summarize every single a
|
||||
| `@uw` | unsigned base64 | `0w_____._____` | `0wbnC.8haTg` | |
|
||||
| `@ux` | unsigned hexadecimal | `0x____.____` | `0x5f5.e138` | |
|
||||
|
||||
You'll also find some irregular auras in use: `%lull`, for instance, has a `@uxblob` type. Nonstandard auras (i.e. those not listed in the table above) render as `@ux` visibly, but are still subject to nesting rules. In fact, the capital-letter suffixes one occasionally encounters (like `@tD` and `@uvJ`) are programmer annotations to mark the intended bit-width of a value. (`A` = {% math %}2^0{% /math %}, `B` = {% math %}2^1%{% /math %}, `C` = {% math %}2^2{% /math %}, `D` = {% math %}2^3{% /math %}, `E` = {% math %}2^5{% /math %}, etc.)
|
||||
You'll also find some irregular auras in use: `%lull`, for instance, has a `@uxblob` type. Nonstandard auras (i.e. those not listed in the table above) render as `@ux` visibly, but are still subject to nesting rules. In fact, the capital-letter suffixes one occasionally encounters (like `@tD` and `@uvJ`) are programmer annotations to mark the intended bit-width of a value. (`A` = {% math %}2^0{% /math %}, `B` = {% math %}2^1%{% /math %}, `C` = {% math %}2^2{% /math %}, `D` = {% math %}2^3{% /math %}, `E` = {% math %}2^4{% /math %}, etc.)
|
||||
|
||||
We also include two other literal syntaxes which don't resolve to atoms:
|
||||
|
||||
|
@ -74,7 +74,7 @@ For instance, in `/sys/vane/ames.hoon`, we find the definitions
|
||||
Thus for Ames, the nested core pattern largely consists of building a list of particular moves and then pulling them back out:
|
||||
|
||||
```hoon
|
||||
:: if processing succeded, send positive ack packet and exit
|
||||
:: if processing succeeded, send positive ack packet and exit
|
||||
++ send-ack
|
||||
|= =bone
|
||||
^+ event-core
|
||||
@ -121,7 +121,7 @@ Gall uses two nested cores to manage agents: `++mo` handles Arvo-level moves, w
|
||||
```hoon
|
||||
::
|
||||
++ mo-core .
|
||||
:: +mo-abed: initialise state with the provided duct
|
||||
:: +mo-abed: initialize state with the provided duct
|
||||
++ mo-abed |=(hun=duct mo-core(hen hun))
|
||||
:: +mo-abet: finalize, reversing moves
|
||||
++ mo-abet [(flop moves) gall-payload]
|
||||
|
@ -4,29 +4,25 @@ date = "2023-05-01"
|
||||
weight = 90
|
||||
next_cohort = "August 2023"
|
||||
image = "https://storage.googleapis.com/media.urbit.org/developers/images/app-school-live.svg"
|
||||
description = "App School focuses on how to build a backend Gall agent, then on connecting it to a React-based front-end. When you're done, you'll be able to produce and distribute your own Urbit apps."
|
||||
description = " App School teaches you all about Urbit-based backend design using Gall agents, as well as interfacing with a React-based front-end. When you're done, you'll be able to produce and distribute your own Urbit apps."
|
||||
+++
|
||||
|
||||
The Urbit Foundation offers cohort classes to cover the [App
|
||||
School](/guides/core/app-school) curriculum jointly.
|
||||
The Urbit Foundation offers live, cohort-based classes to cover the [App
|
||||
School](/guides/core/app-school) curriculum.
|
||||
|
||||
App School focuses on how to build a backend Gall agent, then on connecting it
|
||||
to a React-based front-end. When you're done, you'll be able to produce and
|
||||
distribute your own Urbit apps.
|
||||
Every userspace Urbit application is a **Gall agent**. App School teaches you all about Urbit-based backend design using Gall agents, as well as interfacing with a React-based front-end. When you're done, you'll be able to produce and distribute your own Urbit apps.
|
||||
|
||||
If you prefer to learn as part of a group with a hands-on instructor, regular
|
||||
If you enjoy learning as part of a group with a hands-on instructor, regular
|
||||
exercises and discussions, and a completion certification, then App School Live
|
||||
will be a good fit for you.
|
||||
|
||||
By completing App School as a cohort student, you should be able to design and
|
||||
implement basic user apps, produce minimal working example applications, and
|
||||
contribute at the level of basic Urbit userspace bounties.
|
||||
By completing App School, you will have the basic skillset to design and implement apps, pick up Urbit userspace bounties, and participate in Hackathons.
|
||||
|
||||
The next cohort for App School Live will begin the week of August 21st, 2023.
|
||||
|
||||
- [Complete this form](https://forms.gle/3c8xBubvSiQfj7Tr6) to sign up for the upcomming class.
|
||||
- [Complete this form](https://airtable.com/shrdZcSQrIIb6mAmx) to sign up for the upcoming class.
|
||||
|
||||
Until then, feel free to work through the [App School
|
||||
Until then, feel free to go through the [App School
|
||||
docs](/guides/core/app-school).
|
||||
|
||||
|
||||
|
@ -1,27 +1,24 @@
|
||||
+++
|
||||
title = "Core School Live"
|
||||
title = "Core Academy"
|
||||
weight = 10
|
||||
next_cohort = "Late Summer 2023"
|
||||
next_cohort = "August 28th"
|
||||
image = "https://storage.googleapis.com/media.urbit.org/developers/images/core-school-live.svg"
|
||||
description = "Core School prepares experienced Hoon developers to work on the Arvo kernel, the Vere or New Mars runtime, and otherwise build the platform as a senior developer."
|
||||
description = "Core Academy prepares experienced Hoon developers to work on the Arvo kernel, the Vere or New Mars runtime, and otherwise build the platform as a senior developer."
|
||||
+++
|
||||
|
||||
The Urbit Foundation will offer a cohort classes to train new core developers.
|
||||
Most core developers have been trained up on-the-job inside of Tlon, but as
|
||||
core development expands, other organizations will contribute.
|
||||
|
||||
Core School prepares experienced Hoon developers to work on the Arvo kernel,
|
||||
Core Academy prepares experienced Hoon developers to work on the Arvo kernel,
|
||||
the Vere or New Mars runtime, and otherwise build the platform as a senior
|
||||
developer.
|
||||
|
||||
By completing Core School as a cohort student, you should be able to compose a
|
||||
By completing Core Academy as a cohort student, you should be able to compose a
|
||||
new vane, contribute to Urbit as a core platform and infrastructure developer,
|
||||
and guide in-depth projects with minimal oversight (as distinct from
|
||||
collaboration).
|
||||
|
||||
The first cohort for Core School Live will take place in late summer 2023.
|
||||
|
||||
- [Complete this form](https://forms.gle/gdDWFLiDV1Te65nH8) to get on our
|
||||
mailing list about the next opportunity.
|
||||
- [Complete this form](https://airtable.com/shr6V87yXZxJEZqyu) to sign up for Core Academy before August 6th.
|
||||
|
||||
Until then, feel free to work through the [Arvo docs](/reference/arvo).
|
||||
|
@ -1,7 +1,7 @@
|
||||
+++
|
||||
title = "Hoon School Live"
|
||||
weight = 100
|
||||
next_cohort = "March 29, 2023"
|
||||
next_cohort = "September 2023"
|
||||
image = "https://storage.googleapis.com/media.urbit.org/developers/images/hoon-school-live.svg"
|
||||
description = "Hoon School Live teaches the fundamentals of Hoon with a hands-on instructor, regular exercises and discussions, and a completion certification."
|
||||
+++
|
||||
@ -19,13 +19,11 @@ explain subject-oriented programming. We omit some of the written Hoon School
|
||||
content for time, but you should be well-equipped to understand the optional
|
||||
lessons.
|
||||
|
||||
The next cohort will begin on March 29, 2023. The cohort after that is
|
||||
scheduled to begin in June 2023.
|
||||
The cohort will start in September until October on Thursdays at 1pm EST | 6pm CEST.
|
||||
|
||||
- [Complete this form](https://forms.gle/kgiPckuHaVtJng9r5) to sign up for the next Hoon School Live.
|
||||
- [Complete this form](https://airtable.com/shrqzaLRLzDsHDnCX) to sign up for the next Hoon School Live.
|
||||
|
||||
Until then, feel free to work through the [Hoon School
|
||||
docs](/guides/core/hoon-school).
|
||||
Until then, feel free to work through the [Hoon School docs](/guides/core/hoon-school).
|
||||
|
||||
|
||||
### What will you learn?
|
||||
|
@ -5,11 +5,14 @@ weight = 3
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
|
||||
## [`%ahoy`](/guides/additional/app-workbook/ahoy)
|
||||
## [Building a CLI App with `%rpn`](/guides/additional/app-workbook/rpn)
|
||||
|
||||
## [`%dbug`](/guides/additional/app-workbook/dbug)
|
||||
## [Debugging Wrapper with `%dbug`](/guides/additional/app-workbook/dbug)
|
||||
|
||||
## [`%flap`](/guides/additional/app-workbook/flap)
|
||||
## [Hosting a Website with `%feature`](/guides/additional/app-workbook/feature)
|
||||
|
||||
## [`%feature`](/guides/additional/app-workbook/feature)
|
||||
## [Serving a JS Game with `%flap`](/guides/additional/app-workbook/flap)
|
||||
|
||||
## [Ship Monitoring with `%ahoy`](/guides/additional/app-workbook/ahoy)
|
||||
|
||||
## [Styled Text with `%track7`](/guides/additional/app-workbook/track7)
|
||||
|
@ -1,10 +1,8 @@
|
||||
+++
|
||||
title = "%ahoy Ship Monitoring"
|
||||
weight = 10
|
||||
title = "Ship Monitoring"
|
||||
weight = 194
|
||||
+++
|
||||
|
||||
# `%ahoy` Ship Monitoring
|
||||
|
||||
The `%ahoy` desk by [~midden-fabler](https://urbit.org/ids/~midden-fabler) provides a number of agents to automatically monitor ship activity such as breaching and network uptime. This tutorial examines the `%ahoy` agent specifically with some slight simplifications to demonstrate how an Urbit-native app can be constructed. You will see how to render a front-end using Sail, employ the `++abet` nested core design pattern, construct CLI generators, and set wakeup timers using [Behn](https://developers.urbit.org/reference/glossary/behn).
|
||||
|
||||
`%ahoy` presents a web UI at `/ahoy` rendered using [Sail](https://developers.urbit.org/guides/additional/sail) and [~paldev](https://urbit.org/ids/~paldev)'s Rudder library alongside command-line generators to add, delete, and modify ship watches. Notifications are sent using `%hark-store` if a ship hasn't been contacted after a specified amount of time.
|
||||
|
@ -1,6 +1,6 @@
|
||||
+++
|
||||
title = "%dbug Debugging Wrapper"
|
||||
weight = 40
|
||||
title = "Debugging Wrapper"
|
||||
weight = 42
|
||||
+++
|
||||
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
+++
|
||||
title = "%feature Page Hosting"
|
||||
weight = 160
|
||||
title = "Host a Website"
|
||||
weight = 85
|
||||
+++
|
||||
|
||||
# `%feature` Page Hosting
|
||||
|
||||
[`%feature`](https://github.com/hanfel-dovned/Feature) by [~hanfel-dovned](https://urbit.org/ids/~hanfel-dovned) hosts a simple HTML page from an Urbit ship at an associated URL. This tutorial examines how it uses the middleware [`%schooner`](https://github.com/dalten-collective/schooner/) library by Quartus to return a web page when contacted by a web browser. You will learn how a basic site hosting app can handle HTTP requests and render a page using an `%html` mark.
|
||||
|
||||
`%feature` presents a web page from `/app/feature-ui` at `/apps/feature/feature-ui`. These paths are both configurable by the developer.
|
||||
@ -100,7 +98,7 @@ The system only handles pokes: there are no subscriptions or Arvo calls except
|
||||
[302 ~ [%login-redirect './apps/feature']]
|
||||
?~ body.request.inbound-request
|
||||
[(send [405 ~ [%stock ~]]) state]
|
||||
=/ json (de-json:html q.u.body.request.inbound-request)
|
||||
=/ json (de:json:html q.u.body.request.inbound-request)
|
||||
=/ action (dejs-action +.json)
|
||||
(handle-action action)
|
||||
::
|
||||
@ -185,7 +183,7 @@ The most interesting part of the whole app is the `++handle-http` arm:
|
||||
[302 ~ [%login-redirect './apps/feature']]
|
||||
?~ body.request.inbound-request
|
||||
[(send [405 ~ [%stock ~]]) state]
|
||||
=/ json (de-json:html q.u.body.request.inbound-request)
|
||||
=/ json (de:json:html q.u.body.request.inbound-request)
|
||||
=/ action (dejs-action +.json)
|
||||
(handle-action action)
|
||||
::
|
||||
@ -219,7 +217,7 @@ The most interesting part of the whole app is the `++handle-http` arm:
|
||||
==
|
||||
```
|
||||
|
||||
This arm uses the `server` library and `schooner` to produce a response of a server state and associated data. HTTP requests to `/app/feature` are checked for login authentication, while `/app/feature/public` is not.
|
||||
This arm uses the `server` library and `schooner` to produce a response of a server state and associated data. HTTP requests to `/apps/feature` are checked for login authentication, while `/apps/feature/public` are not.
|
||||
|
||||
### `POST`
|
||||
|
||||
|
@ -1,12 +1,8 @@
|
||||
+++
|
||||
title = "%flap JS Client"
|
||||
weight = 60
|
||||
title = "Serving a JS Game"
|
||||
weight = 193
|
||||
+++
|
||||
|
||||
# `%flap` JS Client
|
||||
|
||||
## Introduction
|
||||
|
||||
In this tutorial, we will take an off-the-shelf JavaScript game which runs in the browser and connect it to an Urbit back-end. This page assumes that you have completed some version of Hoon School and App School, whether the [live courses](/courses) or the [written docs](/guides/core/hoon-school/A-intro). Our goal is to show you one way of directly serving client code from an Urbit ship as server.
|
||||
|
||||
_Flappy Bird_ is an "insanely irritating, difficult and frustrating game which combines a super-steep difficulty curve with bad, boring graphics and jerky movement" ([Huffington Post](https://web.archive.org/web/20140205084251/http://www.huffingtonpost.com/2014/02/03/flappy-bird-tips_n_4717406.html)). We are going to implement `%flap`, a _Flappy Bird_ leaderboard using ~paldev’s `%pals` peer tracking agent. The approach given in this tutorial will apply to any game which is primarily run in the browser and has some persistent state to retain across sessions or communicate between players at discrete intervals. Direct player-v.-player games will require other techniques to implement.
|
||||
@ -339,7 +335,7 @@ The main app implements the logic for exposing and tracking data.
|
||||
%'POST'
|
||||
?~ body.request.inbound-request
|
||||
[(send [405 ~ [%stock ~]]) this]
|
||||
=/ json (de-json:html q.u.body.request.inbound-request)
|
||||
=/ json (de:json:html q.u.body.request.inbound-request)
|
||||
=/ axn `action`(dejs-action +.json)
|
||||
(on-poke %flap-action !>(axn))
|
||||
::
|
||||
@ -501,7 +497,7 @@ Now when we navigate to `localhost:8080/apps/flap`, what do we see? The game ca
|
||||
|
||||
### Serving Correctly
|
||||
|
||||
If we investigate the Developer Tools console in our browser, we see messages to the effect that resources are unable to be located. Resource paths (for `js`, `png`, and `wav` files) tell the browser from whence the resources will come when they are loaded. We have two options here as well: hot-link the resource from its GitHub or other source, or
|
||||
If we investigate the Developer Tools console in our browser, we see messages to the effect that resources are unable to be located. Resource paths (for `js`, `png`, and `wav` files) tell the browser from whence the resources will come when they are loaded. We have two options here as well: hot-link the resource from its GitHub or other source or serve the resource from Urbit.
|
||||
|
||||
If we hot-link the resources, the corresponding lines will look like this:
|
||||
|
||||
@ -852,7 +848,7 @@ With all of the above, you should have a working `%flappy` instance at `http://l
|
||||
%'POST'
|
||||
?~ body.request.inbound-request
|
||||
[(send [405 ~ [%stock ~]]) this]
|
||||
=/ json (de-json:html q.u.body.request.inbound-request)
|
||||
=/ json (de:json:html q.u.body.request.inbound-request)
|
||||
=/ axn `action`(dejs-action +.json)
|
||||
(on-poke %flap-action !>(axn))
|
||||
::
|
||||
@ -1111,7 +1107,7 @@ If you examine `++on-poke` in `/app/flap.hoon`, you will see that HTTP `POST` re
|
||||
%'POST'
|
||||
?~ body.request.inbound-request
|
||||
[(send [405 ~ [%stock ~]]) this]
|
||||
=/ json (de-json:html q.u.body.request.inbound-request)
|
||||
=/ json (de:json:html q.u.body.request.inbound-request)
|
||||
=/ axn `action`(dejs-action +.json)
|
||||
(on-poke %flap-action !>(axn))
|
||||
```
|
||||
|
189
content/guides/additional/app-workbook/rpn.md
Normal file
189
content/guides/additional/app-workbook/rpn.md
Normal file
@ -0,0 +1,189 @@
|
||||
+++
|
||||
title = "Building a CLI App"
|
||||
weight = 28
|
||||
+++
|
||||
|
||||
We will utilize the basic calculator app logic from the [parsing guide](/guides/additional/parsing#recursive-parsers) to produce a linked calculator agent `%rpn` supporting the following operators by the appropriate parsers:
|
||||
|
||||
- numbers (as `@rs` without `.` dot prefix) (`royl-rs:so`)
|
||||
- `+` lus, addition (`lus`)
|
||||
- `-` hep, subtraction (`hep`)
|
||||
- `*` tar, multiplication (`tar`)
|
||||
- `/` fas, division (`fas`)
|
||||
- `.` dot, display top of stack (`dot`)
|
||||
|
||||
We will leave all regular Gall arms as their defaults, but of course poking, subscribing, and peeking should be supported in a full application.
|
||||
|
||||
|
||||
## Agent Logic
|
||||
|
||||
**`/sur/rpn.hoon`**
|
||||
|
||||
We just need to define the expected operators that will show up in the stack. These are `@t` text constants.
|
||||
|
||||
```hoon
|
||||
|%
|
||||
+$ op $? [%op %add]
|
||||
[%op %sub]
|
||||
[%op %mul]
|
||||
[%op %div]
|
||||
[%op %sho]
|
||||
==
|
||||
+$ num @rs
|
||||
+$ command ?(@rs op)
|
||||
--
|
||||
```
|
||||
|
||||
(`+$command` doesn't really feel like the right name here, but we're pattern-matching with the demo `/app/shoe.hoon`.)
|
||||
|
||||
**`/lib/rpn.hoon`**
|
||||
|
||||
These are the parsing rules that the CLI agent will use. We could include these directly in the agent file but we'll post them to a library file.
|
||||
|
||||
```hoon
|
||||
|%
|
||||
++ num royl-rs:so
|
||||
++ op-add (cook |=(p=@ ?:(=('+' p) op+%add ~)) lus)
|
||||
++ op-sub (cook |=(p=@ ?:(=('-' p) op+%sub ~)) hep)
|
||||
++ op-mul (cook |=(p=@ ?:(=('*' p) op+%mul ~)) tar)
|
||||
++ op-div (cook |=(p=@ ?:(=('/' p) op+%div ~)) fas)
|
||||
++ op-sho (cook |=(p=@ ?:(=('.' p) op+%sho ~)) dot)
|
||||
++ ops ;~(pose op-add op-sub op-mul op-div op-sho)
|
||||
--
|
||||
```
|
||||
|
||||
**`/app/rpn.hoon`**
|
||||
|
||||
```hoon
|
||||
++ state-0
|
||||
$: %0
|
||||
stack=(list ?(@rs op:rpn))
|
||||
==
|
||||
```
|
||||
|
||||
**`++command-parser`**
|
||||
|
||||
We want this arm to wait until `RETURN` is pressed so we `++stag` the value with `|` `FALSE`/`%.n`.
|
||||
|
||||
```hoon
|
||||
++ command-parser
|
||||
|= =sole-id:shoe
|
||||
^+ |~(nail *(like [? command:rpn]))
|
||||
%+ stag |
|
||||
(cook command:rpn ;~(pose num:rpnlib ops:rpnlib))
|
||||
```
|
||||
|
||||
**`++on-command`**
|
||||
|
||||
This arm pushes values onto the stack, displays the stack, then checks to parse for the result of an operation.
|
||||
|
||||
```hoon
|
||||
++ on-command
|
||||
|= [=sole-id:shoe =command:rpn]
|
||||
^- (quip card _this)
|
||||
=/ old-stack (weld stack ~[command])
|
||||
=/ new-stack (process:rpnlib old-stack)
|
||||
:_ this(stack new-stack)
|
||||
:~ [%shoe ~ sole+klr+~[(crip "{<old-stack>} →")]]
|
||||
[%shoe ~ sole+klr+~[[[`%br ~ `%g] (crip "{<new-stack>}") ~]]]
|
||||
==
|
||||
```
|
||||
|
||||
For this we add a helper arm to `/lib/rpn.hoon` which takes each entry, makes sure it is a `@rs` atom, and carries out the operation. (This could probably be made more efficient.)
|
||||
|
||||
**`/lib/rpn.hoon`**
|
||||
|
||||
```hoon
|
||||
/- rpn
|
||||
:: * * *
|
||||
++ process
|
||||
|= stack=(list command:rpn)
|
||||
^- (list command:rpn)
|
||||
~| "Failure processing operation on stack {<stack>}"
|
||||
?~ stack !!
|
||||
?- `command:rpn`(snag 0 (flop stack))
|
||||
[%op %add]
|
||||
=/ augend ;;(@rs `command:rpn`(snag 1 (flop stack)))
|
||||
=/ addend ;;(@rs `command:rpn`(snag 2 (flop stack)))
|
||||
(flop (weld ~[(add:rs augend addend)] (slag 3 (flop stack))))
|
||||
::
|
||||
[%op %sub]
|
||||
=/ minuend ;;(@rs `command:rpn`(snag 1 (flop stack)))
|
||||
=/ subtrahend ;;(@rs `command:rpn`(snag 2 (flop stack)))
|
||||
(flop (weld ~[(sub:rs minuend subtrahend)] (slag 3 (flop stack))))
|
||||
::
|
||||
[%op %mul]
|
||||
=/ multiplicand ;;(@rs `command:rpn`(snag 1 (flop stack)))
|
||||
=/ multiplier ;;(@rs `command:rpn`(snag 2 (flop stack)))
|
||||
(flop (weld ~[(mul:rs multiplicand multiplier)] (slag 3 (flop stack))))
|
||||
::
|
||||
[%op %div]
|
||||
=/ numerator ;;(@rs `command:rpn`(snag 1 (flop stack)))
|
||||
=/ denominator ;;(@rs `command:rpn`(snag 2 (flop stack)))
|
||||
(flop (weld ~[(div:rs numerator denominator)] (slag 3 (flop stack))))
|
||||
::
|
||||
[%op %sho]
|
||||
~& > "{<(snag 1 (flop stack))>}"
|
||||
(flop (slag 1 (flop stack)))
|
||||
::
|
||||
@rs
|
||||
stack
|
||||
==
|
||||
```
|
||||
|
||||
### Linking
|
||||
|
||||
After a `%sole` agent has been `|install`ed, it should be registered for Dojo to cycle input to it using `|link`.
|
||||
|
||||
```hoon
|
||||
|link %rpn
|
||||
```
|
||||
|
||||
Now `Ctrl`+`X` allows you to switch to that app and evaluate expressions using it.
|
||||
|
||||
```hoon
|
||||
gall: booted %rpn
|
||||
> 50
|
||||
~ →
|
||||
~[.50]
|
||||
|
||||
> 25
|
||||
~[.50] →
|
||||
~[.50 .25]
|
||||
|
||||
> -
|
||||
~[.50 .25] →
|
||||
~[.-25]
|
||||
|
||||
> 5
|
||||
~[.-25] →
|
||||
~[.-25 .5]
|
||||
|
||||
> /
|
||||
~[.-25 .5] →
|
||||
~[.-0.19999999]
|
||||
|
||||
> 5
|
||||
~[.-0.19999999] →
|
||||
~[.-0.19999999 .5]
|
||||
|
||||
> *
|
||||
~[.-0.19999999 .5] →
|
||||
~[.-0.99999994]
|
||||
|
||||
> 1
|
||||
~[.-0.99999994] →
|
||||
~[.-0.99999994 .1]
|
||||
|
||||
> /
|
||||
~[.-0.99999994 .1] →
|
||||
~[.-1]
|
||||
```
|
||||
|
||||
|
||||
## Exercises
|
||||
|
||||
- Extend the calculator app to support modulus as `%` cen.
|
||||
- Extend the calculator app so it instead operates on `@rd` values. Either use `++cook` to automatically convert the input values from a `1.23`-style input to the `.~1.23` `@rd` style or build a different input parser from the entries in `++royl:so`.
|
||||
- Extend the calculator app so that it can support named variables (using `@tas`) with `=` tis. What new data structure do you need? For convenience, expose the result of the last operation as `ans` (a feature of TI graphing calculators and MATLAB, among other programs).
|
||||
- The calculator app stack isn't really a proper CS stack with push and pop operations. Refactor it to use such a type.
|
174
content/guides/additional/app-workbook/track7.md
Normal file
174
content/guides/additional/app-workbook/track7.md
Normal file
@ -0,0 +1,174 @@
|
||||
+++
|
||||
title = "Styled Text"
|
||||
weight = 199
|
||||
+++
|
||||
|
||||
In this tutorial, we examine how to produce `styx` styled text strings and output them to the terminal from an agent.
|
||||
|
||||
## `%shoe` CLI Session Manager
|
||||
|
||||
`%shoe` is responsible to manage attached agent sessions. It adds a few arms to the standard Gall agent, namely:
|
||||
|
||||
- `++command-parser` is the input parser, similar to the work we were carrying out just above. This parses every input and only permits valid keystrokes (think of Dojo real-time parsing).
|
||||
- `++tab-list` provides autocompletion options. We can ignore for now.
|
||||
- `++on-command` is called whenever a valid command is run. This produces the actual effects.
|
||||
- `++can-connect` supports `|link` connexion to the app.
|
||||
- `++on-connect` provides particular session support when a user connects. We can ignore for now.
|
||||
- `++on-disconnect` provides particular session support when a user disconnects. We can ignore for now.
|
||||
|
||||
To get started with text parsers and CLI agents, we need to focus on `++command-parser` and `++on-command`. But first, the agent's structure and state:
|
||||
|
||||
|
||||
The agent will adopt a two-stage process, wherein a value is put on the stack then the stack is checked for any valid operations.
|
||||
|
||||
### `++command-parser`
|
||||
|
||||
The input parser can simply accept whole words or single inputs, or parse complex expressions (as Dojo does with Hoon).
|
||||
|
||||
This results in a noun of `+$command-type` based on the specific application. The example `/app/shoe.hoon` agent defines:
|
||||
|
||||
```hoon
|
||||
+$ command
|
||||
$? %demo
|
||||
%row
|
||||
%table
|
||||
==
|
||||
```
|
||||
|
||||
and later uses this as:
|
||||
|
||||
```hoon
|
||||
++ command-parser
|
||||
|= =sole-id:shoe
|
||||
^+ |~(nail *(like [? command]))
|
||||
%+ stag &
|
||||
(perk %demo %row %table ~)
|
||||
```
|
||||
|
||||
where the unfamiliar parser components are:
|
||||
|
||||
- `++stag` adds a label, here `&` pam `TRUE`/`%.y` to indicate that the command should be run immediately when it matches. (We won't want this below so we will `++stag` a `|` `FALSE`/`%.n`.)
|
||||
- `++perk` parses a fork in the type.
|
||||
|
||||
### `++on-command`
|
||||
|
||||
This arm accepts a session ID and a command resulting from `++command-parser`. It produces a regular `(quip card _this)` so you can modify agent state and produce effects here.
|
||||
|
||||
|
||||
## `%sole` Effects
|
||||
|
||||
`%sole` is responsible for producing effects. If you want to yield effects to the command line from your CLI agent (which you often do), this is a great place to work.
|
||||
|
||||
`%sole-effect`s are head-tagged by time and produce a variety of terminal effects, from text to bells, colors, and other screen effects.
|
||||
|
||||
|
||||
## `$styx` Styled Text String
|
||||
|
||||
A `klr` effect uses a `styx`, or styled text string. The relevant data structures are in `/sys/lull.hoon`:
|
||||
|
||||
```hoon
|
||||
+$ deco ?(~ %bl %br %un) :: text decoration
|
||||
+$ stye (pair (set deco) (pair tint tint)) :: decos/bg/fg
|
||||
+$ styl %+ pair (unit deco) :: cascading style
|
||||
(pair (unit tint) (unit tint)) ::
|
||||
+$ styx (list $@(@t (pair styl styx))) :: styled text
|
||||
+$ tint $@ ?(%r %g %b %c %m %y %k %w %~) :: text color
|
||||
[r=@uxD g=@uxD b=@uxD] :: 24bit true color
|
||||
```
|
||||
|
||||
- `$deco` is a text decoration, here `%bl` blinking, `%br` bright (bold), and `%un` underlined.
|
||||
- `$tint` is a color, either explicitly the terminal red/green/blue/cyan etc. or a 24-bit true color value.
|
||||
- `$stye` composes these into a style which will be applied to a string.
|
||||
- `$styl` similarly composes styles together.
|
||||
- `$styx` pairs styles with cords.
|
||||
|
||||
This means that composing styled text correctly can require explicitly nesting statements in rather a complicated way.
|
||||
|
||||
For instance, to produce a bold string with hex color `#123456`, we could produce the `sole-effect`:
|
||||
|
||||
```hoon
|
||||
^- sole-effect:sole
|
||||
:- klr
|
||||
^- styx
|
||||
~[[[`%br ~ `[r=0x12 g=0x34 b=0x56]] 'Hello Mars!' ~]]
|
||||
```
|
||||
|
||||
- [~ropdeb-sormyr, "Styled output - requirements and progress" ~2016.8.2 Urbit fora post](https://github.com/urbit/fora-posts/blob/0238536650dfc284f14295d350f9acada0341480/archive/posts/~2016.8.2..21.19.29..2ab8~.md)
|
||||
|
||||
|
||||
## Agent Logic
|
||||
|
||||
Here is an agent that will accept a single character and produce a line with varying random colors of that character.
|
||||
|
||||
**`/app/track7.hoon`**
|
||||
|
||||
```hoon
|
||||
/+ default-agent, dbug, shoe, sole
|
||||
|%
|
||||
+$ versioned-state
|
||||
$% state-0
|
||||
==
|
||||
+$ state-0 %0
|
||||
+$ card card:agent:shoe
|
||||
+$ command @t
|
||||
--
|
||||
%- agent:dbug
|
||||
=| state-0
|
||||
=* state -
|
||||
^- agent:gall
|
||||
%- (agent:shoe command)
|
||||
^- (shoe:shoe command)
|
||||
|_ =bowl:gall
|
||||
+* this .
|
||||
default ~(. (default-agent this %|) bowl)
|
||||
leather ~(. (default:shoe this command) bowl)
|
||||
++ on-init on-init:default
|
||||
++ on-save !>(state)
|
||||
++ on-load
|
||||
|= old=vase
|
||||
^- (quip card _this)
|
||||
`this(state !<(state-0 old))
|
||||
++ on-poke on-poke:default
|
||||
++ on-peek on-peek:default
|
||||
++ on-arvo on-arvo:default
|
||||
++ on-watch on-watch:default
|
||||
++ on-leave on-leave:default
|
||||
++ on-agent on-agent:default
|
||||
++ on-fail on-fail:default
|
||||
++ command-parser
|
||||
|= =sole-id:shoe
|
||||
^+ |~(nail *(like [? command]))
|
||||
(stag & (boss 256 (more gon qit)))
|
||||
++ on-command
|
||||
|= [=sole-id:shoe =command]
|
||||
^- (quip card _this)
|
||||
:_ this
|
||||
^- (list card)
|
||||
:~ :+ %shoe ~
|
||||
^- shoe-effect:shoe
|
||||
:- %sole
|
||||
^- sole-effect:sole :- %klr
|
||||
^- styx
|
||||
=/ idx 0
|
||||
=| fx=styx
|
||||
=/ rng ~(. og eny:bowl)
|
||||
|-
|
||||
?: =(80 idx) fx
|
||||
=^ huer rng (rads:rng 256)
|
||||
=^ hueg rng (rads:rng 256)
|
||||
=^ hueb rng (rads:rng 256)
|
||||
%= $
|
||||
idx +(idx)
|
||||
fx `styx`(weld fx `styx`~[[[`%br ~ `[r=`@ux`huer g=`@ux`hueg b=`@ux`hueb]] command ~]])
|
||||
== ==
|
||||
++ can-connect
|
||||
|= =sole-id:shoe
|
||||
^- ?
|
||||
?| =(~zod src.bowl)
|
||||
(team:title [our src]:bowl)
|
||||
==
|
||||
++ on-connect on-connect:leather
|
||||
++ on-disconnect on-disconnect:leather
|
||||
++ tab-list tab-list:leather
|
||||
--
|
||||
```
|
@ -4,7 +4,7 @@ description = "Learn to write tests with Aqua"
|
||||
weight = 5
|
||||
+++
|
||||
|
||||
# Concepts
|
||||
## Concepts
|
||||
|
||||
Aqua (short for "aquarium", alluding to the idea that you're running
|
||||
multiple ships in a safe, artificial environment and watching them
|
||||
@ -14,7 +14,7 @@ within a single host.
|
||||
pH is a library of functions designed to make it easy to write
|
||||
integration tests using Aqua.
|
||||
|
||||
# First test
|
||||
## First test
|
||||
|
||||
To run your first pH test, run the following commands:
|
||||
|
||||
@ -64,7 +64,7 @@ breaches, mock http clients or servers, or anything you can imagine.
|
||||
Check out `/lib/ph/io.hoon` for other available functions, and look at
|
||||
other tests in `/ted/ph/` for inspiration.
|
||||
|
||||
# Reference
|
||||
## Reference
|
||||
|
||||
Aqua has the following commands:
|
||||
|
||||
|
241
content/guides/additional/generators.md
Normal file
241
content/guides/additional/generators.md
Normal file
@ -0,0 +1,241 @@
|
||||
+++
|
||||
title = "Generators"
|
||||
weight = 73
|
||||
+++
|
||||
|
||||
Generator files provide a way for users to interact with code "scripts" through the Dojo prompt. There are
|
||||
three basic kinds of generators:
|
||||
|
||||
1. Bare or naked generators, standalone computations that can accept input and carry out a single calculation.
|
||||
2. `%say` generators, scripts which can utilize the full system knowledge (`now`, `our`, `eny`) and accept
|
||||
optional input arguments.
|
||||
3. `%ask` generators, scripts driven by interactive prompts.
|
||||
|
||||
(Threads have some commonalities with generators, _q.v._)
|
||||
|
||||
Generators are a Dojo concept, although they can also be applied to agents (such as `+dbug`). This guide will
|
||||
show you how to build and invoke all kinds of generators.
|
||||
|
||||
|
||||
## Bare Generators
|
||||
|
||||
A basic generator is a gate, a core with a `$` buc arm and a sample.
|
||||
|
||||
The Dojo will supply the sample directly to the core in the `$` buc arm.
|
||||
|
||||
A bare generator must be a gate but can have more complicated internal structure, as with all Hoon code. It does
|
||||
not know about entropy `eny`, ship identity `our`, or the timestamp `now`.
|
||||
|
||||
**`/gen/add-one.hoon`**
|
||||
|
||||
```hoon
|
||||
|= n=@ud
|
||||
=<
|
||||
(add-one n)
|
||||
|%
|
||||
++ add-one
|
||||
|= a=@ud
|
||||
^- @ud
|
||||
(add a 1)
|
||||
--
|
||||
```
|
||||
|
||||
Invoke as `+add-one 5`.
|
||||
|
||||
You could in principle use a `|*` bartar wet gate as well, but other cores don't pattern-match to what Dojo expects.
|
||||
|
||||
|
||||
## `%say` Generators
|
||||
|
||||
A `%say` generator can have zero, many, or optional arguments, unlike a bare generator. It can also have access to
|
||||
system variables like `now`, `our`, and `eny`.
|
||||
|
||||
For instance, the following generator can be run with no arguments:
|
||||
|
||||
**`/gen/say.hoon`**
|
||||
|
||||
```hoon {% copy=true %}
|
||||
:- %say
|
||||
|= *
|
||||
:- %noun
|
||||
(add 40 2)
|
||||
```
|
||||
|
||||
```hoon
|
||||
> +say
|
||||
42
|
||||
```
|
||||
|
||||
A `%say` generator is structurally a head-tagged cell of a gate which returns a head-tagged cell of a mark and a value
|
||||
(or a `cask`).
|
||||
|
||||
The head tag over the entire generator is always `%say`. The `cask` tag is most commonly `%noun`.
|
||||
|
||||
We use `%say` generators when we want to provide something else in Arvo, the Urbit operating system, with metadata about
|
||||
the generator's output. This is useful when a generator is needed to pipe data to another program, a frequent occurrence.
|
||||
|
||||
To that end, `%say` generators use `mark`s to make it clear, to other Arvo computations, exactly what kind of data their
|
||||
output is. A `mark` is akin to a MIME type on the Arvo level. A `mark` describes the data in some way, indicating that
|
||||
it's an `%atom`, or that it's a standard such as `%json`, or even that it's an application-specific data structure like
|
||||
`%talk-command`.
|
||||
|
||||
The gate sample follows this pattern, with undesired elements stubbed out by `*`:
|
||||
|
||||
```hoon
|
||||
|= $: :: environment
|
||||
$: now=@da :: timestamp
|
||||
eny=@uvJ :: entropy
|
||||
bec=beak :: clay beak
|
||||
==
|
||||
:: :: unnamed args
|
||||
$=
|
||||
$: arg=@ :: required arguments
|
||||
==
|
||||
~
|
||||
==
|
||||
:: :: named args
|
||||
$=
|
||||
$: named-arg=@ :: optional arguments
|
||||
==
|
||||
~
|
||||
==
|
||||
==
|
||||
```
|
||||
|
||||
The Dojo will modify the sample by inserting `%~` (constant null) at the end of each collection, since the Dojo adapts
|
||||
the input arguments into a list (either the unnamed/required argument list or the named/optional argument list).
|
||||
|
||||
### Zero arguments
|
||||
|
||||
`/gen/vats.hoon` is commonly used to check on the status of installed desks. It can be invoked with optional arguments:
|
||||
|
||||
```
|
||||
> +vats
|
||||
%base
|
||||
/sys/kelvin: [%zuse 414]
|
||||
base hash ends in: drceb
|
||||
%cz hash ends in: drceb
|
||||
app status: running
|
||||
pending updates: ~
|
||||
|
||||
> +vats, =verb %.n
|
||||
%base
|
||||
/sys/kelvin: [%zuse 414]
|
||||
base hash ends in: drceb
|
||||
%cz hash ends in: drceb
|
||||
app status: running
|
||||
pending updates: ~
|
||||
|
||||
> +vats, =filt %suspended
|
||||
```
|
||||
|
||||
### Optional arguments
|
||||
|
||||
Let's look at an example that uses all three parts.
|
||||
|
||||
**`/gen/dice.hoon`**
|
||||
|
||||
```hoon {% copy=true %}
|
||||
:- %say
|
||||
|= [[now=@da eny=@uvJ bec=beak] [n=@ud ~] [bet=@ud ~]]
|
||||
:- %noun
|
||||
[(~(rad og eny) n) bet]
|
||||
```
|
||||
|
||||
This is a very simple dice program with an optional betting functionality. In the code, our sample specifies faces on all
|
||||
of the Arvo data, meaning that we can easily access them. We also require the argument `[n=@ud ~]`, and allow the
|
||||
_optional_ argument `[bet=@ud ~]`.
|
||||
|
||||
We can run this generator like so:
|
||||
|
||||
```hoon
|
||||
> +dice 6, =bet 2
|
||||
[4 2]
|
||||
|
||||
> +dice 6
|
||||
[5 0]
|
||||
|
||||
> +dice 6
|
||||
[2 0]
|
||||
|
||||
> +dice 6, =bet 200
|
||||
[0 200]
|
||||
|
||||
> +dice
|
||||
nest-fail
|
||||
```
|
||||
|
||||
Notice how the `,` com works to separate arguments and how the name of the optional argument must be included.
|
||||
|
||||
We get a different value from the same generator between runs, something that isn't possible with a bare generator. Another
|
||||
novelty is the ability to choose to not use the second argument.
|
||||
|
||||
- [Hoon School, “1.9 Generators”](/guides/core/hoon-school/generators)
|
||||
|
||||
|
||||
## `%ask` Generators
|
||||
|
||||
We use an `%ask` generator when we want to create an interactive program that prompts for inputs as it runs, rather than
|
||||
expecting arguments to be passed in at the time of initiation.
|
||||
|
||||
Like `%say` generators, `%ask` generators are head-tagged cells of gates, but with `%ask`.
|
||||
|
||||
The code below is an `%ask` generator that checks if the user inputs `"blue"` when prompted [per a classic Monty Python
|
||||
scene](https://www.youtube.com/watch?v=L0vlQHxJTp0).
|
||||
|
||||
**`/gen/axe.hoon`**
|
||||
|
||||
```hoon {% copy=true mode="collapse" %}
|
||||
/- sole
|
||||
/+ generators
|
||||
=, [sole generators]
|
||||
:- %ask
|
||||
|= *
|
||||
^- (sole-result (cask tang))
|
||||
%+ print leaf+"What is your favorite color?"
|
||||
%+ prompt [%& %prompt "color: "]
|
||||
|= t=tape
|
||||
%+ produce %tang
|
||||
?: =(t "blue")
|
||||
:~ leaf+"Oh. Thank you very much."
|
||||
leaf+"Right. Off you go then."
|
||||
==
|
||||
:~ leaf+"Aaaaagh!"
|
||||
leaf+"Into the Gorge of Eternal Peril with you!"
|
||||
==
|
||||
```
|
||||
|
||||
Run the generator from the Dojo:
|
||||
|
||||
```hoon
|
||||
> +axe
|
||||
|
||||
What is your favorite color?
|
||||
: color:
|
||||
```
|
||||
|
||||
Instead of simply returning something, your Dojo's prompt changed from `~sampel-palnet:dojo>` to `~sampel-palnet:dojo: color:`,
|
||||
and now expects additional input. Let's give it an answer:
|
||||
|
||||
```hoon
|
||||
: color: red
|
||||
Into the Gorge of Eternal Peril with you!
|
||||
Aaaaagh!
|
||||
```
|
||||
|
||||
`%ask` generators return `sole-effect`s. For more information on these, consult the [guide on command-line agents](/guides/additional/cli).
|
||||
|
||||
`%ask` generators can also accept arguments, although this is uncommon.
|
||||
|
||||
|
||||
## Generators for Agents
|
||||
|
||||
Generators can furthermore interact specifically with agents.
|
||||
|
||||
The [`+dbug` agent](/guides/additional/app-workbook/dbug) is invoked against an agent to display internal state.
|
||||
|
||||
Any app can implement generators to wrap raw pokes (see [`%ahoy`](/guides/additional/app-workbook/ahoy) for instance).
|
||||
|
||||
For instance, `:dojo|wipe` is equivalent to `:dojo +dojo/wipe`. This pokes the `%dojo` agent with the output from running the generator located at `/gen/dojo/wipe.hoon`.
|
||||
|
||||
The Hood/Helm tooling like `|install` are generators automatically routed by Dojo to the correct agent. `|commit`, for instance, is equivalent to `:hood +hood/commit`. `%hood` generators are special-cased because it is the system app.
|
@ -685,7 +685,7 @@ as a reference:
|
||||
|
||||
[eyre-ext-ref]: /reference/arvo/eyre/external-api-ref
|
||||
[eyre-guide]: /reference/arvo/eyre/guide
|
||||
[http-api-src]: https://github.com/urbit/urbit/tree/master/pkg/npm/http-api
|
||||
[http-api-src]: https://github.com/urbit/js-http-api
|
||||
[eyre]: /reference/glossary/eyre
|
||||
[vane]: /reference/glossary/vane
|
||||
[arvo]: /reference/glossary/arvo
|
||||
|
@ -10,8 +10,8 @@ Urbit represents JSON data with the `$json` structure (defined in `lull.hoon`).
|
||||
|
||||
JSON data on the web is encoded in text, so Urbit has two functions in `zuse.hoon` for dealing with this:
|
||||
|
||||
- [`+en-json:html`](/reference/hoon/zuse/2e_2-3#en-jsonhtml) - For printing `$json` to a text-encoded form.
|
||||
- [`+de-json:html`](/reference/hoon/zuse/2e_2-3#de-jsonhtml) - For parsing text-encoded JSON to a `$json` structure.
|
||||
- [`+en:json:html`](/reference/hoon/zuse/2e_2-3#enjsonhtml) - For printing `$json` to a text-encoded form.
|
||||
- [`+de:json:html`](/reference/hoon/zuse/2e_2-3#dejsonhtml) - For parsing text-encoded JSON to a `$json` structure.
|
||||
|
||||
You typically want `$json` data converted to some other `noun` structure or vice versa, so Urbit has three collections of functions for this purpose, also in `zuse.hoon`:
|
||||
|
||||
@ -21,7 +21,7 @@ You typically want `$json` data converted to some other `noun` structure or vice
|
||||
|
||||
The relationship between these types and functions look like this:
|
||||
|
||||
![json diagram](https://media.urbit.org/docs/json-diagram.svg)
|
||||
![json diagram](https://media.urbit.org/docs/json-diagram-v2.svg)
|
||||
|
||||
Note this diagram is a simplification - the `+dejs:format` and `+enjs:format` collections in particular are tools to be used in writing conversion functions rather than simply being used by themselves, but it demonstrates the basic relationships. Additionally, it is less common to do printing/parsing manually - this would typically be handled automatically by Eyre, though it may be necessary if you're retrieving JSON data via the web client vane Iris.
|
||||
|
||||
@ -157,10 +157,10 @@ Now we can try calling the `+to-js` function with our data to convert it to `$js
|
||||
]
|
||||
```
|
||||
|
||||
Let's also see how that `$json` would look as real JSON encoded in text. We can do that with `+en-json:html`:
|
||||
Let's also see how that `$json` would look as real JSON encoded in text. We can do that with `+en:json:html`:
|
||||
|
||||
```
|
||||
> (crip (en-json:html (to-js:user-lib usr)))
|
||||
> (en:json:html (to-js:user-lib usr))
|
||||
'{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}'
|
||||
```
|
||||
|
||||
@ -281,13 +281,13 @@ Let's look at an example. Here's a gate that takes in some `$json`, decodes it w
|
||||
Let's try it:
|
||||
|
||||
```
|
||||
> +of-test (need (de-json:html '{"foo":"Hello"}'))
|
||||
> +of-test (need (de:json:html '{"foo":"Hello"}'))
|
||||
'Hello!!!'
|
||||
|
||||
> +of-test (need (de-json:html '{"bar":true}'))
|
||||
> +of-test (need (de:json:html '{"bar":true}'))
|
||||
'Yes'
|
||||
|
||||
> +of-test (need (de-json:html '{"baz":["a","b","c"]}'))
|
||||
> +of-test (need (de:json:html '{"baz":["a","b","c"]}'))
|
||||
'abc'
|
||||
```
|
||||
|
||||
@ -322,13 +322,13 @@ Let's look at a practical example. Here's a generator you can save in the `%base
|
||||
Let's try it:
|
||||
|
||||
```
|
||||
> +ou-test (need (de-json:html '{"foo":"hello","bar":true,"baz":[1,2,3,4]}'))
|
||||
> +ou-test (need (de:json:html '{"foo":"hello","bar":true,"baz":[1,2,3,4]}'))
|
||||
['hello' %.y {1 2 3 4}]
|
||||
|
||||
> +ou-test (need (de-json:html '{"foo":"hello","bar":true}'))
|
||||
> +ou-test (need (de:json:html '{"foo":"hello","bar":true}'))
|
||||
['hello' %.y {}]
|
||||
|
||||
> +ou-test (need (de-json:html '{"foo":"hello"}'))
|
||||
> +ou-test (need (de:json:html '{"foo":"hello"}'))
|
||||
[%key 'bar']
|
||||
dojo: hoon expression failed
|
||||
```
|
||||
@ -433,7 +433,7 @@ First, we'll save the code above as `user.hoon` in the `/mar` directory our of `
|
||||
Let's quickly create a `$json` object to work with:
|
||||
|
||||
```
|
||||
> =jon (need (de-json:html '{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}'))
|
||||
> =jon (need (de:json:html '{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}'))
|
||||
> jon
|
||||
[ %o
|
||||
p
|
||||
@ -492,7 +492,7 @@ Let's test it out by giving it our `$user` data:
|
||||
Finally, let's see how that looks as JSON encoded in text:
|
||||
|
||||
```
|
||||
> (crip (en-json:html (user-to-json usr)))
|
||||
> (en:json:html (user-to-json usr))
|
||||
'{"joined":1631440078,"username":"john456","name":["John","William","Smith"],"email":"john.smith@example.com"}'
|
||||
```
|
||||
|
||||
|
102
content/guides/additional/noun-channels.md
Normal file
102
content/guides/additional/noun-channels.md
Normal file
@ -0,0 +1,102 @@
|
||||
+++
|
||||
title = "Eyre noun channels"
|
||||
description = "A low-level overview of talking to Eyre's channel system in noun mode."
|
||||
weight = 40
|
||||
+++
|
||||
|
||||
So far, developers have typically used JSON to interact with Urbit ships through
|
||||
Eyre's HTTP interface. As of kernel version `[%zuse 413]`, however, Eyre also
|
||||
supports sending and received nouns directly. At this stage, there are limited
|
||||
options for dealing with nouns in other languages, so this guide will only cover
|
||||
the channel mechanics on a low-level. You may, however, be interested in the
|
||||
[`@urbit/nockjs`](https://github.com/urbit/nockjs) package and the
|
||||
work-in-progress [`json-bgon` PR for
|
||||
`@urbit/js-http-api`](https://github.com/urbit/js-http-api/pull/4).
|
||||
|
||||
{% callout %}
|
||||
|
||||
If you are not familiar with low-level Eyre channel mechanics, please have a
|
||||
read through the [Eyre guide](/reference/arvo/eyre/guide) first.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
Eyre will create a noun channel if a `PUT` request to open a new channel
|
||||
includes the following HTTP header:
|
||||
|
||||
```
|
||||
content-type: application/x-urb-jam
|
||||
```
|
||||
|
||||
...and the body contains the [`++jam`](/reference/hoon/stdlib/2p#jam) of a
|
||||
`list` of `$channel-request`s with
|
||||
[`@uw`](/reference/hoon/auras#table-of-auras) base64 encoding.
|
||||
|
||||
A `channel-request` is defined in `eyre.hoon` as:
|
||||
|
||||
```hoon
|
||||
:: channel-request: an action requested on a channel
|
||||
::
|
||||
+$ channel-request
|
||||
$% :: %ack: acknowledges that the client has received events up to :id
|
||||
::
|
||||
[%ack event-id=@ud]
|
||||
:: %poke: pokes an application, validating :noun against :mark
|
||||
::
|
||||
[%poke request-id=@ud ship=@p app=term mark=@tas =noun]
|
||||
:: %poke-json: pokes an application, translating :json to :mark
|
||||
::
|
||||
[%poke-json request-id=@ud ship=@p app=term mark=@tas =json]
|
||||
:: %watch: subscribes to an application path
|
||||
::
|
||||
[%subscribe request-id=@ud ship=@p app=term =path]
|
||||
:: %leave: unsubscribes from an application path
|
||||
::
|
||||
[%unsubscribe request-id=@ud subscription-id=@ud]
|
||||
:: %delete: kills a channel
|
||||
::
|
||||
[%delete ~]
|
||||
==
|
||||
|
||||
```
|
||||
So, given the following (trivial) `(list channel-request)`:
|
||||
|
||||
```hoon
|
||||
[%delete ~]~
|
||||
```
|
||||
|
||||
...it is jammed to the following HEX:
|
||||
|
||||
```
|
||||
0xACAE8CAD8CAC8F805
|
||||
```
|
||||
|
||||
...then encoded in the following `@uw`-style base64 string in the request body:
|
||||
|
||||
```
|
||||
0w2I.HEOJz.aOfw5
|
||||
```
|
||||
|
||||
If the body of the request is not correctly encoded as described above, it will
|
||||
fail with a `400` status.
|
||||
|
||||
If successful, you can then make a `GET` request to open an event stream for the
|
||||
newly created channel. The `GET` request must include the following header:
|
||||
|
||||
```
|
||||
x-channel-format: application/x-urb-jam
|
||||
```
|
||||
|
||||
If the `GET` request is for an existing channel which is not already in noun
|
||||
mode, it will fail with a `406` status code. You cannot change the channel mode
|
||||
once the channel has been established. If the header is missing, Eyre will
|
||||
assume you're asking for JSON mode, so it will also fail due to the channel mode
|
||||
mismatch.
|
||||
|
||||
If the `GET` request is successful, you'll start receiving SSE events containing `@uw`-encoded jams of the following structure:
|
||||
|
||||
```hoon
|
||||
[request-id=@ud channel-event]
|
||||
```
|
||||
|
||||
See the [`$channel-event`](/reference/arvo/eyre/data-types#channel-event) entry
|
||||
in the data type reference for more details.
|
@ -274,6 +274,25 @@ One common scenario where `+cold` sees play is when writing [command line
|
||||
interface (CLI) apps](/guides/additional/cli-tutorial). We usher the
|
||||
reader there to find an example where `+cold` is used.
|
||||
|
||||
### [`+less`](/reference/hoon/stdlib/4f/#less)
|
||||
|
||||
`+less` builds a `rule` to exclude matches to its first argument. It is commonly
|
||||
used to filter out an undesired match.
|
||||
|
||||
```
|
||||
> (;~(less buc next) [[1 1] " "])
|
||||
[p=[p=1 q=2] q=[~ [p=' ' q=[p=[p=1 q=2] q=""]]]]
|
||||
|
||||
> (;~(less ace next) [[1 1] " "])
|
||||
[p=[p=1 q=1] q=~]
|
||||
```
|
||||
|
||||
Here we see that the first case refuses to parse `buc` `$` (which is not present), so
|
||||
the `ace` ` ` succeeds (via `+next` which matches any character).
|
||||
|
||||
The second case attempts to parse the excluded character `ace` ` ` and fails on the
|
||||
first character as it should.
|
||||
|
||||
### [`+knee`](/reference/hoon/stdlib/4f/#knee)
|
||||
|
||||
Another important function in the parser builder library is `+knee`, used for building
|
||||
|
216
content/guides/additional/remote-scry.md
Normal file
216
content/guides/additional/remote-scry.md
Normal file
@ -0,0 +1,216 @@
|
||||
+++
|
||||
title = "Remote Scry"
|
||||
description = "Learn about scrying over the network"
|
||||
weight = 5
|
||||
+++
|
||||
|
||||
To [scry](/reference/glossary/scry) is to perform a *read* from Urbit's
|
||||
referentially transparent namespace. In other words, it's a function from a
|
||||
`path` to a `noun` (although in some cases, the resulting type may be more
|
||||
constrained). Previously we only supported scrying within the same ship, but
|
||||
from Kernel version `[%zuse 413]`, it is possible to scry from *other* ships.
|
||||
|
||||
{% callout %}
|
||||
|
||||
**Warning**
|
||||
|
||||
1. It should also be noted that, while responses are signed, encryption has not
|
||||
yet been implemented.
|
||||
|
||||
2. The initial release of `[%zuse 413]` had a bug in the remote scry client
|
||||
implementation that causes crashes and failed downloads. This issue was
|
||||
fixed in [this PR](https://github.com/urbit/urbit/pull/6617), which was
|
||||
[released](https://github.com/urbit/urbit/releases/tag/urbit-os-v2.141) (at
|
||||
the same Kelvin) on June 1, 2023. Any ships that are still on the initial
|
||||
`[%zuse 413]` Kelvin release will continue to experience this bug.
|
||||
|
||||
The recommended approach to dealing with this is to include a timer in clients
|
||||
to fall back to using Ames for a download if a remote scry request doesn't
|
||||
succeed within a certain amount of time. This is what Clay uses, and its main
|
||||
advantage is that client ships on older pre-remote-scry Kelvins can still
|
||||
download data from newer server ships.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Lifecycle of a scry
|
||||
|
||||
When you think of scry, you probably think of [`.^`
|
||||
(dotket)](/reference/hoon/rune/dot#-dotket). However, since networking is
|
||||
asynchronous, this is not a suitable interface for remote scry. Instead, a ship
|
||||
that wants to read from a remote part of the namespace will have to pass a
|
||||
`%keen` task to its Ames, which then cooperates with Vere to produce the
|
||||
desired data. In some future event when the result is available, Ames gives it
|
||||
back as a `%tune` gift. From the requester's perspective, this is the entire
|
||||
default lifecycle of a remote scry request.
|
||||
|
||||
Of course, you need to know how `%keen` and `%tune` look to be able to use
|
||||
them. There are also a few exceptions to this default lifecycle. We'll go
|
||||
through all of this in a moment, but first, let's look at what kind of data is
|
||||
possible to scry.
|
||||
|
||||
## Publishing
|
||||
|
||||
At the moment, there are two vanes that can handle remote scry requests:
|
||||
[Clay](/reference/arvo/clay/clay) and [Gall](/reference/arvo/gall/gall). Clay
|
||||
uses it to distribute source code in a more efficient manner than is possible
|
||||
with conventional Ames, but conceptually it only extends its [local
|
||||
scries](/reference/arvo/clay/scry) over the network, with the notable
|
||||
difference that you can't scry at the *current* time, since the requester
|
||||
doesn't know when the request reaches the publisher. Additionally, the paths
|
||||
are modified so that the vane and care are specified separately, like so:
|
||||
`/c/x/1/base/sys/hoon/hoon`.
|
||||
|
||||
Gall is more interesting. First, let's clear up a possible misunderstanding
|
||||
that could easily come up: remote scry does *not* involve calling an agent's
|
||||
`+on-peek` arm. `+on-peek` scries always happen at the current time, and since
|
||||
the requester can't know at which time the publisher handles the request, these
|
||||
aren't possible to reliably serve.
|
||||
|
||||
Instead, agents *ask* Gall to `%grow` paths in the namespace on their behalf.
|
||||
Gall will take care of incrementing version numbers, so that the same path
|
||||
never maps to different nouns. The agent can also ask Gall to delete data,
|
||||
either at a specific version number, or everything up to and including a
|
||||
version number. Concretely, we've extended `$note:agent:gall` to include the
|
||||
following cases:
|
||||
|
||||
```hoon
|
||||
+$ note
|
||||
$% ...
|
||||
[%grow =path =page] :: publish
|
||||
[%tomb =case =path] :: delete one
|
||||
[%cull =case =path] :: delete up to
|
||||
==
|
||||
```
|
||||
|
||||
Here's an example sequence of cards that use these:
|
||||
|
||||
```hoon
|
||||
[%pass /call/back/path %grow /foo atom+'lorem'] :: /foo version 0
|
||||
[%pass /call/back/path %grow /foo atom+'ipsum'] :: /foo version 1
|
||||
[%pass /call/back/path %grow /foo atom+'dolor'] :: /foo version 2
|
||||
[%pass /call/back/path %grow /foo atom+'sit'] :: /foo version 3
|
||||
|
||||
[%pass /call/back/path %tomb ud+3 /foo] :: delete /foo version 3
|
||||
[%pass /call/back/path %cull ud+1 /foo] :: delete /foo 0 through 1
|
||||
|
||||
[%pass /call/back/path %grow /foo atom+'amet'] :: /foo version 4
|
||||
[%pass /call/back/path %grow /foo/bar atom+123] :: /foo/bar version 0
|
||||
```
|
||||
|
||||
After this sequence of cards we would have the following mappings (assuming the
|
||||
agent that emits them is named `%test`):
|
||||
|
||||
```hoon
|
||||
/g/x/2/test//foo -> [%atom 'dolor']
|
||||
/g/x/4/test//foo -> [%atom 'amet']
|
||||
/g/x/0/test//foo/bar -> [%atom 123]
|
||||
```
|
||||
|
||||
Let's pick apart the first one of these paths.
|
||||
|
||||
```hoon
|
||||
/g :: g for Gall
|
||||
/x :: a care of %x generally means "normal read"
|
||||
/2 :: version number
|
||||
/test :: the agent that published the data
|
||||
/ :: ???
|
||||
/foo :: the path that the data is published on
|
||||
```
|
||||
|
||||
What's that lone `/` before the path? It signifies that this data is published
|
||||
by *Gall* itself, instead of the `+on-peek` arm in the `%test` agent. As part
|
||||
of the remote scry release, we will *reserve* part of the scry namespace for
|
||||
Gall, effectively *preventing* any agents from directly publishing at those
|
||||
paths. Though as we've seen, they can do it indirectly, by asking Gall to do it
|
||||
for them using `%grow`.
|
||||
|
||||
As long as the extra `/` is included, Gall will serve scries with care `%x` at
|
||||
both specific revision numbers and at arbitrary times. If the extra `/` is not
|
||||
included, the scry has to happen at the current time, since we don't cache old
|
||||
results of calling `+on-peek`.
|
||||
|
||||
### Additional Gall cares
|
||||
|
||||
Apart from supporting reads using the `%x` care, Gall now also supports three new cares:
|
||||
|
||||
- `%t` lists all subpaths that are bound under a path (only supported at the
|
||||
current time, i.e. not remotely!).
|
||||
- `%w` gives the latest revision number for a path (only supported at the
|
||||
current time, i.e. not remotely!).
|
||||
- `%z` gives the hash identifier of the value bound at the path (supported at
|
||||
any time and at specific revisions, but not remotely).
|
||||
|
||||
All of these require the extra `/` to be present in the path, just as with `%x`.
|
||||
|
||||
## Scrying tasks
|
||||
|
||||
With this, we're ready to look at all the new tasks to, and gifts from, Ames:
|
||||
|
||||
```hoon
|
||||
+$ task
|
||||
$% ...
|
||||
[%keen =ship =path] :: peek [ship /vane/care/case/spur]
|
||||
[%yawn =ship =path] :: cancel request from arvo
|
||||
[%wham =ship =path] :: cancels all scry requests from any vane
|
||||
...
|
||||
==
|
||||
::
|
||||
+$ gift
|
||||
$% ...
|
||||
[%tune spar roar=(unit roar)]
|
||||
...
|
||||
==
|
||||
```
|
||||
|
||||
At this point, most of these should be very clear, but briefly:
|
||||
|
||||
- We pass `[%keen =ship =path]` to Ames to request to read from `path` on
|
||||
`ship`. Example:
|
||||
```hoon
|
||||
[%pass /call/back/path %arvo %a %keen ~sampel /c/x/4/base/sys/hoon/hoon]
|
||||
```
|
||||
|
||||
- We pass `[%yawn =ship =path]` to tell Ames that we're no longer interested in
|
||||
a response. Example:
|
||||
```hoon
|
||||
[%pass /call/back/path %arvo %a %yawn ~sampel /g/x/4/test//foo]
|
||||
```
|
||||
|
||||
- We pass `[%wham =ship =path]` to tell Ames that *no-one* on this ship is
|
||||
interested in a response. Example:
|
||||
```hoon
|
||||
[%pass /call/back/path %arvo %a %wham ~sampel /g/x/4/test//foo]
|
||||
```
|
||||
|
||||
- Ames gives the following to the original requester(s), either when it has a
|
||||
response, or when the request gets `%wham`ed:
|
||||
```hoon
|
||||
[%tune [=ship =path] roar=(unit roar)]
|
||||
```
|
||||
The outer `unit` of `roar` will be `~` if Ames doesn't have a
|
||||
response, but may have one in the future. Otherwise, it will
|
||||
contain a signature and the data. The data in the
|
||||
[`$roar`](/reference/arvo/ames/data-types#roar) may be `~`,
|
||||
meaning that there is no value at this path and will never be
|
||||
one.
|
||||
|
||||
## `-keen`
|
||||
|
||||
In addition to the above interface offered to agents, there is also support for
|
||||
making scry requests from threads using `+keen` in `lib/strandio`. It accepts a
|
||||
`[=ship =path]` and returns a `(unit page)`. There is also a [thread `ted/keen`
|
||||
that demonstrates
|
||||
this](https://github.com/urbit/urbit/blob/i/5788/remote-scry/pkg/arvo/ted/keen.hoon).
|
||||
You can run it from the dojo using `-keen [ship path]`. For example, this reads
|
||||
the `%noun` mark's source code out of `~zod`'s `%kids` desk, try it!
|
||||
|
||||
```
|
||||
-keen [~zod /c/x/1/kids/mar/noun/hoon]
|
||||
```
|
||||
|
||||
## Additional reading
|
||||
|
||||
- [Gall scry reference](/reference/arvo/gall/scry): Reference documentation of
|
||||
Gall's vane-level and agent-level scry interface.
|
||||
|
||||
- [Ames API reference](/reference/arvo/ames/tasks): Reference documentation of `task`s that can be passed to Ames, including those for remote scries.
|
179
content/guides/additional/udon.md
Normal file
179
content/guides/additional/udon.md
Normal file
@ -0,0 +1,179 @@
|
||||
+++
|
||||
title = "Udon (Markdown-esque)"
|
||||
description = "Learn the basics of Udon"
|
||||
weight = 97
|
||||
+++
|
||||
|
||||
Udon is a domain-specific language for composing documents. Udon is very similar
|
||||
to Markdown, but with some minor variation in syntax and some additional
|
||||
Urbit-related affordances.
|
||||
|
||||
Udon files are compiled to `manx`es (Urbit's XHTML/XML representation), so can
|
||||
easily be used to publish documents to the browser. Udon also allows you to
|
||||
embed arbitrary [Sail](/guides/additional/sail) syntax, which itself allows
|
||||
embedding arbitrary Hoon, so it can be quite powerful for dynamic content when
|
||||
compiled against an appropriate subject.
|
||||
|
||||
This document will walk through the basics of Udon and its syntax.
|
||||
|
||||
## Basic example
|
||||
|
||||
Here's an example of an Udon file and its various allowed syntax.
|
||||
|
||||
````
|
||||
;>
|
||||
|
||||
# H1
|
||||
|
||||
## H2
|
||||
|
||||
### H3
|
||||
|
||||
#### H4
|
||||
|
||||
##### H5
|
||||
|
||||
###### H6
|
||||
|
||||
This is a paragraph with _italics_, *bold* and
|
||||
`inline code`. Sentences can be hard wrapped.
|
||||
|
||||
|
||||
- unordered
|
||||
- list
|
||||
|
||||
+ ordered
|
||||
+ list
|
||||
|
||||
[link](https://urbit.org)
|
||||
|
||||
![image](https://media.urbit.org/guides/additional/dist/wut.svg)
|
||||
|
||||
```
|
||||
fenced codeblock
|
||||
(note language spec not supported)
|
||||
```
|
||||
|
||||
horizontal rule:
|
||||
---
|
||||
|
||||
> block quotes
|
||||
may be hard-wrapped if indented
|
||||
|
||||
Backslash at end\
|
||||
of line adds linebreak
|
||||
|
||||
Udon syntax may be prefixed with \*backslashes\* to escape.
|
||||
|
||||
Hoon atom literals like ~sampel-palnet and ~.foo will
|
||||
be rendered as inline code.
|
||||
|
||||
;table
|
||||
;tr
|
||||
;td: Arbitrary
|
||||
;td: Sail
|
||||
==
|
||||
;tr
|
||||
;td: is
|
||||
;td: allowed
|
||||
==
|
||||
==
|
||||
````
|
||||
|
||||
## Syntax summary
|
||||
|
||||
- The first line of a `.udon` document *must* be a single rune: `;>`. This tells
|
||||
the compiler to interpret everything following as udon.
|
||||
- **Paragraphs**: Content on a single line will be made into a paragraph.
|
||||
Paragraphs may be hard-wrapped, so consecutive lines of text will become a
|
||||
single paragraph. The paragraph will be ended by an empty line or other block
|
||||
element.
|
||||
- **Headers**: lines beginning with 1-6 `#`s followed by a single space and then
|
||||
some content (e.g. `## foo`) will be made into headers. The number of `#`s
|
||||
dictates the header level.
|
||||
- **Italics**: content wrapped in single `_`s (e.g. `_foo_`) will be made italic.
|
||||
- **Bold**: content wrapped in single `*`s (e.g. `*foo*`) will be made bold.
|
||||
- **Unordered lists**: lines beginning with `-` followed by a space will be made
|
||||
into items in a list. List lines can be hard-wrapped, with two spaces
|
||||
beginning each subsequent line to be included in the list. Lists can be nested
|
||||
by indenting the `-`s a further two spaces for each level of nesting.
|
||||
- **Ordered lists**: lines beginning with `+` followed by a space will be made into
|
||||
ordered lists, and numbered in the order they appear. These have the same
|
||||
wrapping and nesting logic as unordered lists.
|
||||
- **Links**: this is standard markdown syntax: square bracks containing the display
|
||||
content and then parentheses containing the URL, e.g.
|
||||
`[foo](http://example.com)`. The URL may also be a relative link or an anchor
|
||||
link.
|
||||
- **Images**: this is also standard markdown; a link with an exclamation mark at the
|
||||
beginning, e.g. `![foo](http://example.com/image.png)`. The square brackets
|
||||
contain the alt-text and the the parentheses contain the image URL.
|
||||
- **Inline code**: text wrapped in single backticks will be rendered verbatim in a
|
||||
monospace font.
|
||||
- **Fenced codeblocks**: Triple-backticks on their own line begin and end a
|
||||
codeblock. All lines in between will be rendered verbatim in a monospace font.
|
||||
Note that udon does not support a language specification after the opening
|
||||
backticks like markdown does.
|
||||
- **Horizontal rules**: Three or more hyphens (`---`) will create a horizontal rule.
|
||||
- **Block quotes**: a line beginning with `>` creates a block quote. This may be
|
||||
hard-wrapped, as long as the next line is indented two spaces. Block quotes
|
||||
may contain anything, including other blockquotes.
|
||||
- **Line breaks**: A line ending in a single backslash will have a line break
|
||||
inserted at the end, so it will not flow together with the subsequent line as
|
||||
is usually the case.
|
||||
- **Escape characters**: You may prefix Udon syntax with a backslash to have it
|
||||
treated as the literal text.
|
||||
- **Hoon literals and wings**: Udon will automatically render any values with
|
||||
atom aura syntax as inline code. It'll also render arms like `++foo:bar`,
|
||||
`+$baz`, and `+*foo:bar:baz, as inline code.`
|
||||
- **Sail**: this is hoon's native XML syntax. Udon will parse it, execute it, and
|
||||
include the `+$manx`es produced in the resulting document. This means you can
|
||||
embed arbitrary hoon in the document.
|
||||
|
||||
{% callout %}
|
||||
|
||||
Note that Udon is quite strict on its syntax, and may fail to parse if it's
|
||||
incorrect.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Udon Mode
|
||||
|
||||
An Udon file has a `.udon` extension (an `%udon` mark).
|
||||
|
||||
The first thing in an Udon file must be the `micgar` rune: `;>`
|
||||
|
||||
Micgar tells the Hoon compiler to interpret everything afterwards as Udon.
|
||||
Udon-mode ends at the end of the file; there's no way to terminate micgar before
|
||||
that. Udon is therefore useful for whole documents rather than embedding
|
||||
snippets in other Hoon files.
|
||||
|
||||
The Hoon compiler will produce a `manx` as a result.
|
||||
|
||||
To scry out a file, compile it against the standard library, and stringify the
|
||||
resulting XHTML, you can do:
|
||||
|
||||
```
|
||||
%- crip
|
||||
%- en-xml:html
|
||||
!< manx
|
||||
%+ slap !>(..zuse)
|
||||
%- ream
|
||||
.^(@t %cx /=the-desk=/the-file/udon)
|
||||
```
|
||||
|
||||
Note you may want to provide more than just `..zuse` in the subject (like a
|
||||
`bowl`), or if you're automatically building untrusted code, you may want to
|
||||
provide less. It depends on your use case.
|
||||
|
||||
You can alternatively import and build udon files at compile time with a [`/*`
|
||||
(fastar) Ford rune](/reference/hoon/rune/fas#-fastar) specifying an `%elem` mark
|
||||
(which produces a `manx`), although note it compiles the Udon against an empty
|
||||
subject, so Hoon in embedded Sail won't have access to standard library
|
||||
functions. A mark conversion gate from `%udon` to `%elem` is another option.
|
||||
|
||||
## Examples
|
||||
|
||||
The [Docs App](https://urbit.org/applications/~pocwet/docs) includes a [a few
|
||||
files written in
|
||||
Udon](https://github.com/tinnus-napbus/docs-app/tree/main/bare-desk/doc) which
|
||||
are useful as a reference.
|
@ -14,3 +14,9 @@ insert_anchor_links = "right"
|
||||
## [Roman Numerals](/guides/additional/workbook/roman)
|
||||
|
||||
## [Solitaire Cipher](/guides/additional/workbook/solitaire)
|
||||
|
||||
## [Water between Towers](/guides/additional/workbook/water-towers)
|
||||
|
||||
## [ABC Blocks](/guides/additional/workbook/abc-blocks)
|
||||
|
||||
## [Luhn Number](/guides/additional/workbook/luhn-number)
|
574
content/guides/additional/workbook/abc-blocks.md
Normal file
574
content/guides/additional/workbook/abc-blocks.md
Normal file
@ -0,0 +1,574 @@
|
||||
+++
|
||||
title = "ABC Blocks"
|
||||
weight = 11
|
||||
+++
|
||||
|
||||
## Challenge: ABC Blocks
|
||||
|
||||
You are given a collection of blocks with two letters of the alphabet on each block. A complete alphabet is guaranteed among all sides of the blocks. You would like to check if a given word can be written with the provided set of blocks.
|
||||
|
||||
An example set of blocks:
|
||||
```
|
||||
(F E)
|
||||
(A W)
|
||||
(Q V)
|
||||
(B M)
|
||||
(X H)
|
||||
(N P)
|
||||
(I Z)
|
||||
(G U)
|
||||
(S R)
|
||||
(K Y)
|
||||
(T L)
|
||||
(O C)
|
||||
(J D)
|
||||
(A N)
|
||||
(O B)
|
||||
(E R)
|
||||
(F S)
|
||||
(L Y)
|
||||
(P C)
|
||||
(Z M)
|
||||
```
|
||||
|
||||
Your task for this challenge is to write a generator `abc-blocks`. It takes a cell of two arguments. The first argument is a `(list (pair @t @t))` which represents the input set of blocks. The second argument is a `@t` which represents the word that you'd like to check.
|
||||
|
||||
Your generator should first check if the input blocks cover all letters of the alphabet. If not, the generator should fail (possibly returning an error message). It should also check if the input word only has alphabetical characters (no spaces, numbers, or special characters). Otherwise it should fail. Then, it should check whether the word can be spelled with the blocks, either returning a `%.y` or `%.n`. It should not care about case, for the blocks or for the word.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
> +abc-blocks [[['a', 'b'] ['c' 'd'] ['e' 'f'] ~] 'fad']
|
||||
dojo: naked generator failure
|
||||
|
||||
> +abc-blocks [[['a', 'b'] ['c' 'd'] ['e' 'f'] ['g' 'h'] ['i' 'j'] ['k' 'l'] ['m' 'n'] ['o' 'p'] ['q' 'r'] ['s' 't'] ['u 'v'] ['w' 'x] ['y' z] ~] '12%-3']
|
||||
dojo: naked generator failure
|
||||
|
||||
> +abc-blocks [[['a', 'B'] ['C' 'd'] ['e' 'F'] ['G' 'h'] ['i' 'J'] ['K' 'l'] ['m' 'N'] ['o' 'P'] ['Q' 'r'] ['s' 'T'] ['U 'v'] ['w' 'X'] ['y' Z'] ~] 'cat']
|
||||
%.y
|
||||
|
||||
> +abc-blocks [[['a', 'B'] ['C' 'd'] ['e' 'F'] ['G' 'h'] ['i' 'J'] ['K' 'l'] ['m' 'N'] ['o' 'P'] ['Q' 'r'] ['s' 'T'] ['U 'v'] ['w' 'X'] ['y' Z'] ~] 'CAT']
|
||||
%.y
|
||||
|
||||
> +abc-blocks [[['a', 'B'] ['C' 'd'] ['e' 'F'] ['G' 'h'] ['i' 'J'] ['K' 'l'] ['m' 'N'] ['o' 'P'] ['Q' 'r'] ['s' 'T'] ['U 'v'] ['w' 'X'] ['y' Z'] ~] 'BAT']
|
||||
%.n
|
||||
```
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior.
|
||||
|
||||
```hoon
|
||||
/+ *test
|
||||
/= abc-blocks /gen/abc-blocks
|
||||
|%
|
||||
:: test for failure of incomplete alphabet
|
||||
::
|
||||
++ test-01
|
||||
=/ blocks `(list (pair @t @t))`[['a' 'b'] ['c' 'd'] ['e' 'f'] ['g' 'h'] ['i' 'j'] ['k' 'l'] ['m' 'n'] ['o' 'p'] ['q' 'q'] ['s' 't'] ['u' 'v'] ['w' 'x'] ['y' 'z'] ~]
|
||||
=/ word `@t`'foo'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
++ test-02
|
||||
=/ blocks `(list (pair @t @t))`[['a' 'b'] ['c' 'd'] ['e' 'f'] ['g' 'h'] ['i' 'j'] ['k' 'l'] ['m' 'n'] ['q' 'r'] ['s' 't'] ['u' 'v'] ['w' 'x'] ['y' 'z'] ~]
|
||||
=/ word `@t`'foo'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
++ test-03
|
||||
=/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['E' 'F'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'A'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ~]
|
||||
=/ word `@t`'foo'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
++ test-04
|
||||
=/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['e' 'f'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'A'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ['A' 'B'] ['j' 'x']~]
|
||||
=/ word `@t`'foo'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
++ test-05
|
||||
=/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'foo'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
:: test for failure of input word
|
||||
::
|
||||
++ test-06
|
||||
=/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'foo bar'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
++ test-07
|
||||
=/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'foo1bar'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
++ test-08
|
||||
=/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'foo!bar'
|
||||
%- expect-fail |. (abc-blocks blocks word)
|
||||
:: test for success with various capitalizations and alphabets
|
||||
::
|
||||
++ test-09
|
||||
=/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'TRAP'
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-10
|
||||
=/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'trap'
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-11
|
||||
=/ blocks `(list (pair @t @t))`[['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'tRaP'
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-12
|
||||
=/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'a'] ['c' 't'] ['r' 'o'] ['p' 'n'] ['e' 'y'] ~]
|
||||
=/ word `@t`'TRAP'
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-13
|
||||
=/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ~]
|
||||
=/ word `@t`'trap'
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-14
|
||||
=/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~]
|
||||
=/ word `@t`'fsixqhgjvtrnyyb'
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (abc-blocks blocks word)
|
||||
:: test for being unable to make a word
|
||||
::
|
||||
++ test-15
|
||||
=/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~]
|
||||
=/ word `@t`'fsixqhgjvtrnyyyb'
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-16
|
||||
=/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~]
|
||||
=/ word `@t`'fsixqhgujvtrnyyb'
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-17
|
||||
=/ blocks `(list (pair @t @t))`[['f' 'm'] ['w' 's'] ['i' 'b'] ['d' 'x'] ['q' 'k'] ['z' 'h'] ['g' 'l'] ['u' 'j'] ['v' 'A'] ['c' 't'] ['R' 'o'] ['p' 'n'] ['e' 'y'] ['x' 'y'] ['a' 'b'] ~]
|
||||
=/ word `@t`'AAA'
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-18
|
||||
=/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['E' 'F'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'T'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ~]
|
||||
=/ word `@t`'AGENTT'
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-19
|
||||
=/ blocks `(list (pair @t @t))`[['A' 'B'] ['C' 'D'] ['E' 'F'] ['G' 'H'] ['I' 'J'] ['K' 'L'] ['M' 'N'] ['O' 'P'] ['Q' 'R'] ['S' 'T'] ['U' 'V'] ['W' 'X'] ['Y' 'Z'] ['S' 'T'] ~]
|
||||
=/ word `@t`'AGENTtT'
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (abc-blocks blocks word)
|
||||
++ test-20
|
||||
=/ blocks `(list (pair @t @t))`[['A' 'Z'] ['A' 'Z'] ['F' 'M'] ['W' 'S'] ['I' 'B'] ['D' 'X'] ['Q' 'K'] ['Z' 'H'] ['G' 'L'] ['U' 'J'] ['V' 'A'] ['C' 'T'] ['R' 'O'] ['P' 'N'] ['E' 'Y'] ~]
|
||||
=/ word `@t`'ZAZAZ'
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (abc-blocks blocks word)
|
||||
--
|
||||
```
|
||||
|
||||
## Solutions
|
||||
|
||||
_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere._
|
||||
|
||||
### Solution #1
|
||||
|
||||
_By ~dozreg-toplud. In the process, he found and fixed a bug in the implementation of `++curr`._
|
||||
|
||||
```hoon
|
||||
:: +abc-blocks: a solution to the HSL challenge #2
|
||||
::
|
||||
:: https://github.com/tamlut-modnys/template-hsl-abc-blocks
|
||||
:: Takes a cell of arguments [blocks=(list (pair @t @t)) word=@t],
|
||||
:: produces a flag.
|
||||
:: Crashes if the alphabet is not represented in the blocks, or if there are
|
||||
:: non-alphabetical characters in the blocks or in the word.
|
||||
:: Produces %.y if the word can be written with the given list of blocks,
|
||||
:: %.n otherwise
|
||||
::
|
||||
:: Solving this challenge revealed a bug in ++curr implementation! Refer to
|
||||
:: the bottom of the file.
|
||||
::
|
||||
|^
|
||||
:: Main part:
|
||||
::
|
||||
|= [blocks=(list (pair @t @t)) word=@t]
|
||||
^- ?
|
||||
=/ word-tape=tape (trip word)
|
||||
:: Convert input values to lowercase
|
||||
::
|
||||
=. word-tape (cass word-tape)
|
||||
=. blocks
|
||||
%+ turn
|
||||
blocks
|
||||
|= [a=@t b=@t]
|
||||
^- (pair @t @t)
|
||||
:- (crip (cass (trip a)))
|
||||
(crip (cass (trip b)))
|
||||
:: Define alphabet
|
||||
::
|
||||
=/ alphabet=(set @t) (silt "abcdefghijklmnopqrstuvwxyz")
|
||||
:: Assert: only alphabetical characters in the blocks
|
||||
::
|
||||
?. %+ levy
|
||||
blocks
|
||||
|= [a=@t b=@t]
|
||||
^- ?
|
||||
&((~(has in alphabet) a) (~(has in alphabet) b))
|
||||
~_ leaf+"non-alphabetical character in blocks"
|
||||
!!
|
||||
:: Assert: only alphabetical characters in the word
|
||||
::
|
||||
?. %+ levy
|
||||
word-tape
|
||||
|= =cord
|
||||
^- ?
|
||||
(~(has in alphabet) cord)
|
||||
~_ leaf+"non-alphabetical character in word"
|
||||
!!
|
||||
:: Assert: complete alphabet among the blocks
|
||||
::
|
||||
?. :: Iterate for block list indices i:
|
||||
::
|
||||
=+ i=0
|
||||
|- ^- ?
|
||||
:: if the alphabet set is empty, then the blocks contain all the letters
|
||||
::
|
||||
?: =(~ alphabet)
|
||||
%.y
|
||||
:: else, if we reached the end of the block list, then the opposite is true
|
||||
::
|
||||
?: =(i (lent blocks))
|
||||
%.n
|
||||
:: else, delete letters on a block from the alphabet and continue
|
||||
::
|
||||
=+ [a b]=(snag i blocks)
|
||||
$(i +(i), alphabet (~(del in (~(del in alphabet) b)) a))
|
||||
~_ leaf+"not complete alphabet in blocks"
|
||||
!!
|
||||
:: check if we can compose the word with the blocks
|
||||
::
|
||||
(check blocks word-tape)
|
||||
::
|
||||
:: Helping functions
|
||||
:: ++check: checks if the word can be composed with the given blocks
|
||||
::
|
||||
++ check
|
||||
|= [blocks=(list (pair @t @t)) word=tape]
|
||||
^- ?
|
||||
:: Self-reference
|
||||
::
|
||||
=* this-gate ..$
|
||||
:: The word can be composed if it's empty, ...
|
||||
::
|
||||
?~ word %.y
|
||||
:: ... or if the list of indices of blocks that contain i.word is not empty
|
||||
:: and t.word can be composed with at least one list of the blocks made by
|
||||
:: removing one of the blocks that contain i.word.
|
||||
::
|
||||
:: Logical OR on a list (%.n if the list is empty)
|
||||
::
|
||||
%+ lien
|
||||
:: (list of lists of blocks made by removing one block that contains
|
||||
:: i.word for each such block)
|
||||
::
|
||||
%+ turn
|
||||
:: (list of block indices that contain i.word)
|
||||
::
|
||||
(find-in-blocks i.word blocks)
|
||||
:: (gate that removes a block from a list of blocks by an index)
|
||||
::
|
||||
(curr (curr oust blocks) 1)
|
||||
:: (gate that applies ++check to a given list of blocks and t.word)
|
||||
::
|
||||
(curr this-gate t.word)
|
||||
:: ++ find-in-blocks: returns a list of block indices that contain
|
||||
:: a given letter
|
||||
::
|
||||
++ find-in-blocks
|
||||
|= [letter=@t blocks=(list (pair @t @t))]
|
||||
^- (list @)
|
||||
=+ i=0
|
||||
=| =(list @)
|
||||
:: Iterate over elements of blocks
|
||||
::
|
||||
|-
|
||||
?~ blocks
|
||||
list
|
||||
:: If a block contains the letter, append its index to the list
|
||||
::
|
||||
=? list |(=(letter -:i.blocks) =(letter +:i.blocks)) (snoc list i)
|
||||
$(i +(i), blocks t.blocks)
|
||||
:: ++curr: rewrite ++curr from stdlib because the original has a bug
|
||||
:: (https://github.com/urbit/urbit/issues/6655)
|
||||
::
|
||||
++ curr
|
||||
|* [a=$-(^ *) c=*]
|
||||
|* b=_,.+<-.a
|
||||
(a b c)
|
||||
::
|
||||
--
|
||||
```
|
||||
|
||||
### Solution #2
|
||||
|
||||
_By ~bantus-follus_
|
||||
|
||||
```hoon
|
||||
|= [blocks=(list (pair @t @t)) word=@t]
|
||||
=<
|
||||
=/ alphacheck (alphabet-check merged-blocks)
|
||||
?. (character-check word)
|
||||
~| "Input word contains invalid characters." !!
|
||||
=/ spellcheck (spell-check word)
|
||||
spellcheck
|
||||
|%
|
||||
++ alphabet "abcdefghijklmnopqrstuvwxyz"
|
||||
::
|
||||
:: merges all blocks into a single tape
|
||||
++ merged-blocks (merge blocks)
|
||||
::
|
||||
:: turns all blocks into individual tapes
|
||||
++ tape-blocks (turn (turn (turn (turn blocks pair-to-list) crip) trip) cass)
|
||||
++ merge
|
||||
|= blocks=(list (pair @t @t))
|
||||
^- tape
|
||||
(cass (trip (crip `(list @t)`(zing (turn blocks pair-to-list)))))
|
||||
::
|
||||
:: converts each pair to a (list @t)
|
||||
++ pair-to-list
|
||||
|= input=(pair @t @t)
|
||||
^- (list @t)
|
||||
[-:input +:input ~]
|
||||
::
|
||||
:: checks if input blocks cover all letters of the alphabet
|
||||
++ alphabet-check
|
||||
|= input=tape
|
||||
^- ?
|
||||
=/ i 0
|
||||
|-
|
||||
?: =(i 26)
|
||||
%.y
|
||||
?~ (find [(snag i alphabet)]~ input)
|
||||
~| "Full alphabet not found. {<(snag i alphabet)>} not in blocks" !!
|
||||
$(i +(i))
|
||||
::
|
||||
:: checks if input word has valid chaaracters. %.y means all characters are valid
|
||||
++ character-check
|
||||
|= word=@t
|
||||
^- ?
|
||||
=/ i 0
|
||||
=/ tapeword (cass (trip word))
|
||||
|-
|
||||
?: =(+(i) (lent tapeword))
|
||||
%.y
|
||||
?~ (find [(snag i tapeword)]~ alphabet)
|
||||
%.n
|
||||
$(i +(i))
|
||||
::
|
||||
:: checks if the word can be spelled using the input blocks
|
||||
++ spell-check
|
||||
|= word=@t
|
||||
^- ?
|
||||
=/ tapeword (cass (trip word))
|
||||
=/ tape-blocks tape-blocks
|
||||
=/ i 0
|
||||
=/ letter (snag i tapeword)
|
||||
|-
|
||||
?: =(+(i) (lent tapeword))
|
||||
=/ blockcheck (check-blocks [tape-blocks letter])
|
||||
?. check:blockcheck
|
||||
%.n
|
||||
%.y
|
||||
=/ blockcheck (check-blocks [tape-blocks letter])
|
||||
?. check:blockcheck
|
||||
%.n
|
||||
$(i +(i), letter (snag +(i) tapeword), tape-blocks (oust [num:blockcheck 1] tape-blocks))
|
||||
:: cycles through blocks, checking for a letter
|
||||
++ check-blocks
|
||||
|= [tape-blocks=(list tape) letter=@t]
|
||||
^- [num=@ check=?]
|
||||
=/ i 0
|
||||
=/ block (snag i tape-blocks)
|
||||
|-
|
||||
?: =(+(i) (lent tape-blocks))
|
||||
?~ (find [letter]~ block)
|
||||
[~ %.n]
|
||||
[i %.y]
|
||||
?~ (find [letter]~ block)
|
||||
$(i +(i), block (snag +(i) tape-blocks))
|
||||
[i %.y]
|
||||
--
|
||||
```
|
||||
|
||||
### Solution #3
|
||||
_By ~dannul-bortux_
|
||||
|
||||
```hoon
|
||||
!:
|
||||
|= [inlist=(list [@t @t]) inword=@t]
|
||||
^- $?(%.y %.n)
|
||||
:: If, input list is empty
|
||||
::
|
||||
?: =(0 (lent inlist))
|
||||
:: Then, throw error
|
||||
::
|
||||
~| 'Error - input list cannot be empty'
|
||||
!!
|
||||
=< (validate-input inlist (cass (trip inword)))
|
||||
|%
|
||||
++ validate-input
|
||||
|= [blocks=(list [@t @t]) cword=tape]
|
||||
=/ lblocks (to-lowercase blocks)
|
||||
?: ?& (validate-alpha-only cword)
|
||||
(validate-complete-alpha lblocks)
|
||||
(validate-word lblocks cword)
|
||||
==
|
||||
%.y
|
||||
%.n
|
||||
++ validate-alpha-only
|
||||
|= w=tape
|
||||
=/ i 0
|
||||
:: =/ tword (trip w)
|
||||
|-
|
||||
?: =(i (lent w))
|
||||
%.y
|
||||
?. ?& (gte `@ud`(snag i w) 97)
|
||||
(lte `@ud`(snag i w) 122)
|
||||
==
|
||||
!!
|
||||
%= $
|
||||
i +(i)
|
||||
==
|
||||
++ validate-complete-alpha
|
||||
|= blocks=(list [@t @t])
|
||||
=/ alphabet "abcdefghijklmnopqrstuvwxyz"
|
||||
=/ bltape (block-taper blocks)
|
||||
:: ~& "bl tape is {<bltape>}"
|
||||
:: =/ bltape "abcdefghijklmnopqrstuvwxyz"
|
||||
=/ i 0
|
||||
|-
|
||||
?: =(i (lent alphabet))
|
||||
:: ~& "returning yes"
|
||||
%.y
|
||||
?: =(~ (find (trip (snag i alphabet)) bltape))
|
||||
:: ~& "returning no at letter: {<(snag i alphabet)>}"
|
||||
!!
|
||||
%= $
|
||||
:: alphabet (remove-letters alphabet (snag i blocks))
|
||||
i +(i)
|
||||
==
|
||||
:: %.n
|
||||
:: ++ remove-letters
|
||||
:: |= [in=tape let=[@t @t]]
|
||||
:: ~& "removing letters"
|
||||
|
||||
:: in
|
||||
++ block-taper
|
||||
|= b=(list [@t @t])
|
||||
=/ i 0
|
||||
=/ bltape *tape
|
||||
|-
|
||||
?: =(i (lent b))
|
||||
bltape
|
||||
:: ~& +2:(snag i `(list [@t @t])`b)
|
||||
%= $
|
||||
bltape (snoc (snoc bltape +2:(snag i `(list [@t @t])`b)) +3:(snag i `(list [@t @t])`b))
|
||||
:: bltape (snoc bltape 'a')
|
||||
i +(i)
|
||||
==
|
||||
++ validate-word
|
||||
|= [blocks=(list [@t @t]) cword=tape]
|
||||
=/ wordcombos `(list tape)`(get-combos blocks)
|
||||
:: ~& "validating word"
|
||||
:: ~& wordcombos
|
||||
=/ i 0
|
||||
|-
|
||||
?: =(i (lent wordcombos))
|
||||
%.n
|
||||
:: ~& (snag i wordcombos)
|
||||
?: (word-compare (snag i wordcombos) cword)
|
||||
%.y
|
||||
%= $
|
||||
i +(i)
|
||||
==
|
||||
:: ?: ?& (validate-alph-aonly )
|
||||
:: (validate-complete-alpha )
|
||||
:: (validate-word )
|
||||
:: ==
|
||||
:: %.y
|
||||
:: %.n
|
||||
++ get-combos
|
||||
|= n=(list [@t @t])
|
||||
=/ i 1
|
||||
=/ outlist `(list tape)`(snoc `(list tape)`(snoc *(list tape) (trip +2:(snag 0 `(list [@t @t])`n))) (trip +3:(snag 0 `(list [@t @t])`n)))
|
||||
:: ~& outlist
|
||||
|-
|
||||
?: =(i (lent n))
|
||||
outlist
|
||||
:: ?: =(i 0)
|
||||
:: %= $
|
||||
:: outlist (snoc `(list tape)`(snoc `(list tape)`outlist (trip +2:(snag 0 `(list [@t @t])`n))) (trip +3:(snag 0 `(list [@t @t])`n)))
|
||||
:: i +(i)
|
||||
:: ==
|
||||
=/ j 0
|
||||
=/ temp *(list tape)
|
||||
|-
|
||||
?: =(j (lent outlist))
|
||||
%= ^$
|
||||
outlist temp
|
||||
i +(i)
|
||||
==
|
||||
%= $
|
||||
:: temp (snoc (snoc `(list tape)`outlist (trip +2:(snag 0 `(list [@t @t])`n))) (trip +3:(snag 0 `(list [@t @t])`n)))
|
||||
temp (snoc `(list tape)`(snoc `(list tape)`temp (snoc (snag j outlist) +2:(snag i `(list [@t @t])`n))) (snoc (snag j outlist) +3:(snag i `(list [@t @t])`n)))
|
||||
j +(j)
|
||||
==
|
||||
:: %= $
|
||||
:: i +(i)
|
||||
:: j 3
|
||||
:: ==
|
||||
++ word-compare
|
||||
|= [combo=tape cword=tape]
|
||||
=/ i 0
|
||||
:: ~& combo
|
||||
:: ~& cword
|
||||
|-
|
||||
:: ~& combo
|
||||
?: =(i (lent cword))
|
||||
%.y
|
||||
?: =(~ (find (trip (snag i cword)) combo))
|
||||
%.n
|
||||
%= $
|
||||
combo (oust [+3:(find (trip (snag i cword)) combo) 1] combo)
|
||||
i +(i)
|
||||
==
|
||||
++ to-lowercase
|
||||
|= blocks=(list [@t @t])
|
||||
=/ lcase *(list [@t @t])
|
||||
=/ i 0
|
||||
|-
|
||||
?: =(i (lent blocks))
|
||||
:: lcase
|
||||
:: ~& lcase
|
||||
lcase
|
||||
=/ m (crip (cass (trip +2:(snag i blocks))))
|
||||
=/ n (crip (cass (trip +3:(snag i blocks))))
|
||||
%= $
|
||||
lcase (snoc `(list [@t @t])`lcase [m n])
|
||||
:: lcase (snoc `(list [@t @t])`lcase ['a' 'b'])
|
||||
i +(i)
|
||||
==
|
||||
:: blocks
|
||||
:: %.n
|
||||
--
|
||||
```
|
@ -3,11 +3,9 @@ title = "Competitive Programming"
|
||||
weight = 10
|
||||
+++
|
||||
|
||||
# Competitive Programming
|
||||
|
||||
Let's take a quick look at implementations for some common tasks in Hoon. I assume that you want to use library tools whenever possible, but you can delve into the source code itself if you want to know more.
|
||||
|
||||
### Sorting
|
||||
## Sorting
|
||||
|
||||
- Given a `list` of values, sort the values according to a given rule and produce a `list`.
|
||||
|
||||
@ -44,7 +42,7 @@ If something isn't a `list`, the easiest way to sort it is to convert it to a `l
|
||||
~[1 2 3 4 5 6 7 8 9 10]
|
||||
```
|
||||
|
||||
### Bitwise Operations
|
||||
## Bitwise Operations
|
||||
|
||||
Bitwise operations are necessary when you are closely packing data into binary formats or otherwise dealing with binary data. Basically there are three scenarios:
|
||||
|
||||
@ -52,7 +50,7 @@ Bitwise operations are necessary when you are closely packing data into binary f
|
||||
2. Working with MIME-type data. Urbit has standard library support for yielding and parsing these, but it's sometimes a bit confusing where they may be located.
|
||||
3. Directly processing particular kinds of data streams, like audio or video data. On Urbit, you'll be serving these or interfacing with an external service. (Remember, Urbit is more like a server than a GPU.)
|
||||
|
||||
#### Binary Operations
|
||||
### Binary Operations
|
||||
|
||||
If you are working with packed binary data, you'll typically print the atom data with a `@ux` unsigned hexadecimal aura.
|
||||
|
||||
@ -153,7 +151,7 @@ Standard bitwise logical operations are available:
|
||||
0b1111.1111.1111.0100
|
||||
```
|
||||
|
||||
#### MIME Data Operations
|
||||
### MIME Data Operations
|
||||
|
||||
[MIME data types](https://en.wikipedia.org/wiki/MIME) allow HTTP communications to include rich content. The `++html` core in the standard library provides quite a few operations for encoding and decoding MIME-typed values.
|
||||
|
||||
@ -187,7 +185,7 @@ There are operations for JSON, [Base58](https://en.wikipedia.org/wiki/Binary-to-
|
||||
|
||||
Urbit furthermore has a notion of _jammed noun_, which is essentially a serialization (marshaling, pickling) of a noun into an atom.
|
||||
|
||||
### Primes and Factors
|
||||
## Primes and Factors
|
||||
|
||||
To calculate a prime number, the tried-and-true method is the Sieve of Eratosthenes, which is an elimination algorithm. In other words, we need to be able to calculate factors of a given positive integer. We're actually going to do an even simpler (and less efficient) method here, and leave the Sieve as an exercise to the reader.
|
||||
|
||||
@ -234,7 +232,7 @@ Now that we can find factors, it should be straightforward to find primes. In t
|
||||
|
||||
- How would you change this algorithm to the more efficient Sieve of Eratosthenes?
|
||||
|
||||
### Pragmatic Input/Output
|
||||
## Pragmatic Input/Output
|
||||
|
||||
While Hoon has a sophisticated text parsing library, the primitives are rather low-level and we won't assume that you want to directly implement a parser using them in a rapid-fire competitive environment.
|
||||
|
||||
@ -245,11 +243,11 @@ Fortunately, there is a regular expression library you can incorporate into your
|
||||
|
||||
- https://github.com/lynko/re.hoon
|
||||
|
||||
### Functional Operators
|
||||
## Functional Operators
|
||||
|
||||
Hoon is a functional programming language, so naturally it supports the basic collective operations which functional programming languages expect.
|
||||
|
||||
#### Curry
|
||||
### Curry
|
||||
|
||||
[_Currying_](https://en.wikipedia.org/wiki/Currying) describes taking a function of multiple arguments and reducing it to a set of functions that each take only one argument. _Binding_, an allied process, is used to set the value of some of those arguments permanently. Hoon has a left-bind `++cury` and a right-bind `++curr`.
|
||||
|
||||
@ -265,7 +263,7 @@ Hoon is a functional programming language, so naturally it supports the basic co
|
||||
117
|
||||
```
|
||||
|
||||
#### Map
|
||||
### Map
|
||||
|
||||
The Map operation describes applying a function to each item of a set or iterable object, resulting in the same final number of items transformed. In Hoon terms, we would say slamming a gate on each member of a `list` or `set`. The standard library arms that accomplish this include [`++turn`](https://developers.urbit.org/reference/hoon/stdlib/2b#turn) for a `list`, [`++run:in`](https://developers.urbit.org/reference/hoon/stdlib/2h#repin) for a `set`, and [`++run:by`](https://developers.urbit.org/reference/hoon/stdlib/2i#runby) for a `map`.
|
||||
|
||||
@ -273,7 +271,7 @@ The Map operation describes applying a function to each item of a set or iterabl
|
||||
> (turn `(list @ud)`~[1 2 3 4 5] one)
|
||||
```
|
||||
|
||||
#### Reduce
|
||||
### Reduce
|
||||
|
||||
The Reduce operation applies a function as a sequence of pairwise operations to each item, resulting in one summary value. The standard library arms that accomplish this are [`++roll`](https://developers.urbit.org/reference/hoon/stdlib/2b#roll) and [`++reel`](https://developers.urbit.org/reference/hoon/stdlib/2b#reel) for a `list`, [`++rep:in`](https://developers.urbit.org/reference/hoon/stdlib/2h#repin) for a `set`, and [`++rep:by`](https://developers.urbit.org/reference/hoon/stdlib/2i#repby) for a `map`.
|
||||
|
||||
@ -284,7 +282,7 @@ The Reduce operation applies a function as a sequence of pairwise operations to
|
||||
b=120
|
||||
```
|
||||
|
||||
#### Filter
|
||||
### Filter
|
||||
|
||||
The Filter operation applies a true/false function to each member of a collection, resulting in some number of items equal to or fewer than the size of the original set. In Hoon, the library arms that carry this out include [`++skim`](https://developers.urbit.org/reference/hoon/stdlib/2b#skim), [`++skid`](https://developers.urbit.org/reference/hoon/stdlib/2b#skid), [`++murn`](https://developers.urbit.org/reference/hoon/stdlib/2b#murn) for a `list`, and [`++rib:by`](https://developers.urbit.org/reference/hoon/stdlib/2i#ribby) for a `map`.
|
||||
|
||||
@ -297,7 +295,7 @@ An interesting feature of Hoon is that it really prefers functions-that-produce-
|
||||
|
||||
- [Functional Programming](https://developers.urbit.org/guides/core/hoon-school/Q-func) - This module will discuss some gates-that-work-on-gates and other assorted operators that are commonly recognized as functional programming tools.
|
||||
|
||||
### Floating-Point Operations
|
||||
## Floating-Point Operations
|
||||
|
||||
`@ud` unsigned decimal operations are, of course, postive-integer-only. For floating-point maths, you will need to work with `@rs` for 32-bit single-precision IEEE 754 floating-point atoms. These are prefixed with a single `.` which is _not_ a decimal point.
|
||||
|
||||
@ -332,7 +330,7 @@ Equivalent mathematical operations for `@rs` values are available in the `++rs`
|
||||
|
||||
(I picked the above set of examples after perusing the excellent book [Antti Laaksonen (2017) _Guide to Competitive Programming: Learning and Improving Algorithms Through Contests_](https://link.springer.com/book/10.1007/978-3-319-72547-5).)
|
||||
|
||||
### Debugging and Troubleshooting
|
||||
## Debugging and Troubleshooting
|
||||
|
||||
Static typing with compile-time type checking turns out to be a secret strength of Hoon. Once you've satisfied the typechecker, your code is often surprisingly free of bugs (compared to e.g. Python).
|
||||
|
||||
|
@ -3,7 +3,7 @@ title = "Gleichniszahlenreihe"
|
||||
weight = 30
|
||||
+++
|
||||
|
||||
# Challenge: The Look-and-Say Sequence
|
||||
## Challenge: The Look-and-Say Sequence
|
||||
|
||||
_Gleichniszahlenreihe_, or the [look-and-say sequence](https://en.wikipedia.org/wiki/Look-and-say_sequence), is constructed from an aural description of a sequence of numbers.
|
||||
|
||||
@ -13,7 +13,7 @@ This is a fairly complicated program. You need a few parts: the ability to tak
|
||||
|
||||
- Compose a `%say` generator which carries out the look-and-say sequence calculation for a given input. The input should be a number which indicates which value in the sequence is desired (e.g. 1→1, 2→11, 3→21).
|
||||
|
||||
## Solutions
|
||||
## Solutions
|
||||
|
||||
_These solutions were submitted by the Urbit community as part of the Hoon School Live ~2022.2 cohort. They are made available under both the [MIT license](https://mit-license.org/) and the [CC0 license](https://creativecommons.org/share-your-work/public-domain/cc0). We ask you to acknowledge authorship should you utilize these elsewhere._
|
||||
|
||||
|
574
content/guides/additional/workbook/luhn-number.md
Normal file
574
content/guides/additional/workbook/luhn-number.md
Normal file
@ -0,0 +1,574 @@
|
||||
+++
|
||||
title = "Luhn Number"
|
||||
weight = 128
|
||||
+++
|
||||
|
||||
## Challenge: Luhn Number
|
||||
|
||||
The Luhn test is used by some credit card companies to distinguish valid credit card numbers from what could be a random selection of digits.
|
||||
|
||||
A Luhn number is a sequence of digits that passes the following test:
|
||||
|
||||
1. Reverse the order of the digits.
|
||||
2. Take the first, third, and every odd-numbered digit in the reversed digits and sum them to form `s1`
|
||||
3. Taking the second, fourth, and every even-numbered digit in the reversed digits:
|
||||
1. Multiply each by two. Within each doubled digit, sum those digits (if the answer is greater than nine) to form partial sums.
|
||||
2. Sum the partial sums of the even digits to form `s2`
|
||||
4. If `s1 + s2` ends in zero then the original number is in the form of a valid credit card number as verified by the Luhn test.
|
||||
|
||||
For example, if the trial number is 49927398716:
|
||||
|
||||
```
|
||||
Reverse the digits:
|
||||
61789372994
|
||||
Sum the odd digits:
|
||||
6 + 7 + 9 + 7 + 9 + 4 = 42 = s1
|
||||
The even digits:
|
||||
1, 8, 3, 2, 9
|
||||
Two times each even digit:
|
||||
2, 16, 6, 4, 18
|
||||
Sum the digits of each multiplication:
|
||||
2, 7, 6, 4, 9
|
||||
Sum the last:
|
||||
2 + 7 + 6 + 4 + 9 = 28 = s2
|
||||
|
||||
s1 + s2 = 70 ends in zero, which means that 49927398716 passes the Luhn test
|
||||
```
|
||||
|
||||
Your task for this challenge is as follows. First you will write a library file `lib/luhn-number` with a core containing an arm named `++validate`. `validate` will be a gate that takes as input a `tape` which is a sequence of digits, and returns either a `%.y` or `%.n` if the number is a Luhn number or not.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
> =ln -build-file %/lib/luhn-number/hoon
|
||||
> (validate:ln "49927398716")
|
||||
%.y
|
||||
> (validate:ln "1234")
|
||||
%.n
|
||||
```
|
||||
|
||||
Next you will write a generator file `gen/luhn-number` which takes as input a `tape` which consists of digits or the `*` character, such as:
|
||||
```
|
||||
"*1*25**574*18403"
|
||||
"****"
|
||||
"584"
|
||||
```
|
||||
|
||||
It will return a `(list tape)` which contains all of the Luhn numbers that fit that format. The numbers should be in lexicographic order (smallest to largest by first digit, then second digit, and so on). You may choose to import and use your `++validate` arm, or perhaps use some other strategy.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
> +luhn-number "**123"
|
||||
["01123" "15123" "20123" "39123" "44123" "58123" "63123" "77123" "82123" "96123" ~]
|
||||
|
||||
> +luhn-number "123"
|
||||
~
|
||||
|
||||
> +luhn-number "49927398716"
|
||||
[49927398716 ~]
|
||||
```
|
||||
|
||||
Some notes:
|
||||
* We take the input as a `tape` rather than a `@ud` because a potential credit card number can have leading zeros.
|
||||
|
||||
* Note that in Hoon, we index starting from 0 -- so the first digit will be in the 0th index, second in 1st index, and so on.
|
||||
|
||||
* This website may be of use for both checking if a number is Luhn and generating a list from missing digits: https://www.dcode.fr/luhn-algorithm
|
||||
|
||||
* Don't worry about numbers with less than 2 digits, or improperly formatted input (with letters and spaces etc.). You can assume that the input tape will have the correct format.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior.
|
||||
|
||||
```hoon
|
||||
/+ *test
|
||||
/+ ln=luhn-number
|
||||
/= luhn-number /gen/luhn-number
|
||||
|%
|
||||
:: test valid numbers
|
||||
::
|
||||
++ test-01
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "49927398716")
|
||||
++ test-02
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "1234567812345670")
|
||||
++ test-03
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "4417123456789105")
|
||||
++ test-04
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "20210917131347022")
|
||||
++ test-05
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "1806040794512")
|
||||
++ test-06
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "9856849794512")
|
||||
++ test-07
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "5995841300227")
|
||||
++ test-08
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "00")
|
||||
++ test-09
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "34")
|
||||
++ test-10
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "00005991")
|
||||
++ test-11
|
||||
%+ expect-eq
|
||||
!> %.y
|
||||
!> (validate:ln "02310568590238405")
|
||||
:: test invalid numbers
|
||||
::
|
||||
++ test-12
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "1234")
|
||||
++ test-13
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "92")
|
||||
++ test-14
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "00001463")
|
||||
++ test-15
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "754717798")
|
||||
++ test-16
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "507274573")
|
||||
++ test-17
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "2342352356198234238")
|
||||
++ test-18
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "02310568590238406")
|
||||
++ test-19
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "5019876543217144")
|
||||
++ test-20
|
||||
%+ expect-eq
|
||||
!> %.n
|
||||
!> (validate:ln "220743131719012023")
|
||||
:: test number generation
|
||||
::
|
||||
++ test-21
|
||||
%+ expect-eq
|
||||
!> `(list tape)`["01123" "15123" "20123" "39123" "44123" "58123" "63123" "77123" "82123" "96123" ~]
|
||||
!> (luhn-number "**123")
|
||||
++ test-22
|
||||
%+ expect-eq
|
||||
!> `(list tape)`~
|
||||
!> (luhn-number "123")
|
||||
++ test-23
|
||||
%+ expect-eq
|
||||
!> `(list tape)`["12345690" ~]
|
||||
!> (luhn-number "12345690")
|
||||
++ test-24
|
||||
%+ expect-eq
|
||||
!> `(list tape)`["023259872" "123259871" "223259870" "323259879" "423259878" "523259877" "623259876" "723259875" "823259874" "923259873" ~]
|
||||
!> (luhn-number "*2325987*")
|
||||
++ test-25
|
||||
%+ expect-eq
|
||||
!> `(list tape)`["845927593912820" ~]
|
||||
!> (luhn-number "8459275*3912820")
|
||||
++ test-26
|
||||
%+ expect-eq
|
||||
!> `(list tape)`["00" "18" "26" "34" "42" "59" "67" "75" "83" "91" ~]
|
||||
!> (luhn-number "**")
|
||||
++ test-27
|
||||
%+ expect-eq
|
||||
!> `(list tape)`["4002" "4192" "4242" "4382" "4432" "4572" "4622" "4762" "4812" "4952" ~]
|
||||
!> (luhn-number "4**2")
|
||||
++ test-28
|
||||
%+ expect-eq
|
||||
!> `(list tape)`["10017" "10157" "10207" "10397" "10447" "10587" "10637" "10777" "10827" "10967" "11007" "11197" "11247" "11387" "11437" "11577" "11627" "11767" "11817" "11957" "12047" "12187" "12237" "12377" "12427" "12567" "12617" "12757" "12807" "12997" "13037" "13177" "13227" "13367" "13417" "13557" "13607" "13797" "13847" "13987" "14027" "14167" "14217" "14357" "14407" "14597" "14647" "14787" "14837" "14977" "15057" "15107" "15297" "15347" "15487" "15537" "15677" "15727" "15867" "15917" "16097" "16147" "16287" "16337" "16477" "16527" "16667" "16717" "16857" "16907" "17087" "17137" "17277" "17327" "17467" "17517" "17657" "17707" "17897" "17947" "18077" "18127" "18267" "18317" "18457" "18507" "18697" "18747" "18887" "18937" "19067" "19117" "19257" "19307" "19497" "19547" "19687" "19737" "19877" "19927" ~]
|
||||
!> (luhn-number "1***7")
|
||||
--
|
||||
```
|
||||
|
||||
## Solutions
|
||||
|
||||
_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere._
|
||||
|
||||
### Solution #1
|
||||
|
||||
_By ~dozreg-toplud._
|
||||
|
||||
`lib/luhn-number.hoon`
|
||||
```hoon
|
||||
:: lib/luhn-number.hoon
|
||||
:: Library for HSL challenge #3
|
||||
::
|
||||
|%
|
||||
:: ++validate: gate defined in the challenge
|
||||
::
|
||||
++ validate
|
||||
|= a=tape
|
||||
^- ?
|
||||
=. a (flop a)
|
||||
=/ a-digits=(list @) (turn a (cury slav %ud))
|
||||
=/ s1=@ (roll (skim-odd a-digits) add)
|
||||
=; s2=@
|
||||
=(0 (mod (add s1 s2) 10))
|
||||
%+ roll
|
||||
(skim-even a-digits)
|
||||
:: (gate that adds digits of 2*i to the accumulator)
|
||||
::
|
||||
|= [i=@ acc=@]
|
||||
=+ i2=(mul i 2)
|
||||
:(add acc (div i2 10) (mod i2 10))
|
||||
:: ++skim-odd: return elements of a list with odd indices (1-indexed)
|
||||
::
|
||||
++ skim-odd
|
||||
|* a=(list)
|
||||
^+ a
|
||||
?~ a
|
||||
~
|
||||
?~ t.a
|
||||
~[i.a]
|
||||
[i.a $(a t.t.a)]
|
||||
:: ++skim-even: return elements of a list with even indices (1-indexed)
|
||||
::
|
||||
++ skim-even
|
||||
|* a=(list)
|
||||
^+ a
|
||||
?: |(?=(~ a) ?=(~ t.a))
|
||||
~
|
||||
[i.t.a $(a t.t.a)]
|
||||
::
|
||||
--
|
||||
```
|
||||
|
||||
`gen/luhn-number.hoon`
|
||||
```hoon
|
||||
:: gen/luhn-number.hoon
|
||||
:: Naked generator for HSL challenge #3
|
||||
::
|
||||
/+ *luhn-number
|
||||
::
|
||||
|= a=tape
|
||||
^- (list tape)
|
||||
=* this-gate ..$
|
||||
=/ index-tar=(unit @) (find "*" a)
|
||||
:: if no * in `a`,
|
||||
::
|
||||
?~ index-tar
|
||||
:: then return empty list if `a` is not a Luhn number, else return list
|
||||
:: with `a`
|
||||
::
|
||||
?. (validate a)
|
||||
~
|
||||
~[a]
|
||||
:: else, replace first * with a digit, call this gate for each digit 0-9,
|
||||
:: weld the results
|
||||
::
|
||||
=/ dry-snap ^~((bake snap ,[tape @ char]))
|
||||
%- zing
|
||||
%+ turn
|
||||
"0123456789"
|
||||
(cork (cury (cury dry-snap a) u.index-tar) this-gate)
|
||||
```
|
||||
|
||||
### Solution #2
|
||||
|
||||
_By ~pardun-nollev._
|
||||
|
||||
`lib/luhn-number.hoon`
|
||||
```hoon
|
||||
|%
|
||||
++ validate
|
||||
|= input=tape
|
||||
=/ input (flop input)
|
||||
=(0 (mod (add (get-s1 input) (get-s2 input)) 10))
|
||||
++ get-s1
|
||||
|= input=tape
|
||||
^- @ud
|
||||
(roll (odd-digits input) add)
|
||||
++ get-s2
|
||||
|= input=tape
|
||||
^- @ud
|
||||
(roll (multiply-digits (convert-digits-to-text (double-digits input))) add)
|
||||
:: take a tape
|
||||
:: convert to @ud and sum
|
||||
++ sum-digits
|
||||
|= input=tape
|
||||
^- @ud
|
||||
(roll (turn input |=(a=@t (rash a dem))) add)
|
||||
:: take list of tapes
|
||||
:: convert to digits
|
||||
:: multiply each list of digits
|
||||
++ multiply-digits
|
||||
|= input=(list tape)
|
||||
^- (list @ud)
|
||||
(turn input |=(a=tape (sum-digits a)))
|
||||
:: take each number
|
||||
:: convert to tape
|
||||
++ convert-digits-to-text
|
||||
|= input=(list @ud)
|
||||
^- (list tape)
|
||||
(turn input |=(a=@ud (scow %ud a)))
|
||||
:: get even digits and multiply by two
|
||||
++ double-digits
|
||||
|= input=tape
|
||||
^- (list @ud)
|
||||
(turn (even-digits input) |=(a=@ud (mul a 2)))
|
||||
++ get-element
|
||||
|= [idx=@ud input=tape]
|
||||
^- tape
|
||||
?: (gte (lent input) +(idx))
|
||||
`tape`~[(snag idx input)]
|
||||
~
|
||||
++ odd-digits
|
||||
|= input=tape
|
||||
^- (list @ud)
|
||||
=/ output=tape ~
|
||||
|-
|
||||
?: =(input ~)
|
||||
(turn output |=(a=@t (rash a dem)))
|
||||
%= $
|
||||
output (weld output (get-element 0 input))
|
||||
input (oust [0 2] input)
|
||||
==
|
||||
++ even-digits
|
||||
|= input=tape
|
||||
^- (list @ud)
|
||||
=/ output=tape ~
|
||||
|-
|
||||
?: =(input ~)
|
||||
(turn output |=(a=@t (rash a dem)))
|
||||
%= $
|
||||
output (weld output (get-element 1 input))
|
||||
input (oust [0 2] input)
|
||||
==
|
||||
--
|
||||
```
|
||||
|
||||
`gen/luhn-number.hoon`
|
||||
|
||||
```hoon
|
||||
/+ luhn-number
|
||||
|= input=tape
|
||||
=<
|
||||
(skim `(list tape)`(get-permutations input) validate:luhn-number)
|
||||
|%
|
||||
++ digits "0123456789"
|
||||
++ get-permutations
|
||||
|= input=tape
|
||||
=/ output=(list tape) ~[input]
|
||||
=/ idx 0
|
||||
|-
|
||||
?: =(idx (lent input))
|
||||
output
|
||||
%= $
|
||||
output (churn-numbers idx output)
|
||||
idx +(idx)
|
||||
==
|
||||
++ churn-numbers
|
||||
|= [idx=@ud input=(list tape)]
|
||||
^- (list tape)
|
||||
(zing (turn input |=(a=tape (generate-perms idx a))))
|
||||
++ generate-perms
|
||||
|= [idx=@ud input=tape]
|
||||
^- (list tape)
|
||||
?: =((snag idx input) '*')
|
||||
(turn digits |=(a=@t (snap input idx a)))
|
||||
~[input]
|
||||
--
|
||||
```
|
||||
|
||||
### Solution #3
|
||||
|
||||
_By ~motdeg-bintul_
|
||||
|
||||
`lib/luhn-number`
|
||||
|
||||
```hoon
|
||||
:: lib/luhn-number.hoon
|
||||
:: Your code goes here
|
||||
::
|
||||
|%
|
||||
++ validate
|
||||
|= a=tape
|
||||
&(=((checkdits a) 0) (gth (lent a) 0))
|
||||
++ checkdits
|
||||
|= a=tape
|
||||
=/ totalsum (add (s1 a) (s2 a))
|
||||
=/ sumtape (trip `@t`(scot %ud totalsum))
|
||||
=/ digits (scan sumtape (star dit))
|
||||
:: ~& (odds a)
|
||||
:: ~& (doubler a)
|
||||
:: ~& `(list @)`(getsums (doubler a))
|
||||
:: ~& (s1 a)
|
||||
:: ~& (s2 a)
|
||||
:: ~& totalsum
|
||||
?: (lte totalsum 9)
|
||||
+2:digits
|
||||
(snag (sub (lent +3:digits) 1) `(list @ud)`+3:digits)
|
||||
++ odds
|
||||
|= a=tape
|
||||
=/ reverse (flop a)
|
||||
=/ count 0
|
||||
|-
|
||||
^- (list @ud)
|
||||
|-
|
||||
?: (gte count (lent reverse))
|
||||
~
|
||||
:- (scan (trip (snag count reverse)) dit)
|
||||
$(count (add 2 count))
|
||||
++ s1
|
||||
|= a=tape
|
||||
(roll `(list @ud)`(odds a) add)
|
||||
++ evens
|
||||
|= a=tape
|
||||
=/ reverse (flop a)
|
||||
=/ count 1
|
||||
|-
|
||||
^- (list @ud)
|
||||
|-
|
||||
?: (gte count (lent reverse))
|
||||
~
|
||||
:- (scan (trip (snag count reverse)) dit)
|
||||
$(count (add 2 count))
|
||||
++ double
|
||||
|= [a=@]
|
||||
(mul 2 a)
|
||||
++ doubler
|
||||
|= a=tape
|
||||
(turn `(list @ud)`(evens a) double)
|
||||
++ adddit
|
||||
|= [a=(list @ud) b=@ud]
|
||||
=/ list1 a
|
||||
=/ list2 `(list @t)`(turn list1 (cury scot %ud))
|
||||
=/ count b
|
||||
=/ digits (scan (trip (snag count list2)) (star dit))
|
||||
=/ d1 (snag 0 digits)
|
||||
?: =((lent digits) 1)
|
||||
`@ud`d1
|
||||
?: (gth (lent digits) 1)
|
||||
`@ud`(add d1 (snag 1 digits))
|
||||
~
|
||||
++ getsums
|
||||
|= a=(list @ud)
|
||||
=/ nums a
|
||||
=/ count 0
|
||||
|-
|
||||
?: (lth count (lent nums))
|
||||
:- (adddit nums count)
|
||||
$(count (add 1 count))
|
||||
?: =(count (lent nums))
|
||||
~
|
||||
$(count (add 1 count))
|
||||
++ s2
|
||||
|= a=tape
|
||||
=/ nums (doubler a)
|
||||
(roll `(list @)`(getsums nums) add)
|
||||
--
|
||||
```
|
||||
|
||||
`gen/luhn-number`
|
||||
|
||||
```hoon
|
||||
:: gen/luhn-number.hoon
|
||||
:: Your code goes here
|
||||
::
|
||||
/= ln /lib/luhn-number
|
||||
|= a=tape
|
||||
=<
|
||||
(checkmissing a)
|
||||
|%
|
||||
++ missingnums
|
||||
|= a=tape
|
||||
=/ count 0
|
||||
|-
|
||||
?: =(count (lent a))
|
||||
~
|
||||
?: =((snag count a) '*')
|
||||
:- count
|
||||
$(count (add 1 count))
|
||||
?: =(count (sub (lent a) 1))
|
||||
~
|
||||
$(count (add 1 count))
|
||||
++ replaceast
|
||||
|= a=tape
|
||||
=/ pos `(list @)`(missingnums a)
|
||||
=/ count 0
|
||||
=/ newtape a
|
||||
=/ num `@t`(scot %ud 0)
|
||||
|-
|
||||
?: =(count (sub (lent pos) 1))
|
||||
`(list @t)`(snap newtape (snag count pos) num)
|
||||
%= $
|
||||
newtape (snap newtape (snag count pos) num)
|
||||
count (add count 1)
|
||||
==
|
||||
++ replacedigits
|
||||
|= [a=tape b=@ud]
|
||||
=/ count 0
|
||||
=/ dits (trip (crip (replaceast a)))
|
||||
=/ newdits (a-co:co b)
|
||||
=/ flopdits (flop newdits)
|
||||
=/ indexcap (sub (lent flopdits) 1)
|
||||
=/ pos (flop `(list @ud)`(missingnums a))
|
||||
=/ newnum `tape`dits
|
||||
|-
|
||||
?: =(count (lent newdits))
|
||||
newnum
|
||||
%= $
|
||||
newnum `tape`(snap newnum (snag count pos) (snag count flopdits))
|
||||
count (add 1 count)
|
||||
==
|
||||
++ testnewnum
|
||||
|= a=tape
|
||||
=/ format a
|
||||
=/ count 0
|
||||
=/ countdit (a-co:co count)
|
||||
=/ newnum `tape`~
|
||||
=/ pos `(list @ud)`(missingnums format)
|
||||
=/ dgtlent (lent pos)
|
||||
|-
|
||||
^- (list tape)
|
||||
?: &(=((lent (a-co:co count)) (add 1 dgtlent)) =((validate:ln newnum) %.y))
|
||||
[newnum ~]
|
||||
?: =((lent (a-co:co count)) (add 1 dgtlent))
|
||||
~
|
||||
?: =((validate:ln newnum) %.y)
|
||||
:- newnum
|
||||
%= $
|
||||
count (add 1 count)
|
||||
newnum (replacedigits format count)
|
||||
countdit (a-co:co count)
|
||||
==
|
||||
?: =((lent countdit) (add 1 dgtlent))
|
||||
~
|
||||
%= $
|
||||
count (add 1 count)
|
||||
newnum (replacedigits format count)
|
||||
countdit (trip `@t`(scot %ud count))
|
||||
==
|
||||
++ checkmissing
|
||||
|= a=tape
|
||||
?: &(=((missingnums a) ~) =((validate:ln a) %.y))
|
||||
`(list tape)`[a ~]
|
||||
(testnewnum a)
|
||||
--
|
||||
```
|
@ -3,7 +3,7 @@ title = "Rhonda Numbers"
|
||||
weight = 48
|
||||
+++
|
||||
|
||||
# Challenge: Rhonda Numbers
|
||||
## Challenge: Rhonda Numbers
|
||||
|
||||
A Rhonda number is a positive integer _n_ that satisfies the property that, for [a given base _b_](https://en.wikipedia.org/wiki/Radix), the product of the base-_b_ digits of _n_ is equal to _b_ times the sum of _n_'s prime factors. Only composite bases (non-prime bases) have Rhonda numbers.
|
||||
|
||||
|
@ -3,7 +3,7 @@ title = "Roman Numerals"
|
||||
weight = 50
|
||||
+++
|
||||
|
||||
# Challenge: Printing and Parsing Roman Numerals
|
||||
## Challenge: Printing and Parsing Roman Numerals
|
||||
|
||||
Roman numerals constitute a numeral system capable of expressing positive integers by additive values (rather than place-number notation). Additive series are produced by summing values in a series, as `iii` → 3, while subtractive values are produced by prepending certain smaller values ahead of a larger value, as `ix` → 9.
|
||||
|
||||
@ -36,7 +36,7 @@ Roman numerals constitute a numeral system capable of expressing positive intege
|
||||
|
||||
**Note**: This design pattern is not optimal since analysis over a union of some types can be difficult to carry out, and it would be better to either separate the generators or use a flag. In this case, the pattern works because we are distinguishing an atom from a cell.
|
||||
|
||||
## Unit Tests
|
||||
## Unit Tests
|
||||
|
||||
Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior.
|
||||
|
||||
@ -866,7 +866,7 @@ Following a principle of test-driven development, we compose a series of tests w
|
||||
--
|
||||
```
|
||||
|
||||
## Solutions
|
||||
## Solutions
|
||||
|
||||
_These solutions were submitted by the Urbit community as part of a competition in ~2022.6. They are made available under both the [MIT license](https://mit-license.org/) and the [CC0 license](https://creativecommons.org/share-your-work/public-domain/cc0). We ask you to acknowledge authorship should you utilize these elsewhere._
|
||||
|
||||
|
@ -3,9 +3,7 @@ title = "Solitaire Cipher"
|
||||
weight = 60
|
||||
+++
|
||||
|
||||
# Solitaire Cipher
|
||||
|
||||
## Challenge: Solitaire Encryption Cipher
|
||||
## Challenge: Solitaire Encryption Cipher
|
||||
|
||||
The [Solitaire or Pontifex algorithm](https://en.wikipedia.org/wiki/Solitaire_%28cipher%29) is a cryptographic algorithm designed by cryptographer [Bruce Schneier](https://www.schneier.com/academic/solitaire/) based on coordinating two decks of cards so that they can be used to communicate between two field agents. Given a standard deck of 52 playing cards and two distinguishable jokers, a message may be encrypted as a keystream, or sequence of values combined with the message to encrypt or decrypt it. The algorithm features prominently in Neal Stephenson's novel _Cryptonomicon_.
|
||||
|
||||
@ -43,7 +41,7 @@ To generate one character:
|
||||
The foregoing hyperlinks showcase worked examples of Solitaire in action.
|
||||
|
||||
|
||||
## Solutions
|
||||
## Solutions
|
||||
|
||||
_This solution was produced by ~rabsef-bicrym. It is made available under the [GNU GPL](https://github.com/rabsef-bicrym/urbitasofia/blob/master/LICENSE). (Note that this is different from the other code snippets on this site, which are made available under the [MIT license](https://mit-license.org/)._
|
||||
|
||||
|
381
content/guides/additional/workbook/water-towers.md
Normal file
381
content/guides/additional/workbook/water-towers.md
Normal file
@ -0,0 +1,381 @@
|
||||
+++
|
||||
title = "Water Towers"
|
||||
weight = 230
|
||||
+++
|
||||
|
||||
## Challenge: Water between Towers
|
||||
|
||||
In a two-dimensional world, we begin with a bar-chart, or rows of unit-width 'towers' of arbitrary height. Then it rains, completely filling all convex enclosures in the chart with water.
|
||||
|
||||
```
|
||||
9 ██ 9 ██
|
||||
8 ██ 8 ██
|
||||
7 ██ ██ 7 ██≈≈≈≈≈≈≈≈██
|
||||
6 ██ ██ ██ 6 ██≈≈██≈≈≈≈██
|
||||
5 ██ ██ ██ ████ 5 ██≈≈██≈≈██≈≈████
|
||||
4 ██ ██ ████████ 4 ██≈≈██≈≈████████
|
||||
3 ██████ ████████ 3 ██████≈≈████████
|
||||
2 ████████████████ ██ 2 ████████████████≈≈██
|
||||
1 ████████████████████ 1 ████████████████████
|
||||
```
|
||||
|
||||
Your task for this challenge is to write a generator `water-towers`. It will take as input a `(list @ud)`, with each number representing the height of a tower from left to right. It will output a `@ud` representing the units of water that can be contained within the structure.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
> +water-towers [5 3 7 2 6 4 5 9 1 2 ~]
|
||||
14
|
||||
```
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Following a principle of test-driven development, we compose a series of tests which allow us to rigorously check for expected behavior.
|
||||
|
||||
```hoon
|
||||
/+ *test
|
||||
/= water-towers /gen/water-towers
|
||||
|%
|
||||
++ test-01
|
||||
%+ expect-eq
|
||||
!> `@ud`2
|
||||
!> (water-towers [1 5 3 7 2 ~])
|
||||
++ test-02
|
||||
%+ expect-eq
|
||||
!> `@ud`14
|
||||
!> (water-towers [5 3 7 2 6 4 5 9 1 2 ~])
|
||||
++ test-03
|
||||
%+ expect-eq
|
||||
!> `@ud`35
|
||||
!> (water-towers [2 6 3 5 2 8 1 4 2 2 5 3 5 7 4 1 ~])
|
||||
++ test-04
|
||||
%+ expect-eq
|
||||
!> `@ud`0
|
||||
!> (water-towers [5 5 5 5 ~])
|
||||
++ test-05
|
||||
%+ expect-eq
|
||||
!> `@ud`0
|
||||
!> (water-towers [5 6 7 8 ~])
|
||||
++ test-06
|
||||
%+ expect-eq
|
||||
!> `@ud`0
|
||||
!> (water-towers [8 7 7 6 5 4 3 2 ~])
|
||||
++ test-07
|
||||
%+ expect-eq
|
||||
!> `@ud`0
|
||||
!> (water-towers [0 1 6 7 10 7 6 1 0 ~])
|
||||
++ test-08
|
||||
%+ expect-eq
|
||||
!> `@ud`0
|
||||
!> (water-towers [100 0 0 0 0 0 0 0 ~])
|
||||
++ test-09
|
||||
%+ expect-eq
|
||||
!> `@ud`7
|
||||
!> (water-towers [100 0 0 0 0 0 0 0 1 ~])
|
||||
++ test-10
|
||||
%+ expect-eq
|
||||
!> `@ud`50
|
||||
!> (water-towers [10 0 0 0 0 0 10 ~])
|
||||
++ test-11
|
||||
%+ expect-eq
|
||||
!> `@ud`4
|
||||
!> (water-towers [8 7 8 7 8 7 8 7 8 ~])
|
||||
++ test-12
|
||||
%+ expect-eq
|
||||
!> `@ud`40
|
||||
!> (water-towers [0 1 2 3 4 5 4 3 2 1 1 2 3 4 5 4 3 2 1 1 2 3 4 5 4 3 2 1 0 ~])
|
||||
--
|
||||
```
|
||||
|
||||
## Solutions
|
||||
|
||||
_These solutions were submitted by the Urbit community as part of a competition in ~2023.6. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere._
|
||||
|
||||
### Solution #1
|
||||
|
||||
_By ~dannul-bortux. A model for literate programming in Hoon._
|
||||
|
||||
```hoon
|
||||
::
|
||||
:: A gate for computing volume of water collected between towers.
|
||||
::
|
||||
:: Take a list (of type list @ud), with each value representing the height of
|
||||
:: a tower from left to right. Outputs a @ud representing the units of water
|
||||
:: that can be contained within the structure.
|
||||
::
|
||||
:: Our approach involves calculating the total volume of rainfall or water by
|
||||
:: aggregating the water volume from each tower location. For a specific
|
||||
:: tower location. water volume is determined by subtracting the “height”
|
||||
:: of the tower with maximum rainfall (“total height with water”) from the
|
||||
:: height of the tower alone. Tower heights are given by corresponding values
|
||||
:: in the input list.
|
||||
::
|
||||
:: The “total height with water” at a location is determined by the height of
|
||||
:: surrounding boundary towers within our structure. Each tower location will
|
||||
:: have at most two boundary towers: one boundary tower on either side (left
|
||||
:: and right). The left boundary tower is defined as the highest tower to the
|
||||
:: left of our specified tower location. The right boundary tower is defined
|
||||
:: as the highest tower to the right of our specified tower location. The
|
||||
:: value of “total height with water” at a location is equal to the lesser of
|
||||
:: the two boundary tower heights (the minimum of the left boundary tower
|
||||
:: height vs. right boundary tower height). When less than two boundary
|
||||
:: towers are present, the “total height with water” is equal to the height
|
||||
:: of the tower itself because no water can be contained without boundaries.
|
||||
::
|
||||
|= inlist=(list @ud)
|
||||
^- @ud
|
||||
:: If, input list is empty
|
||||
::
|
||||
?: =(0 (lent inlist))
|
||||
:: Then, throw error
|
||||
::
|
||||
~| 'Error - input list cannot be empty'
|
||||
!!
|
||||
=< (compute-totalvol inlist)
|
||||
|%
|
||||
::
|
||||
:: +compute-totalvol: Gets total volume of water by summing water at each
|
||||
:: individual location.
|
||||
::
|
||||
:: Moves left to right iterating over each location (index in list).
|
||||
:: Determines waterfall at each location and aggregates all waterfall to
|
||||
:: find and return total volume.
|
||||
::
|
||||
++ compute-totalvol
|
||||
|= [n=(list @ud)]
|
||||
^- @ud
|
||||
:: i is face for iterating over all index/locations
|
||||
::
|
||||
=/ i 0
|
||||
:: tot is face for aggregating volume of water
|
||||
::
|
||||
=/ tot 0
|
||||
|-
|
||||
:: If, we're at end of input list
|
||||
::
|
||||
?: =(i (lent n))
|
||||
:: then, return total
|
||||
::
|
||||
tot
|
||||
:: else, compute water volume at current index, add to total, and increment i
|
||||
::
|
||||
%= $
|
||||
tot (add tot (compute-indvol i n))
|
||||
i +(i)
|
||||
==
|
||||
::
|
||||
:: +compute-indvol: Computes volume at an individual location.
|
||||
::
|
||||
:: Computes volume at an individual location (index in input list) by
|
||||
:: subtracting tower height from “total height with water”. “Total height
|
||||
:: with water” will be determined at a particular location by the height of
|
||||
:: “boundary towers” for that location.
|
||||
::
|
||||
++ compute-indvol
|
||||
|= [loc=@ud n=(list @ud)]
|
||||
^- @ud
|
||||
(sub (compute-waterheight loc n) (snag loc `(list @ud)`n))
|
||||
::
|
||||
:: +compute-waterheight: Measures the “total height with water” at a specified
|
||||
:: index/location.
|
||||
::
|
||||
:: “Total height with water” at a particular location is measured using the
|
||||
:: heights (value) at the left and right boundary towers. The lesser of these
|
||||
:: two values (left height vs right height) is equal to the “total height
|
||||
:: with water” at our input location.
|
||||
::
|
||||
:: Right boundary tower is the tallest tower to the right of the location--
|
||||
:: i.e. highest value (height) with higher index. The left boundary tower is
|
||||
:: the tallest tower to the left of the location i.e. highest value (height)
|
||||
:: with lower index.
|
||||
::
|
||||
:: The “find-boundaryheight” arm iterates left to right and works for
|
||||
:: measuring height of the right boundary tower. For the left boundary tower
|
||||
:: we can use a mirror approach. We reverse the input list and adjust the
|
||||
:: input index accordinglyto move right-to-left.
|
||||
::
|
||||
:: In the case where no right or left boundary tower exists, our
|
||||
:: “find-boundaryheight” arm will return the tower height at our current
|
||||
:: index (indicating no water present) and we correctly compute 0 water
|
||||
:: volume in our compute-indvol arm.
|
||||
::
|
||||
++ compute-waterheight
|
||||
|= [loc=@ud n=(list @ud)]
|
||||
^- @ud
|
||||
:: rbth is a face for our "right boundary tower height" computed using our
|
||||
:: "find-boundaryheight" arm moving left to right
|
||||
::
|
||||
=/ rbth (find-boundaryheight loc n)
|
||||
:: lbth is a face for our "right boundary tower height" computed using our
|
||||
:: "find-boundaryheight" arm moving (mirrored) right to left
|
||||
::
|
||||
=/ lbth (find-boundaryheight (sub (lent n) +(loc)) (flop n))
|
||||
:: If, right boundary tower height is less than left boundary tower height,
|
||||
::
|
||||
?: (lth rbth lbth)
|
||||
:: then, return right boundary tower height
|
||||
::
|
||||
rbth
|
||||
:: else, return left boundary tower height
|
||||
::
|
||||
lbth
|
||||
::
|
||||
:: +find-boundaryheight: Computes the height of the highest tower to the right
|
||||
:: of the input location
|
||||
::
|
||||
:: Moves left to right starting at input location until the end of input
|
||||
:: list. Tracks height of each tower location with a height greater than
|
||||
:: height at corresponding input location.
|
||||
::
|
||||
++ find-boundaryheight
|
||||
|= [loc=@ud n=(list @ud)]
|
||||
^- @ud
|
||||
:: i is face used to iterate over input list starting one past input index
|
||||
::
|
||||
=/ i +(loc)
|
||||
:: bheight is face used to measure boundary tower heights--i.e. any tower
|
||||
:: heights greater than height at input location. At start, bheight is set to
|
||||
:: input location height. If no greater heights are found, input location
|
||||
:: height is returned (indicating no higher boundary towers found).
|
||||
::
|
||||
=/ bheight (snag loc n)
|
||||
|-
|
||||
:: If, we are at the end of our input
|
||||
::
|
||||
?: (gte i (lent n))
|
||||
:: then, return boundary tower height
|
||||
::
|
||||
bheight
|
||||
:: else, if current tower height is greater than currently stored boundary
|
||||
:: tower height, replace boundary tower height. Incr iteration idx.
|
||||
::
|
||||
%= $
|
||||
bheight ?: (gth (snag i n) bheight)
|
||||
(snag i n)
|
||||
bheight
|
||||
i +(i)
|
||||
==
|
||||
--
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Solution #2
|
||||
_By ~racfer-hattes. A short and elegant solution._
|
||||
|
||||
```hoon
|
||||
=>
|
||||
|%
|
||||
++ go
|
||||
|= [current=@ud previous=(list @ud) next=(list @ud)]
|
||||
=/ left-peak (roll previous max)
|
||||
=/ right-peak (roll next max)
|
||||
=/ min-peak (min left-peak right-peak)
|
||||
=/ water-level
|
||||
?: (lth min-peak current) 0
|
||||
(sub min-peak current)
|
||||
?~ next water-level
|
||||
(add water-level $(current i.next, next t.next, previous [current previous]))
|
||||
--
|
||||
|= xs=(list @ud)
|
||||
?~ xs 0
|
||||
%- go [i.xs ~ t.xs]
|
||||
```
|
||||
|
||||
### Solution #3
|
||||
_By ~dozreg-toplud. Another very literate and clean solution._
|
||||
|
||||
|
||||
```hoon
|
||||
:: +water-towers: a solution to the HSL challenge #1
|
||||
::
|
||||
:: https://github.com/tamlut-modnys/template-hsl-water-towers
|
||||
:: Takes a (list @ud) of tower heights, returns the number of the units of
|
||||
:: water that can be held in the given structure.
|
||||
::
|
||||
|= towers=(list @ud)
|
||||
^- @ud
|
||||
=<
|
||||
:: x, y are horizontal and vertical axes
|
||||
::
|
||||
=| water-counter=@ud
|
||||
=/ x-last-tower=@ud (dec (lent towers))
|
||||
=/ y-highest-tower=@ud (roll towers max)
|
||||
:: iterate along y axis from y=0
|
||||
::
|
||||
=/ y=@ud 0
|
||||
|-
|
||||
^- @ud
|
||||
:: if, y > max(towers)
|
||||
::
|
||||
?: (gth y y-highest-tower)
|
||||
:: then, return water-counter
|
||||
::
|
||||
water-counter
|
||||
:: else, iterate along x axis from x=1
|
||||
::
|
||||
=/ x=@ud 1
|
||||
|-
|
||||
^- @ud
|
||||
:: if, x = x(last tower)
|
||||
::
|
||||
?: =(x x-last-tower)
|
||||
:: then, go to the next y
|
||||
::
|
||||
^$(y +(y))
|
||||
:: else, increment water-counter if the point [x y] is not occupied by a tower
|
||||
:: and has towers to the left and right on the same y, after go to the next x
|
||||
::
|
||||
=? water-counter ?& (not-tower x y)
|
||||
(has-tower-left x y)
|
||||
(has-tower-right x y)
|
||||
==
|
||||
+(water-counter)
|
||||
$(x +(x))
|
||||
::
|
||||
:: Core with helping functions
|
||||
::
|
||||
|%
|
||||
:: ++not-tower: returns %.y if the coordinate [x y] is free from a tower,
|
||||
:: %.n if occupied.
|
||||
::
|
||||
++ not-tower
|
||||
|= [x=@ud y=@ud]
|
||||
^- ?
|
||||
(gth y (snag x towers))
|
||||
:: ++has-tower-left: returns %.y if there is a tower with height >= y to
|
||||
:: the left from x, %.n otherwise. Enabled computation caching to only test
|
||||
:: each point once.
|
||||
::
|
||||
++ has-tower-left
|
||||
|= [x=@ud y=@ud]
|
||||
~+
|
||||
^- ?
|
||||
:: no towers to the left from the 0th tower
|
||||
::
|
||||
?: =(x 0)
|
||||
%.n
|
||||
:: check recursively to the left
|
||||
::
|
||||
?| (gte (snag (dec x) towers) y)
|
||||
$(x (dec x))
|
||||
==
|
||||
:: ++has-tower-right: returns %.y if there is a tower with height >= y to
|
||||
:: the right from x, %.n otherwise. Enabled computation caching to only test
|
||||
:: each point once.
|
||||
::
|
||||
++ has-tower-right
|
||||
|= [x=@ud y=@ud]
|
||||
~+
|
||||
^- ?
|
||||
:: no towers to the right from the last tower
|
||||
::
|
||||
?: =(x (dec (lent towers)))
|
||||
%.n
|
||||
:: check recursively to the right
|
||||
::
|
||||
?| (gte (snag +(x) towers) y)
|
||||
$(x +(x))
|
||||
==
|
||||
::
|
||||
--
|
||||
```
|
@ -14,7 +14,7 @@ The best place to start when building a new agent is its type definitions in its
|
||||
Let's look at each of these questions in turn, and put together our agent's
|
||||
`/sur` file, which we'll call `/sur/journal.hoon`.
|
||||
|
||||
### 1. Basic types
|
||||
## 1. Basic types
|
||||
|
||||
Our journal entries will just be plain text, so a simple `@t` will work fine to
|
||||
store their contents. Entries will be organized by date, so we'll also need to
|
||||
@ -34,7 +34,7 @@ The structure for a journal entry can therefore be:
|
||||
+$ entry [=id =txt]
|
||||
```
|
||||
|
||||
### 2. Actions
|
||||
## 2. Actions
|
||||
|
||||
Now that we know what a journal entry looks like, we can think about what kind
|
||||
of actions/commands our agent will handle in its `++on-poke` arm. For our
|
||||
@ -54,7 +54,7 @@ We can create a tagged union structure for these actions, like so:
|
||||
==
|
||||
```
|
||||
|
||||
### 3. Updates
|
||||
## 3. Updates
|
||||
|
||||
Updates are a little more complicated than our actions. Firstly, our front-end
|
||||
needs to be able to retrieve an initial list of journal entries to display. Once
|
||||
@ -113,7 +113,7 @@ milliseconds since the Unix Epoch:
|
||||
==
|
||||
```
|
||||
|
||||
### 4. State
|
||||
## 4. State
|
||||
|
||||
We need to store two things in our state: the journal entries and the update
|
||||
log. We could just use a couple of `map`s like so:
|
||||
|
@ -8,7 +8,7 @@ this section, we'll briefly look at how JSON works in Urbit, and write a library
|
||||
to convert our agent's structures to and from JSON for our front-end.
|
||||
|
||||
JSON data comes into Eyre as a string, and Eyre parses it with the
|
||||
[`++de-json:html`](/reference/hoon/zuse/2e_2-3#de-jsonhtml) function in
|
||||
[`++de:json:html`](/reference/hoon/zuse/2e_2-3#dejsonhtml) function in
|
||||
[`zuse.hoon`](/reference/hoon/zuse). The
|
||||
hoon type it's parsed to is `$json`, which is defined as:
|
||||
|
||||
@ -29,7 +29,7 @@ agent (unless the mark specified is already `%json`, in which case it will be
|
||||
delivered directly). Outbound facts will go through the same process in
|
||||
reverse - converted from the agent's native mark to `$json`, then encoded in a
|
||||
string by Eyre using
|
||||
[`++en-json:html`](/reference/hoon/zuse/2e_2-3#en-jsonhtml) and delivered
|
||||
[`++en:json:html`](/reference/hoon/zuse/2e_2-3#enjsonhtml) and delivered
|
||||
to the web client. The basic flow for both inbound messages (pokes) and outbound
|
||||
messages (facts and scry results) looks like this:
|
||||
|
||||
@ -171,7 +171,7 @@ complex nested `$json` decoding function can be built up in this manner.
|
||||
For example:
|
||||
|
||||
```
|
||||
> =js %- need %- de-json:html
|
||||
> =js %- need %- de:json:html
|
||||
'''
|
||||
{
|
||||
"foo": "hello",
|
||||
|
@ -10,175 +10,113 @@ React app front-end.
|
||||
|
||||
Node.js must be installed, and can be downloaded from their
|
||||
[website](https://nodejs.org/en/download). With that installed, we'll have the
|
||||
`npm` package manager available. The first thing we'll do is globally install
|
||||
the `create-react-app` package with the following command:
|
||||
`npm` package manager available and its utility binaries like `npx` to help
|
||||
set up our project. The first thing we'll do is create a project using the
|
||||
[`create-landscape-app`](https://www.npmjs.com/package/@urbit/create-landscape-app)
|
||||
template with the following command:
|
||||
|
||||
```sh
|
||||
npm install -g create-react-app
|
||||
```
|
||||
|
||||
Once installed, we can use it to create a new `journal-ui` directory and setup a
|
||||
new React app in it with the following command:
|
||||
|
||||
```sh
|
||||
create-react-app journal-ui
|
||||
npx @urbit/create-landscape-app
|
||||
✔ What should we call your application? … journal
|
||||
✔ What URL do you use to access Urbit? … http://127.0.0.1:8080
|
||||
```
|
||||
|
||||
We can then open our new directory:
|
||||
|
||||
```sh
|
||||
cd journal-ui
|
||||
```sh {% copy=true %}
|
||||
cd journal/ui
|
||||
```
|
||||
|
||||
Its contents should look something like this:
|
||||
|
||||
```
|
||||
journal-ui
|
||||
├── node_modules
|
||||
ui
|
||||
├── index.html
|
||||
├── package.json
|
||||
├── package-lock.json
|
||||
├── public
|
||||
├── README.md
|
||||
├── postcss.config.js
|
||||
├── tailwind.config.js
|
||||
├── vite.config.js
|
||||
└── src
|
||||
```
|
||||
|
||||
## Install `http-api`
|
||||
## Install dependencies
|
||||
|
||||
Inside our React app directory, let's install the `@urbit/http-api` NPM package:
|
||||
Inside our React app directory, let's install the NPM packages used by
|
||||
our project:
|
||||
|
||||
```sh
|
||||
npm i @urbit/http-api
|
||||
```sh {% copy=true %}
|
||||
npm i
|
||||
```
|
||||
|
||||
We also install a handful of other packages for the UI components
|
||||
(`bootstrap@5.1.3 react-bootstrap@2.2.0 react-textarea-autosize@8.3.3
|
||||
date-fns@2.28.0 react-bottom-scroll-listener@5.0.0 react-day-picker@7.4.10`),
|
||||
but that's not important to our purposes here.
|
||||
This command will install the Urbit interface package (i.e. `@urbit/http-api`)
|
||||
and all the other packages used by our React application. When building from
|
||||
scratch with `create-landscape-app`, this includes a number of useful
|
||||
development libraries that enable automatic refresh on file edits (i.e. `vite`
|
||||
and `@vitejs/plugin-react-refresh`) and simple page styling (i.e.
|
||||
`tailwindcss`). The remainder of this tutorial will focus primarily on how the
|
||||
Urbit interface package is used to communicate with a live ship from within a
|
||||
React application.
|
||||
|
||||
## Additional tweaks
|
||||
## Basic app setup
|
||||
|
||||
Our front-end will be served directly from the ship by the `%docket` app, where
|
||||
a user will open it by clicking on its homescreen tile. Docket serves such
|
||||
front-ends with a base URL path of `/apps/[desk]/`, so in our case it will be
|
||||
`/apps/journal`. In order for our app to be built with correct resource paths,
|
||||
we must add the following line to `package.json`:
|
||||
With all the basics now in place, we can begin work on the app itself. For this
|
||||
simple demonstration, we'll be working just with the `src/app.jsx` file, which
|
||||
contains the rendering logic for our React application. Before we look at the
|
||||
full front-end source for our journal app, let's first review the simpler
|
||||
default code provided by `create-landscape-app` to cover some Urbit API and
|
||||
React basics.
|
||||
|
||||
```json
|
||||
"homepage": "/apps/journal/",
|
||||
### Urbit API setup
|
||||
|
||||
First, let's open up `src/app.jsx` and look at the import statements at the top
|
||||
of this file:
|
||||
|
||||
```javascript
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Urbit from '@urbit/http-api';
|
||||
import { scryCharges } from '@urbit/api';
|
||||
import { AppTile } from './components/AppTile';
|
||||
```
|
||||
|
||||
Our app also needs to know the name of the ship it's being served from in order
|
||||
to talk with it. The `%docket` agent serves a small file for this purpose at
|
||||
`[host]/session.js`. This file is very simple and just contains:
|
||||
The first two of these statements are very common in Urbit React applications;
|
||||
the first imports the React library and a few of its important functions (to be
|
||||
covered in a moment) and the second imports the `Urbit` class, which will be
|
||||
used subsequently to enable browser-to-ship communication.
|
||||
|
||||
```js
|
||||
window.ship = "sampel-palnet";
|
||||
Next, the code sets up the `Urbit` API object as a global variable, which
|
||||
allows the browser-to-ship connection to be established *exactly once* when the
|
||||
page is first being loaded:
|
||||
|
||||
```javascript
|
||||
const api = new Urbit('', '', window.desk);
|
||||
api.ship = window.ship;
|
||||
```
|
||||
|
||||
`sampel-palnet` will of course be replaced by the actual name of the ship. We
|
||||
include this script by adding the following line to the `<head>` section of
|
||||
`public/index.html`:
|
||||
|
||||
```
|
||||
<script src="/session.js"></script>
|
||||
```
|
||||
|
||||
## Basic API setup
|
||||
|
||||
With everything now setup, we can begin work on the app itself. In this case
|
||||
we'll just edit the existing `App.js` file in the `/src` directory. The first thing is to import the `Urbit` class from `@urbit/http-api`:
|
||||
|
||||
```js
|
||||
import Urbit from "@urbit/http-api";
|
||||
```
|
||||
|
||||
We also need to import a few other things, mostly relating to UI components (but
|
||||
these aren't important for our purposes here):
|
||||
|
||||
```js
|
||||
import React, { Component } from "react";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "react-day-picker/lib/style.css";
|
||||
import TextareaAutosize from "react-textarea-autosize";
|
||||
import Button from "react-bootstrap/Button";
|
||||
import Card from "react-bootstrap/Card";
|
||||
import Stack from "react-bootstrap/Stack";
|
||||
import Tab from "react-bootstrap/Tab";
|
||||
import Tabs from "react-bootstrap/Tabs";
|
||||
import ToastContainer from "react-bootstrap/ToastContainer";
|
||||
import Toast from "react-bootstrap/Toast";
|
||||
import Spinner from "react-bootstrap/Spinner";
|
||||
import CloseButton from "react-bootstrap/CloseButton";
|
||||
import Modal from "react-bootstrap/Modal";
|
||||
import DayPickerInput from "react-day-picker/DayPickerInput";
|
||||
import endOfDay from "date-fns/endOfDay";
|
||||
import startOfDay from "date-fns/startOfDay";
|
||||
import { BottomScrollListener } from "react-bottom-scroll-listener";
|
||||
```
|
||||
|
||||
Inside the existing `App` class:
|
||||
|
||||
```js
|
||||
class App extends Component {
|
||||
```
|
||||
|
||||
...we'll clear out the existing demo code and start adding ours. The first thing
|
||||
is to define our app's state. We'll look at most of the state entries in the
|
||||
next section. For now, we'll just consider `status`.
|
||||
|
||||
```js
|
||||
state = {
|
||||
// .....
|
||||
status: null,
|
||||
// .....
|
||||
};
|
||||
```
|
||||
|
||||
Next, we'll setup the `Urbit` API object in `componentDidMount`. We could do
|
||||
this outside the `App` class since we're adding it to `window`, but we'll do it
|
||||
this way so it's all in one place:
|
||||
|
||||
```js
|
||||
componentDidMount() {
|
||||
window.urbit = new Urbit("");
|
||||
window.urbit.ship = window.ship;
|
||||
window.urbit.onOpen = () => this.setState({status: "con"});
|
||||
window.urbit.onRetry = () => this.setState({status: "try"});
|
||||
window.urbit.onError = (err) => this.setState({status: "err"});
|
||||
this.init();
|
||||
};
|
||||
```
|
||||
|
||||
The first thing we do is create a new instance of the `Urbit` class we imported
|
||||
from `@urbit/http-api`, and save it to `window.urbit`. The `Urbit` class
|
||||
constructor takes three arguments: `url`, `desk` and `code`, of which only `url`
|
||||
The first statement creates a new instance of the `Urbit` class we imported
|
||||
from `@urbit/http-api`, and saves it to the `api` variable. The `Urbit` class
|
||||
constructor takes three arguments: `url`, `code`, and `desk`, of which only `url`
|
||||
is mandatory.
|
||||
|
||||
- `url` is the URL of the ship we want to talk to. Since our React app will be
|
||||
served by the ship, we can just leave it as an empty `""` string and let
|
||||
served by the ship, we can just leave it as an empty `''` string and let
|
||||
`Urbit` use root-relative paths.
|
||||
- `desk` is only necessary if we want to run threads through Eyre, and since
|
||||
we're not going to do that, we can exclude it.
|
||||
- `code` is the web login code for authentication, but since the user will
|
||||
already have logged in, we can also exclude that.
|
||||
- `code` is the web login code for authentication. Since the user will already
|
||||
have logged in, we can also leave it as an empty `''` string.
|
||||
- `desk` is only necessary if we want to run threads through Eyre. This example
|
||||
doesn't submit any such requests, but the `desk` is set anyway for
|
||||
demonstration purposes.
|
||||
|
||||
Therefore, we call the class contructor with just the empty `url` string:
|
||||
The second statement sets the ship name in our `Urbit` instance. Eyre requires
|
||||
the ship name be specified in all requests; if we don't set it, Eyre will
|
||||
reject all the messages we send. Fortunately, `create-landscape-app` handles
|
||||
this detail by automatically initializing the active ship's name to the
|
||||
variable `window.ship`, so we just set `api.ship` to this value.
|
||||
|
||||
```js
|
||||
window.urbit = new Urbit("");
|
||||
```
|
||||
|
||||
Next, we need to set the ship name in our `Urbit` instance. Eyre requires the
|
||||
ship name be specified in all requests, so if we don't set it, Eyre will reject
|
||||
all the messages we send. We previously included `session.js` which sets
|
||||
`window.ship` to the ship name, so we just set `window.urbit.ship` as that:
|
||||
|
||||
```js
|
||||
window.urbit.ship = window.ship;
|
||||
```
|
||||
|
||||
Next, we set three callbacks: `onOpen`, `onRetry`, and `onError`. These
|
||||
callbacks are fired when the state of our channel connection changes:
|
||||
While not referenced in the `create-landscape-app` default code, the `Urbit`
|
||||
class has three additional callbacks that can be set: `onOpen`, `onRetry`, and
|
||||
`onError`. These callbacks are fired when the state of our channel connection
|
||||
changes:
|
||||
|
||||
- `onOpen` is called when a connection is established.
|
||||
- `onRetry` is called when a channel connection has been interrupted (such as by
|
||||
@ -188,22 +126,103 @@ callbacks are fired when the state of our channel connection changes:
|
||||
- `onError` is called with an `Error` message once all retries have failed, or
|
||||
otherwise when a fatal error occurs.
|
||||
|
||||
We'll look at how we handle these cases in the next section. For now, we'll just
|
||||
set the `status` entry in the state to either `"con"`, `"try"`, or `"err"` as
|
||||
the case may be. Note that it's not mandatory to set these callbacks, but
|
||||
leaving connection problems unhandled is usually a bad idea.
|
||||
We'll look at how we can use these callbacks in the next section. Note that
|
||||
it's not mandatory to set these callbacks, but leaving connection problems
|
||||
unhandled is usually a bad idea.
|
||||
|
||||
The last thing we do is call:
|
||||
### React app setup
|
||||
|
||||
```js
|
||||
this.init();
|
||||
Finally, let's take a quick look at the React rendering logic for our
|
||||
application. React rendering occurs within components, which are defined either
|
||||
as classes (e.g. `class A extends Component { /* ... */ }`) or functions (e.g.
|
||||
`function A() { /* ... */ }`). While recent React versions support both styles,
|
||||
the latter "modern" style is preferred and used by most Urbit React
|
||||
applications.
|
||||
|
||||
Our code defines a few components, but we'll just focus on the primary
|
||||
component for this tutorial; this component is defined as a functional
|
||||
component named `App`:
|
||||
|
||||
```javascript
|
||||
export function App() {
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
This function will fetch initial entries and subscribe for updates. We'll look
|
||||
at it in the next section.
|
||||
As is common for React components, the first thing we'll define in our `App`
|
||||
component is its state. In React, modifying a component's state causes it to be
|
||||
re-rendered, so state variables should be carefully chosen to constitute all
|
||||
"display-affecting" values. In modern React, component state is defined using
|
||||
the [`useState()`] hook, which returns a pair of `[stateVariable,
|
||||
setStateVariableFunction]`. Since our default `create-landscape-app` code just
|
||||
displays the list of apps installed on a ship, it only needs to store this list
|
||||
as its state:
|
||||
|
||||
```javascript
|
||||
const [apps, setApps] = useState();
|
||||
```
|
||||
|
||||
With the state established, we now define the code responsible for populating
|
||||
this state. The canonical way to grab data from an external service/system in
|
||||
React is to use the [`useEffect()`] hook. This function takes two arguments:
|
||||
(1) the callback function for loading the external data and (2) a list of all
|
||||
state variables dependencies, which will cause re-invocations of the first
|
||||
argument when modified. Our app just needs to load the list of apps on our ship
|
||||
(called `charges`) once, so its [`useEffect()`] invocation is simple:
|
||||
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
async function init() {
|
||||
const charges = (await api.scry(scryCharges)).initial;
|
||||
setApps(charges);
|
||||
}
|
||||
|
||||
init();
|
||||
}, []);
|
||||
```
|
||||
|
||||
The last step is to return the HTML that will be used to render our component
|
||||
in the browser. This HTML must adhere to the syntactic rules of
|
||||
[JSX](https://en.wikipedia.org/wiki/JSX_(JavaScript)), which allow for greater
|
||||
flexibility through extensions like embedded JavaScript (contained in curly
|
||||
brace enclosures). Our component renders each app it found when scrying our
|
||||
ship as a tile accompanied by its title and description:
|
||||
|
||||
```javascript {% mode="collapse" %}
|
||||
return (
|
||||
<main className="flex items-center justify-center min-h-screen">
|
||||
<div className="max-w-md space-y-6 py-20">
|
||||
<h1 className="text-3xl font-bold">Welcome to hut</h1>
|
||||
<p>Here's your urbit's installed apps:</p>
|
||||
{apps && (
|
||||
<ul className="space-y-4">
|
||||
{Object.entries(apps).map(([desk, app]) => (
|
||||
<li key={desk} className="flex items-center space-x-3 text-sm leading-tight">
|
||||
<AppTile {...app} />
|
||||
<div className="flex-1 text-black">
|
||||
<p>
|
||||
<strong>{app.title || desk}</strong>
|
||||
</p>
|
||||
{app.info && <p>{app.info}</p>}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
```
|
||||
|
||||
With this brief primer complete, we'll take a closer look at our journal
|
||||
application's front-end and how it utilizes the Urbit HTTP API in the next
|
||||
section.
|
||||
|
||||
## Resources
|
||||
|
||||
- [React Tutorial](https://react.dev/learn/tutorial-tic-tac-toe) - A tutorial
|
||||
walking through the basics of writing a modern React application.
|
||||
|
||||
- [HTTP API Guide](/guides/additional/http-api-guide) - Reference documentation for
|
||||
`@urbit/http-api`.
|
||||
|
||||
@ -214,3 +233,7 @@ at it in the next section.
|
||||
- [`@urbit/http-api` source
|
||||
code](https://github.com/urbit/urbit/tree/master/pkg/npm/http-api) - The
|
||||
source code for the `@urbit/http-api` NPM package.
|
||||
|
||||
|
||||
[`usestate()`]: https://react.dev/reference/react/useState
|
||||
[`useeffect()`]: https://react.dev/reference/react/useEffect
|
||||
|
@ -3,58 +3,72 @@ title = "7. React app logic"
|
||||
weight = 8
|
||||
+++
|
||||
|
||||
With the basic things setup, we can now go over the logic of our app. We'll just
|
||||
focus on functions that are related to ship communications using the `Urbit`
|
||||
object we previously setup, and ignore UI components and other helper functions.
|
||||
Now that we've reviewed the basics of setting up an Urbit React app, we can
|
||||
dive into the more complex logic that drives our [journal app's
|
||||
front-end](https://github.com/urbit/docs-examples/tree/main/journal-app/ui).
|
||||
We'll focus on the app's main component `App` (defined in
|
||||
[`src/app.jsx`](https://github.com/urbit/docs-examples/tree/main/journal-app/ui/src/app.jsx))
|
||||
and how it leverages functions related to ship communications using the `Urbit`
|
||||
object. For more information on UI components and other helper functions, see
|
||||
the [resources section](#resources).
|
||||
|
||||
## State
|
||||
|
||||
In the previous section we just mentioned the connection `status` field of our
|
||||
state. Here's the full state of our App:
|
||||
In the previous section, we introduced how React components use [`useState()`]
|
||||
to declare state variables within components. The main `App` component in our
|
||||
journal app contains a number of these statements to manage its many
|
||||
constituents and sub-components:
|
||||
|
||||
```js {% copy=true %}
|
||||
state = {
|
||||
entries: [], // list of journal entries for display
|
||||
drafts: {}, // edits which haven't been submitted yet
|
||||
newDraft: {}, // new entry which hasn't been submitted yet
|
||||
results: [], // search results
|
||||
searchStart: null, // search query start date
|
||||
searchEnd: null, // search query end date
|
||||
resultStart: null, // search results start date
|
||||
resultEnd: null, // search results end date
|
||||
searchTime: null, // time of last search
|
||||
latestUpdate: null, // most recent update we've received
|
||||
entryToDelete: null, // deletion target for confirmation modal
|
||||
status: null, // connection status (con, try, err)
|
||||
errorCount: 0, // number of errors so far
|
||||
errors: new Map(), // list of error messages for display
|
||||
};
|
||||
```javascript
|
||||
// Control/Meta State //
|
||||
const [subEvent, setSubEvent] = useState({});
|
||||
const [latestUpdate, setLatestUpdate] = useState(null);
|
||||
const [status, setStatus] = useState(null);
|
||||
const [errorCount, setErrorCount] = useState(0);
|
||||
const [errors, setErrors] = useState(new Map());
|
||||
|
||||
// Journal State //
|
||||
const [entries, setEntries] = useState([]);
|
||||
const [drafts, setDrafts] = useState({});
|
||||
const [newDraft, setNewDraft] = useState({});
|
||||
const [entryToDelete, setEntryToDelete] = useState(null);
|
||||
|
||||
// Search State //
|
||||
const [results, setResults] = useState([]);
|
||||
const [searchMeta, setSearchMeta] = useState({
|
||||
time: null,
|
||||
start: null,
|
||||
end: null,
|
||||
});
|
||||
```
|
||||
|
||||
We'll see how these are used subsequently.
|
||||
|
||||
## Initialize
|
||||
|
||||
The first thing our app does is call `init()`:
|
||||
After defining its state, the next thing our `App` component does is define a
|
||||
function called `init()`, which is one of the first functions called during its
|
||||
bootstrapping process:
|
||||
|
||||
```js
|
||||
init = () => {
|
||||
this.getEntries().then(
|
||||
```javascript
|
||||
const init = () => {
|
||||
getEntries().then(
|
||||
(result) => {
|
||||
this.handleUpdate(result);
|
||||
this.setState({ latestUpdate: result.time });
|
||||
this.subscribe();
|
||||
setSubEvent(result);
|
||||
setLatestUpdate(result.time);
|
||||
subscribe();
|
||||
},
|
||||
(err) => {
|
||||
this.setErrorMsg("Connection failed");
|
||||
this.setState({ status: "err" });
|
||||
addError("Connection failed");
|
||||
setStatus("err");
|
||||
}
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
This function just calls `getEntries()` to retrieve the initial list of journal
|
||||
entries then, if that succeeded, it calls `subscribe()` to subscribe for new
|
||||
entries; then, if that succeeded, it publishes this update with `setSubEvent()`
|
||||
and `setLatestUpdate()` and then calls `subscribe()` to subscribe for new
|
||||
updates. If the initial entry retrieval failed, we set the connection `status`
|
||||
and save an error message in the `errors` map. We'll look at what we do with
|
||||
errors later.
|
||||
@ -63,13 +77,13 @@ errors later.
|
||||
|
||||
![entries screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/entries.png)
|
||||
|
||||
The `getEntries` function scries our `%journal` agent for up to 10 entries
|
||||
before the oldest we currently have. We call this initially, and then each time
|
||||
The `getEntries()` function scries our `%journal` agent for up to 10 entries
|
||||
before the oldest we currently have. We call this initially and then each time
|
||||
the user scrolls to the bottom of the list.
|
||||
|
||||
```js
|
||||
getEntries = async () => {
|
||||
const { entries: e } = this.state;
|
||||
```javascript
|
||||
const getEntries = async () => {
|
||||
const e = entries;
|
||||
const before = e.length === 0 ? Date.now() : e[e.length - 1].id;
|
||||
const max = 10;
|
||||
const path = `/entries/before/${before}/${max}`;
|
||||
@ -93,27 +107,27 @@ direct GET requests allow other marks too.
|
||||
The `Urbit.scry` method returns a Promise which will contain an HTTP error
|
||||
message if the scry failed. We handle it with a `.then` expression back in the
|
||||
function that called it, either [`init()`](#initialize) or `moreEntries()`. If
|
||||
the Promise is successfuly, the results are passed to the
|
||||
[`handleUpdate`](#updates) function which appends the new entries to the
|
||||
existing ones in state.
|
||||
the Promise is successfully evaluated, the results are passed to the
|
||||
[`setSubEvent()`](#updates) function, which appends the new entries to the
|
||||
existing ones via a [`useEffect()`] hook (more on this [below](#updates)).
|
||||
|
||||
## Subscription
|
||||
|
||||
A subscription to the `/updates` path of our `%journal` agent is opened with our
|
||||
`subscribe()` function:
|
||||
|
||||
```js
|
||||
subscribe = () => {
|
||||
```javascript
|
||||
const subscribe = () => {
|
||||
try {
|
||||
window.urbit.subscribe({
|
||||
app: "journal",
|
||||
path: "/updates",
|
||||
event: this.handleUpdate,
|
||||
err: () => this.setErrorMsg("Subscription rejected"),
|
||||
quit: () => this.setErrorMsg("Kicked from subscription"),
|
||||
event: setSubEvent,
|
||||
err: () => addError("Subscription rejected"),
|
||||
quit: () => addError("Kicked from subscription"),
|
||||
});
|
||||
} catch {
|
||||
this.setErrorMsg("Subscription failed");
|
||||
addError("Subscription failed");
|
||||
}
|
||||
};
|
||||
```
|
||||
@ -124,7 +138,8 @@ object:
|
||||
- `app` - the target agent.
|
||||
- `path` - the `%watch` path we're subscribing to.
|
||||
- `event` - a function to handle each fact the agent sends out. We call our
|
||||
`handleUpdate` function, which we'll describe below.
|
||||
`setSubEvent()` function to set off a cascade to update the interface;
|
||||
this process is described [below](#updates).
|
||||
- `err` - a function to call if the subscription request is rejected (nacked).
|
||||
We just display an error in this case.
|
||||
- `quit` - a function to call if we get kicked from the subscription. We also
|
||||
@ -137,86 +152,130 @@ keep track of these IDs in your app's state.
|
||||
|
||||
## Updates
|
||||
|
||||
This `handleUpdate` function handles all updates we receive. It's called
|
||||
whenever an event comes in for our subscription, and it's also called with the
|
||||
results of [`getEntries`](#getting-entries) and [`getUpdates`](#error-handling)
|
||||
(described later).
|
||||
The architecture for updating a React interface based on incoming facts from an
|
||||
`Urbit` subscription tends to follow a common pattern constituted of three
|
||||
major parts:
|
||||
|
||||
It's a bit complex, but basically it just checks whether the JSON object is
|
||||
`add`, `edit`, `delete`, or `entries`, and then updates the state appropriately.
|
||||
The object it's receiving is just the `$update` structure converted to JSON by
|
||||
the mark conversion functions we wrote previously.
|
||||
1. A [`useState()`] call that creates an update object field as part of the
|
||||
main component's state:
|
||||
```javascript
|
||||
const [subEvent, setSubEvent] = useState({});
|
||||
```
|
||||
2. An `Urbit.subscribe` call that passes the update object's setter function as
|
||||
its `event` field:
|
||||
```javascript
|
||||
window.urbit.subscribe({/* ... */, event: setSubEvent});
|
||||
```
|
||||
3. A [`useEffect()`] invocation that triggers off of the update object, which
|
||||
contains the logic for handling subscription updates:
|
||||
```javascript
|
||||
useEffect(() => {/* ... */}, [subEvent]);
|
||||
```
|
||||
|
||||
```js
|
||||
handleUpdate = (upd) => {
|
||||
const { entries, drafts, results, latestUpdate } = this.state;
|
||||
if (upd.time !== latestUpdate) {
|
||||
if ("entries" in upd) {
|
||||
this.setState({ entries: entries.concat(upd.entries) });
|
||||
} else if ("add" in upd) {
|
||||
const { time, add } = upd;
|
||||
const eInd = this.spot(add.id, entries);
|
||||
const rInd = this.spot(add.id, results);
|
||||
const toE =
|
||||
entries.length === 0 || add.id > entries[entries.length - 1].id;
|
||||
const toR = this.inSearch(add.id, time);
|
||||
The key piece of this architecture is the [`useEffect()`] trigger, which is
|
||||
called whenever an event comes in on the subscription wire (achieved by
|
||||
including the subscription object `subEvent` as a re-invocation trigger in
|
||||
[`useEffect()`]'s second argument). In our application, this hook is also
|
||||
triggered by calls to [`getEntries()`](#getting-entries) and
|
||||
[`getUpdates()`](#error-handling), which will be described in greater detail
|
||||
later.
|
||||
|
||||
The trigger code is a bit complex, but in broad brushstrokes it just checks the
|
||||
header of the incoming JSON object (i.e. one of `add`, `edit`, `delete`, or
|
||||
`entries`) and then updates the state appropriately. The object it's receiving
|
||||
is just the `$update` structure converted to JSON by the mark conversion
|
||||
functions we wrote previously.
|
||||
|
||||
```javascript {% mode="collapse" %}
|
||||
useEffect(() => {
|
||||
const getDataIndex = (id, data) => {
|
||||
let low = 0;
|
||||
let high = data.length;
|
||||
while (low < high) {
|
||||
let mid = (low + high) >>> 1;
|
||||
if (data[mid].id > id) low = mid + 1;
|
||||
else high = mid;
|
||||
}
|
||||
return low;
|
||||
};
|
||||
|
||||
const isInSearch = (id, time) => (
|
||||
searchMeta.time !== null &&
|
||||
time >= searchMeta.time &&
|
||||
searchMeta.start.getTime() <= id &&
|
||||
searchMeta.end.getTime() >= id
|
||||
);
|
||||
|
||||
if (subEvent.time !== latestUpdate) {
|
||||
if ("entries" in subEvent) {
|
||||
// NOTE: `BottomScrollListener` can fire on top of `init`, which can
|
||||
// cause entries to be double loaded; we trim duplicates to avoid overlap
|
||||
const [existing, incoming] = [entries, subEvent.entries];
|
||||
const oldestExistingId = existing.length === 0
|
||||
? Date.now()
|
||||
: existing[existing.length - 1].id;
|
||||
let newestIncomingInd = getDataIndex(oldestExistingId, incoming);
|
||||
newestIncomingInd += newestIncomingInd < incoming.length
|
||||
&& incoming[newestIncomingInd].id >= oldestExistingId;
|
||||
setEntries(existing.concat(incoming.slice(newestIncomingInd)));
|
||||
} else if ("add" in subEvent) {
|
||||
const { time, add } = subEvent;
|
||||
const eInd = getDataIndex(add.id, entries);
|
||||
const rInd = getDataIndex(add.id, results);
|
||||
const toE = entries.length === 0 || add.id > entries[entries.length - 1].id;
|
||||
const toR = isInSearch(add.id, time);
|
||||
toE && entries.splice(eInd, 0, add);
|
||||
toR && results.splice(rInd, 0, add);
|
||||
this.setState({
|
||||
...(toE && { entries: entries }),
|
||||
...(toR && { results: results }),
|
||||
latestUpdate: time,
|
||||
});
|
||||
} else if ("edit" in upd) {
|
||||
const { time, edit } = upd;
|
||||
toE && setEntries([...entries]);
|
||||
toR && setResults([...results]);
|
||||
setLatestUpdate(time);
|
||||
} else if ("edit" in subEvent) {
|
||||
const { time, edit } = subEvent;
|
||||
const eInd = entries.findIndex((e) => e.id === edit.id);
|
||||
const rInd = results.findIndex((e) => e.id === edit.id);
|
||||
const toE = eInd !== -1;
|
||||
const toR = rInd !== -1 && this.inSearch(edit.id, time);
|
||||
const toR = rInd !== -1 && isInSearch(edit.id, time);
|
||||
if (toE) entries[eInd] = edit;
|
||||
if (toR) results[rInd] = edit;
|
||||
(toE || toR) && delete drafts[edit.id];
|
||||
this.setState({
|
||||
...(toE && { entries: entries }),
|
||||
...(toR && { results: results }),
|
||||
...((toE || toR) && { drafts: drafts }),
|
||||
latestUpdate: time,
|
||||
});
|
||||
} else if ("del" in upd) {
|
||||
const { time, del } = upd;
|
||||
toE && setEntries([...entries]);
|
||||
toR && setResults([...results]);
|
||||
(toE || toR) && setDrafts({...drafts});
|
||||
setLatestUpdate(time);
|
||||
} else if ("del" in subEvent) {
|
||||
const { time, del } = subEvent;
|
||||
const eInd = entries.findIndex((e) => e.id === del.id);
|
||||
const rInd = results.findIndex((e) => e.id === del.id);
|
||||
const toE = eInd !== -1;
|
||||
const toR = this.inSearch(del.id, time) && rInd !== -1;
|
||||
const toR = isInSearch(del.id, time) && rInd !== -1;
|
||||
toE && entries.splice(eInd, 1);
|
||||
toR && results.splice(rInd, 1);
|
||||
(toE || toR) && delete drafts[del.id];
|
||||
this.setState({
|
||||
...(toE && { entries: entries }),
|
||||
...(toR && { results: results }),
|
||||
...((toE || toR) && { drafts: drafts }),
|
||||
latestUpdate: time,
|
||||
});
|
||||
toE && setEntries([...entries]);
|
||||
toR && setResults([...results]);
|
||||
(toE || toR) && setDrafts({...drafts});
|
||||
setLatestUpdate(time);
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [subEvent]);
|
||||
```
|
||||
|
||||
## Add, edit, delete
|
||||
|
||||
![add screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/add.png)
|
||||
|
||||
When a user writes a new journal entry and hits submit, the `submitNew` function
|
||||
is called. It uses the `Urbit.poke` method to poke our `%journal` agent.
|
||||
When a user writes a new journal entry and hits submit, the `createEntry()`
|
||||
function is called. It uses the `Urbit.poke` method to poke our `%journal`
|
||||
agent.
|
||||
|
||||
```js
|
||||
submitNew = (id, txt) => {
|
||||
```javascript
|
||||
const createEntry = (id, txt) => {
|
||||
window.urbit.poke({
|
||||
app: "journal",
|
||||
mark: "journal-action",
|
||||
json: { add: { id: id, txt: txt } },
|
||||
onSuccess: () => this.setState({ newDraft: {} }),
|
||||
onError: () => this.setErrorMsg("New entry rejected"),
|
||||
onSuccess: () => setDraft({}),
|
||||
onError: () => setError("New entry rejected"),
|
||||
});
|
||||
};
|
||||
```
|
||||
@ -239,35 +298,38 @@ The `Urbit.poke` method takes five arguments:
|
||||
`onSuccess` and `onError` are optional, but it's usually desirable to handle
|
||||
these cases.
|
||||
|
||||
The `delete` and `submitEdit` functions are similar to `submitNew`, but for the
|
||||
`%del` and `%edit` actions rather than `%add`:
|
||||
The `deleteEntry()` and `editEntry()` functions are similar to `createEntry()`,
|
||||
but for the `%del` and `%edit` actions rather than `%add`:
|
||||
|
||||
![edit screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/edit.png)
|
||||
|
||||
```js
|
||||
submitEdit = (id, txt) => {
|
||||
if (txt !== null) {
|
||||
```javascript
|
||||
const editEntry = (id, txt) => {
|
||||
if (txt === null) {
|
||||
delete drafts[id];
|
||||
setDrafts({...drafts});
|
||||
} else {
|
||||
window.urbit.poke({
|
||||
app: "journal",
|
||||
mark: "journal-action",
|
||||
json: { edit: { id: id, txt: txt } },
|
||||
onError: () => this.setErrorMsg("Edit rejected"),
|
||||
onError: () => setError("Edit rejected"),
|
||||
});
|
||||
} else this.cancelEdit(id);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
![delete screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/delete.png)
|
||||
|
||||
```js
|
||||
delete = (id) => {
|
||||
```javascript
|
||||
const deleteEntry = (id) => {
|
||||
window.urbit.poke({
|
||||
app: "journal",
|
||||
mark: "journal-action",
|
||||
json: {"del": {"id": id}},
|
||||
onError: ()=>this.setErrorMsg("Deletion rejected")
|
||||
})
|
||||
this.setState({rmModalShow: false, entryToDelete: null})
|
||||
json: { del: { id: id } },
|
||||
onError: () => setError("Deletion rejected"),
|
||||
});
|
||||
setDeleteId(null);
|
||||
};
|
||||
```
|
||||
|
||||
@ -279,73 +341,66 @@ our agent.
|
||||
|
||||
![search screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/search.png)
|
||||
|
||||
When searching for entries between two dates, the `getSearch` function is
|
||||
When searching for entries between two dates, the `searchEntries()` function is
|
||||
called, which uses the `Urbit.scry` method to scry for the results in a similar
|
||||
fashion to [`getEntries`](#getting-entries), but using the
|
||||
`/x/entries/between/[start]/[end]` endpoint.
|
||||
|
||||
```js
|
||||
getSearch = async () => {
|
||||
const { searchStart: ss, searchEnd: se, latestUpdate: lu } = this.state;
|
||||
if (lu !== null && ss !== null && se !== null) {
|
||||
let start = ss.getTime();
|
||||
let end = se.getTime();
|
||||
if (start < 0) start = 0;
|
||||
if (end < 0) end = 0;
|
||||
const path = `/entries/between/${start}/${end}`;
|
||||
window.urbit
|
||||
.scry({
|
||||
app: "journal",
|
||||
path: path,
|
||||
})
|
||||
.then(
|
||||
(result) => {
|
||||
this.setState({
|
||||
searchTime: result.time,
|
||||
searchStart: null,
|
||||
searchEnd: null,
|
||||
resultStart: ss,
|
||||
resultEnd: se,
|
||||
results: result.entries,
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
this.setErrorMsg("Search failed");
|
||||
}
|
||||
);
|
||||
} else {
|
||||
lu !== null && this.setErrorMsg("Searh failed");
|
||||
}
|
||||
```javascript
|
||||
const searchEntries = async () => {
|
||||
const start = Math.max(inputStart.getTime(), 0);
|
||||
const end = Math.max(inputEnd.getTime(), 0);
|
||||
window.urbit.scry({
|
||||
app: "journal",
|
||||
path: `/entries/between/${start}/${end}`,
|
||||
}).then(
|
||||
(result) => {
|
||||
setInputStart(null);
|
||||
setInputEnd(null);
|
||||
setResults(result.entries);
|
||||
setSearchMeta({
|
||||
time: result.time,
|
||||
start: inputStart,
|
||||
end: inputEnd
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
setError("Search failed");
|
||||
}
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
When the channel connection is interrupted, the `Urbit` object will begin trying to reconnect. On each attempt, it sets the connection `status` to `"try"`, as we specified for the `onRetry` callback. When this is set, a "reconnecting" message is displayed at the bottom of the screen:
|
||||
When the channel connection is interrupted, the `Urbit` object will begin
|
||||
trying to reconnect. On each attempt, it sets the connection `status` to
|
||||
`"try"`, as we specified for the `onRetry` callback. When this is set, a
|
||||
"reconnecting" message is displayed at the bottom of the screen:
|
||||
|
||||
![reconnecting screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/reconnecting.png)
|
||||
|
||||
If all three reconnection attempts fail, the `onError` callback is fired and we replace the "reconnecting" message with a "reconnect" button:
|
||||
If all three reconnection attempts fail, the `onError` callback is fired and we
|
||||
replace the "reconnecting" message with a "reconnect" button:
|
||||
|
||||
![reconnect screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/reconnect.png)
|
||||
|
||||
When clicked, the following function is called:
|
||||
|
||||
```js
|
||||
reconnect = () => {
|
||||
```javascript
|
||||
const reconnect = () => {
|
||||
window.urbit.reset();
|
||||
const latest = this.state.latestUpdate;
|
||||
if (latest === null) {
|
||||
this.init();
|
||||
if (latestUpdate === null) {
|
||||
init();
|
||||
} else {
|
||||
this.getUpdates().then(
|
||||
getUpdates().then(
|
||||
(result) => {
|
||||
result.logs.map((e) => this.handleUpdate(e));
|
||||
this.subscribe();
|
||||
result.logs.map(setSubEvent);
|
||||
subscribe();
|
||||
},
|
||||
(err) => {
|
||||
this.setErrorMsg("Connection failed");
|
||||
this.setState({ status: "err" });
|
||||
addError("Connection failed");
|
||||
setStatus("err");
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -363,10 +418,9 @@ Since we've reset the channel, we don't know if we've missed any updates. Rather
|
||||
than having to refresh our whole state, we can use the `getUpdates()` function
|
||||
to get any missing update:
|
||||
|
||||
```js
|
||||
getUpdates = async () => {
|
||||
const { latestUpdate: latest } = this.state;
|
||||
const since = latest === null ? Date.now() : latest;
|
||||
```javascript
|
||||
const getUpdates = async () => {
|
||||
const since = latestUpdate === null ? Date.now() : latestUpdate;
|
||||
const path = `/updates/since/${since}`;
|
||||
return window.urbit.scry({
|
||||
app: "journal",
|
||||
@ -381,18 +435,21 @@ recent than `latestUpdate`, which is always set to the last logged action we
|
||||
received. The `getUpdates` function returns a Promise to the `reconnect`
|
||||
function above which called it. The `reconnect` function handles it in a `.then`
|
||||
expression, where the success case passes each update retrieved to the
|
||||
[`handleUpdate`](#updates) function, updating our state.
|
||||
[`setSubEvent()`](#updates) function, updating our state.
|
||||
|
||||
Lastly, as well as handling channel connection errors, we also handle errors
|
||||
such as poke nacks or failed scries by printing error messages added to the
|
||||
`error` map by the `setErrorMsg` function. You could of course handle nacks,
|
||||
kicks, scry failures, etc differently than just printing an error, it depends on
|
||||
the needs of your app.
|
||||
`error` map by the `setErrorMsg()` function. You could of course handle nacks,
|
||||
kicks, scry failures, etc differently than just printing an error; it depends
|
||||
on the needs of your app.
|
||||
|
||||
![search failed screenshot](https://media.urbit.org/guides/core/app-school-full-stack-guide/search-failed.png)
|
||||
|
||||
## Resources
|
||||
|
||||
- [React Tutorial](https://react.dev/learn/tutorial-tic-tac-toe) - A tutorial
|
||||
walking through the basics of writing a modern React application.
|
||||
|
||||
- [HTTP API Guide](/guides/additional/http-api-guide) - Reference documentation for
|
||||
`@urbit/http-api`.
|
||||
|
||||
@ -403,3 +460,7 @@ the needs of your app.
|
||||
- [`@urbit/http-api` source
|
||||
code](https://github.com/urbit/urbit/tree/master/pkg/npm/http-api) - The
|
||||
source code for the `@urbit/http-api` NPM package.
|
||||
|
||||
|
||||
[`usestate()`]: https://react.dev/reference/react/useState
|
||||
[`useeffect()`]: https://react.dev/reference/react/useEffect
|
||||
|
@ -34,20 +34,20 @@ There's a handful of extra files we need in the root of our desk:
|
||||
|
||||
We only have one agent to start, so `desk.bill` is very simple:
|
||||
|
||||
```
|
||||
``` {% copy=true %}
|
||||
:~ %journal
|
||||
==
|
||||
```
|
||||
|
||||
Likewise, `sys.kelvin` just contains:
|
||||
|
||||
```
|
||||
[%zuse 417]
|
||||
``` {% copy=true %}
|
||||
[%zuse 414]
|
||||
```
|
||||
|
||||
The `desk.docket-0` file is slightly more complicated:
|
||||
|
||||
```
|
||||
``` {% copy=true %}
|
||||
:~
|
||||
title+'Journal'
|
||||
info+'Dear diary...'
|
||||
@ -107,24 +107,21 @@ Once created, we can mount it to the unix filesystem.
|
||||
|
||||
In the dojo of a fake ship:
|
||||
|
||||
```
|
||||
> |merge %journal our %webterm
|
||||
>=
|
||||
> |mount %journal
|
||||
>=
|
||||
``` {% copy=true %}
|
||||
|new-desk %journal
|
||||
|mount %journal
|
||||
```
|
||||
|
||||
Now we can browse to it in the unix terminal:
|
||||
|
||||
```sh
|
||||
cd ~/zod/journal
|
||||
```sh {% copy=true %}
|
||||
cd /path/to/zod/journal
|
||||
```
|
||||
|
||||
Currently it has the same files as the `%webterm` desk, so we need to delete
|
||||
those:
|
||||
Currently it just contains some skeleton files, so we need to delete those:
|
||||
|
||||
```sh
|
||||
rm -r .
|
||||
```sh {% copy=true %}
|
||||
rm -rI /path/to/zod/journal/*
|
||||
```
|
||||
|
||||
Apart from the kernel and standard library, desks need to be totally
|
||||
@ -132,53 +129,56 @@ self-contained, including all mark files and libraries necessary to build them.
|
||||
For example, since our app contains a number of `.hoon` files, we need the
|
||||
`hoon.hoon` mark, and its dependencies. The easiest way to ensure our desk has
|
||||
everything it needs is to copy in the "dev" versions of the `%base` and
|
||||
`%garden` desks. To do this, we first clone the Urbit git repository:
|
||||
`%garden` desks. To do this, we first clone the Urbit and Landscape git repositories:
|
||||
|
||||
```sh
|
||||
```sh {% copy=true %}
|
||||
git clone https://github.com/urbit/urbit.git urbit-git
|
||||
git clone https://github.com/tloncorp/landscape.git landscape-git
|
||||
```
|
||||
|
||||
If we navigate to the `pkg` directory in the cloned repo:
|
||||
If we navigate to the `pkg` directory in the cloned `urbit` repo:
|
||||
|
||||
```sh
|
||||
cd ~/urbit-git/pkg
|
||||
```sh {% copy=true %}
|
||||
cd /path/to/urbit-git/pkg
|
||||
```
|
||||
|
||||
...we can combine the `base-dev` and `garden-dev` desks with the included
|
||||
`symbolic-merge.sh` script:
|
||||
...we can combine the `base-dev` and Landscape `desk-dev` desks with the
|
||||
included `symbolic-merge.sh` script:
|
||||
|
||||
```sh
|
||||
```sh {% copy=true %}
|
||||
./symbolic-merge.sh base-dev journal
|
||||
./symbolic-merge.sh garden-dev journal
|
||||
./symbolic-merge.sh ../../landscape-git/desk-dev journal
|
||||
```
|
||||
|
||||
Now, we copy the contents of the new `journal` folder into our empty desk:
|
||||
|
||||
```sh
|
||||
cp -rL journal/* ~/zod/journal/
|
||||
```sh {% copy=true %}
|
||||
cp -rL journal/* /path/to/zod/journal/
|
||||
```
|
||||
|
||||
Note we've used the `L` flag to resolve symbolic links, because the dev-desks
|
||||
contain symlinks to files in the actual `arvo` and `garden` folders.
|
||||
Note we've used the `L` flag to resolve symbolic links.
|
||||
|
||||
We can copy across all of our own files too:
|
||||
|
||||
```sh
|
||||
cp -r ~/ourfiles/* ~/zod/journal/
|
||||
```sh {% copy=true %}
|
||||
cp -r /path/to/ourfiles/* /path/to/zod/journal/
|
||||
```
|
||||
|
||||
Finally, in the dojo, we can commit the whole lot:
|
||||
|
||||
```
|
||||
``` {% copy=true %}
|
||||
|commit %journal
|
||||
```
|
||||
|
||||
## Glob
|
||||
|
||||
The next step is to build our front-end and upload the files to our ship. In the
|
||||
`journal-ui` folder containing our React app, we can run:
|
||||
The next step is to build our front-end and upload the files to our ship. If
|
||||
you haven't yet downloaded the journal front-end source files, you can grab
|
||||
them from [their repository](https://github.com/urbit/docs-examples). In the
|
||||
folder containing our React app (`journal-app/ui` relative to the repository
|
||||
base directory), we can run:
|
||||
|
||||
```sh
|
||||
```sh {% copy=true %}
|
||||
npm run build
|
||||
```
|
||||
|
||||
@ -186,7 +186,7 @@ This will create a `build` directory containing the compiled front-end files. To
|
||||
upload it to our ship, we need to first install the `%journal` desk. In the
|
||||
dojo:
|
||||
|
||||
```
|
||||
``` {% copy=true %}
|
||||
|install our %journal
|
||||
```
|
||||
|
||||
@ -208,7 +208,7 @@ If we now return to the homescreen of our ship, we'll see our tile displayed, an
|
||||
The last thing we need to do is publish our app, so other users can install it
|
||||
from our ship. To do that, we just run the following command in the dojo:
|
||||
|
||||
```
|
||||
``` {% copy=true %}
|
||||
:treaty|publish %journal
|
||||
```
|
||||
|
||||
|
@ -27,7 +27,7 @@ here](https://github.com/urbit/urbit/tree/master/pkg/npm/api).
|
||||
|
||||
Here is the reference material for each section of this walkthrough.
|
||||
|
||||
#### Types
|
||||
### Types
|
||||
|
||||
- [App School /sur section](/guides/core/app-school/7-sur-and-marks#sur) -
|
||||
This section of App School covers writing a `/sur` structure library for
|
||||
@ -38,7 +38,7 @@ Here is the reference material for each section of this walkthrough.
|
||||
This section of `zuse.hoon` contains all the functions for working with
|
||||
`mop`s, and is well commented.
|
||||
|
||||
#### Agent
|
||||
### Agent
|
||||
|
||||
- [App School I](/guides/core/app-school/intro) - App School I covers all
|
||||
aspects of writing Gall agents in detail.
|
||||
@ -52,7 +52,7 @@ Here is the reference material for each section of this walkthrough.
|
||||
The `agentio` library in the `%base` desk contains a large number of useful
|
||||
functions which making writing Gall agents easier.
|
||||
|
||||
#### JSON
|
||||
### JSON
|
||||
|
||||
- [The JSON Guide](/guides/additional/json-guide) - The stand-alone JSON guide
|
||||
covers JSON encoding/decoding in great detail.
|
||||
@ -69,7 +69,7 @@ Here is the reference material for each section of this walkthrough.
|
||||
- [Eyre Overview](/reference/arvo/eyre/eyre) - This section of the Eyre vane
|
||||
documentation goes over the basic features of the Eyre vane.
|
||||
|
||||
#### Marks
|
||||
### Marks
|
||||
|
||||
- [The Marks section of the Clay documentation](/reference/arvo/clay/marks/marks) -
|
||||
This section of the Clay vane documentation covers mark files comprehensively.
|
||||
@ -80,7 +80,7 @@ Here is the reference material for each section of this walkthrough.
|
||||
- [The JSON Guide](/guides/additional/json-guide) - This also covers writing mark
|
||||
files to convert to/from JSON.
|
||||
|
||||
#### Eyre
|
||||
### Eyre
|
||||
|
||||
- [The Eyre vane documentation](/reference/arvo/eyre/eyre) - This section of the vane
|
||||
docs covers all aspects of Eyre.
|
||||
@ -91,7 +91,7 @@ Here is the reference material for each section of this walkthrough.
|
||||
documentation walks through using Eyre's external API at a low level (using
|
||||
`curl`).
|
||||
|
||||
#### React App Setup and Logic
|
||||
### React App Setup and Logic
|
||||
|
||||
- [HTTP API Guide](/guides/additional/http-api-guide) - Reference documentation for
|
||||
`@urbit/http-api`.
|
||||
@ -104,7 +104,7 @@ Here is the reference material for each section of this walkthrough.
|
||||
code](https://github.com/urbit/urbit/tree/master/pkg/npm/http-api) - The
|
||||
source code for the `@urbit/http-api` NPM package.
|
||||
|
||||
#### Desk and Glob
|
||||
### Desk and Glob
|
||||
|
||||
- [App publishing/distribution docs](/guides/additional/software-distribution) -
|
||||
Documentation covering third party desk composition, publishing and
|
||||
|
@ -15,71 +15,29 @@ define _scry endpoints_ which allow data to be requested from their states. The
|
||||
endpoints can process the data in any way before returning it, but they cannot
|
||||
alter the actual state - scries can only read, not modify.
|
||||
|
||||
Most of the time, scry requests are handled by Arvo, which routes the request to
|
||||
the appropriate vane. When you scry a Gall agent you actually scry Gall itself.
|
||||
Gall interprets the request, runs it on the specified agent, and then returns
|
||||
the result. Scries are performed with the
|
||||
[dotket](/reference/hoon/rune/dot#-dotket) (`.^`) rune. Here's a summary of
|
||||
their format:
|
||||
Gall itself defines some special vane-level endpoints [as described in its scry
|
||||
reference](/reference/arvo/gall/scry), but most scries to Gall are routed to
|
||||
particular agents and handled by them instead. Agent scries are what we'll
|
||||
focus on here.
|
||||
|
||||
Scries are performed with the [dotket](/reference/hoon/rune/dot#-dotket) (`.^`)
|
||||
rune. Here's a summary of their format:
|
||||
|
||||
![scry summary diagram](https://storage.googleapis.com/media.urbit.org/docs/arvo/scry-diagram-v2.svg)
|
||||
|
||||
A note on `care`s: Cares are most carefully implemented by Clay, where they specify
|
||||
submodules and have tightly defined behaviors. For Gall agents, most of these
|
||||
don't have any special behavior, and are just used to indicate the general kind
|
||||
of data produced by the endpoint. There are a handful of exceptions to this:
|
||||
`%d`, `%e`, `%u` and `%x`.
|
||||
|
||||
#### `%d`
|
||||
|
||||
A scry to Gall with a `%d` `care` and no `path` will produce the `desk` in which
|
||||
the specified agent resides. For example:
|
||||
|
||||
```
|
||||
> .^(desk %gd /=hark-store=)
|
||||
%garden
|
||||
> .^(desk %gd /=hood=)
|
||||
%base
|
||||
```
|
||||
|
||||
#### `%e`
|
||||
|
||||
A scry to Gall with a `%e` `care`, a `desk` rather than agent in the `desk`
|
||||
field of the above diagram, and no path, will produce a set of all installed
|
||||
agents on that desk and their status. For example:
|
||||
|
||||
```
|
||||
> .^((set [=dude:gall live=?]) %ge /=garden=)
|
||||
{ [dude=%hark-system-hook live=%.y]
|
||||
[dude=%treaty live=%.y]
|
||||
[dude=%docket live=%.y]
|
||||
[dude=%settings-store live=%.y]
|
||||
[dude=%hark-store live=%.y]
|
||||
}
|
||||
```
|
||||
|
||||
#### `%u`
|
||||
|
||||
A scry to Gall with a `%u` `care` and no `path` will check whether or not the
|
||||
specified agent is installed and running:
|
||||
|
||||
```
|
||||
> .^(? %gu /=btc-wallet=)
|
||||
%.y
|
||||
> .^(? %gu /=btc-provider=)
|
||||
%.n
|
||||
> .^(? %gu /=foobar=)
|
||||
%.n
|
||||
```
|
||||
A note on `care`s: Cares are most carefully implemented by Clay, where they
|
||||
specify submodules and have tightly defined behaviors. For Gall agents, most of
|
||||
these don't have any special behavior, and are just used to indicate the
|
||||
general kind of data produced by the endpoint, with the exception of the `%x`
|
||||
care:
|
||||
|
||||
#### `%x`
|
||||
|
||||
A scry to Gall with a `%x` `care` will be passed to the agent for handling. Gall
|
||||
handles `%x` specially, and expects an extra field at the end of the `path` that
|
||||
specifies the `mark` to return. Gall will take the data produced by the
|
||||
specified endpoint and try to convert it to the given mark, crashing if the mark
|
||||
conversion fails. The extra field specifying the mark is not passed through to
|
||||
the agent itself. Here's a couple of examples:
|
||||
Gall handles `%x` specially, and expects an extra field at the end of the
|
||||
`path` that specifies the `mark` to return. Gall will take the data produced by
|
||||
the specified endpoint and try to convert it to the given mark, crashing if the
|
||||
mark conversion fails. The extra field specifying the mark is not passed
|
||||
through to the agent itself. Here's a couple of examples:
|
||||
|
||||
```
|
||||
> =store -build-file /=landscape=/sur/graph-store/hoon
|
||||
@ -338,8 +296,9 @@ crash!
|
||||
- Scries will fail if the scry endpoint does not exist, the requested data does
|
||||
not exist, or the data does not nest in the return type specified.
|
||||
- Scries can only be performed on the local ship, not on remote ships.
|
||||
- Gall scries with an agent name in the `desk` field will be passed to that
|
||||
agent's `on-peek` arm for handling.
|
||||
- Gall scries with an agent name in the `desk` field and without an extra empty
|
||||
element at the beginning of the path will be passed to that agent's `on-peek`
|
||||
arm for handling.
|
||||
- Gall scries with a `%x` `care` take a `mark` at the end of the scry `path`,
|
||||
telling Gall to convert the data returned by the scry endpoint to the mark
|
||||
specified.
|
||||
@ -352,6 +311,7 @@ crash!
|
||||
## Exercises
|
||||
|
||||
- Have a read through the [Scry Guide](/reference/arvo/concepts/scry).
|
||||
- Have a look at Gall's [scry reference](/reference/arvo/gall/scry).
|
||||
- Have a read through the [dotket rune
|
||||
documentation](/reference/hoon/rune/dot#-dotket).
|
||||
- Run through the [Example](#example) yourself if you've not done so already.
|
||||
|
@ -115,7 +115,7 @@ Azimuth](https://developers.urbit.org/guides/core/hoon-school/C-azimuth#the-urbi
|
||||
|
||||
While working with Hoon, you'll often want to delete an old fake ship and
|
||||
recreate a fresh one. Rather than having to wait a few minutes for the fresh
|
||||
ship to be initialised, you can instead create a backup copy of a fake ship.
|
||||
ship to be initialized, you can instead create a backup copy of a fake ship.
|
||||
That way you can just delete the current copy, replace it with the backup, and
|
||||
reboot in a matter of seconds.
|
||||
|
||||
|
@ -356,7 +356,7 @@ This syntax is a little bit strange in the Dojo because subsequent expressions,
|
||||
38
|
||||
|
||||
> perfect-number
|
||||
38
|
||||
28
|
||||
```
|
||||
|
||||
The difference is that the Dojo “pin” is permanent until deleted:
|
||||
|
@ -227,12 +227,12 @@ How can we control what kind of value a gate returns? Many programming language
|
||||
Remember `^-` kethep? We will use `^-` as a _fence_, a way of making sure only data matching the appropriate structure get passed on.
|
||||
|
||||
```hoon {% copy=true %}
|
||||
:: Confirm whether a value is greater than one.
|
||||
:: Confirm whether a value is greater than one by return 1 (if no) or 0 (if yes).
|
||||
|= a=@ud
|
||||
^- @ud
|
||||
?: (gth a 1)
|
||||
%.n
|
||||
%.y
|
||||
1
|
||||
0
|
||||
```
|
||||
|
||||
**This is the correct way to define a gate.** Frequent annotation of type with `^-` kethep fences is _essential_ to producing good Hoon code. From this point forward in Hoon School, we will hew to this standard.
|
||||
|
@ -106,9 +106,9 @@ Here's a non-exhaustive list of auras, along with examples of corresponding lite
|
||||
| `@sv` | signed base32 | `-0v1df64.49beg` |
|
||||
| `@sw` | signed base64 | `--0wbnC.8haTg` |
|
||||
| `@sx` | signed hexadecimal | `-0x5f5.e138` |
|
||||
| `@t` | UTF-8 text (cord) | `'howdy'` |
|
||||
| `@ta` | ASCII text (knot) | `~.howdy` |
|
||||
| `@tas` | ASCII text symbol (term) | `%howdy` |
|
||||
| `@t` | UTF-8 text (`cord`) | `'howdy'` |
|
||||
| `@ta` | ASCII text (subset) (`knot`) | `~.howdy` |
|
||||
| `@tas` | ASCII text symbol (subset) (`term`) | `%howdy` |
|
||||
| `@u` | unsigned integer | no literal |
|
||||
| `@ub` | unsigned binary | `0b11.1000` |
|
||||
| `@ud` | unsigned decimal | `1.000.056` |
|
||||
@ -118,6 +118,8 @@ Here's a non-exhaustive list of auras, along with examples of corresponding lite
|
||||
|
||||
Some of these auras nest under others. For example, `@u` is for all unsigned auras. But there are other, more specific auras; `@ub` for unsigned binary numbers, `@ux` for unsigned hexadecimal numbers, etc. (For a more complete list of auras, see [Auras](/reference/hoon/auras).)
|
||||
|
||||
`knot` and `term` values each use a URL-safe subset of ASCII, omitting characters like spaces.
|
||||
|
||||
### Aura Inference in Hoon
|
||||
|
||||
Let's work a few more examples in the Dojo using the `?` operator. We'll focus on just the unsigned auras for now:
|
||||
|
@ -242,7 +242,7 @@ Produce a gate (generator) which accepts a `tape` value and returns a `(list @ud
|
||||
|
||||
The previous code simply modified a value by addition. You can generalize this to other arithmetic processes, like multiplication, but you can also grow a data structure like a list.
|
||||
|
||||
For example, given the `tape` `"hello"`, the generator should return the list `~[104 101 108 108 111]`.
|
||||
For example, given the `tape` `"hello"`, the generator should return the list `[104 101 108 108 111 ~]`. (A list is structurally a null-terminated tuple, or rightwards-branching cell ending in `~` or `0`.) We can equivalently write `~[104 101 108 108 111]` which is a special syntax reducing to the same thing.
|
||||
|
||||
Two tools that may help:
|
||||
|
||||
@ -779,12 +779,12 @@ and verify that our program correctly produces the sequence of numbers 1, 1, 2,
|
||||
=/ q 1
|
||||
=/ r *(list @ud)
|
||||
|- ^- (list @ud)
|
||||
?: =(i n) r
|
||||
?: =(index n) r
|
||||
%= $
|
||||
i +(i)
|
||||
p q
|
||||
q (add p q)
|
||||
r [q r]
|
||||
index +(index)
|
||||
p q
|
||||
q (add p q)
|
||||
r [q r]
|
||||
==
|
||||
```
|
||||
|
||||
@ -939,12 +939,4 @@ F_{n+1} (x, y+1) & = F_n (F_{n+1} (x, y), F_{n+1} (x, y) + y + 1) & \text{if } n
|
||||
\end{array}
|
||||
{% /math %}
|
||||
|
||||
<!--
|
||||
\begin{array}{lll}
|
||||
F_0 (x, y) & = x+y \\
|
||||
F_{n+1} (x, 0) & = x & \text{if } n \ge 0 \\
|
||||
F_{n+1} (x, y+1) & = F_n (F_{n+1} (x, y), F_{n+1} (x, y) + y + 1) & \text{if } n\ge 0 \\
|
||||
\end{array}
|
||||
-->
|
||||
|
||||
- Implement the Sudan function as a gate.
|
||||
|
@ -284,22 +284,31 @@ Unfortunately `/` fas runes don't work in the Dojo right now, so we need to buil
|
||||
|
||||
A [desk](/reference/glossary/desk) organizes a collection of files, including generators, libraries, agents, and system code, into one coherent bundle. A desk is similar to a file drive in a conventional computer, or a Git branch. Desks are supported by the Clay vane in Arvo, the Urbit OS.
|
||||
|
||||
At this point, you've likely only worked on the `%base` desk. You can see data about any particular desk using the `+vat` generator:
|
||||
At this point, you've likely only worked on the `%base` desk. You can see data about any particular desk using the `+vats` generator:
|
||||
|
||||
```hoon
|
||||
> +vat %base
|
||||
> +vats %base
|
||||
%base
|
||||
/sys/kelvin: [%zuse 417]
|
||||
base hash: ~
|
||||
%cz hash: 0v2.r1lbp.i9jr2.hosbi.rvg16.pqe7u.i3hnp.j7k27.9jsgv.8k7rp.oi98q
|
||||
/sys/kelvin: [%zuse 413]
|
||||
base hash ends in: hih5c
|
||||
%cz hash ends in: hih5c
|
||||
app status: running
|
||||
pending updates: ~
|
||||
|
||||
> +vats %base, =verb %.y
|
||||
%base
|
||||
/sys/kelvin: [%zuse 413]
|
||||
base hash: 0v2.vhcjk.rj42q.e3la7.1679q.u2qs2.35vnn.9n1jm.mj66h.kgpe5.hih5c
|
||||
%cz hash: 0v2.vhcjk.rj42q.e3la7.1679q.u2qs2.35vnn.9n1jm.mj66h.kgpe5.hih5c
|
||||
app status: running
|
||||
force on: ~
|
||||
force off: ~
|
||||
publishing ship: ~
|
||||
updates: local
|
||||
source ship: ~
|
||||
source desk: ~
|
||||
source aeon: ~
|
||||
updates: remote
|
||||
source ship: ~marnec-dozzod-marzod
|
||||
source desk: %kids
|
||||
source aeon: 43
|
||||
kids desk: %kids
|
||||
pending updates: ~
|
||||
```
|
||||
|
||||
|
@ -312,8 +312,6 @@ A `fish-loop` arises when using a recursive mold definition like `list`. (The r
|
||||
fish-loop
|
||||
```
|
||||
|
||||
although a promised `?#` wuthax rune should match it once implemented.
|
||||
|
||||
### `generator-build-fail`
|
||||
|
||||
A `generator-build-fail` most commonly results from composing code with mismatched runes (and thus the wrong children including hanging expected-but-empty slots).
|
||||
|
@ -32,7 +32,7 @@ The second way of making a function call involves an expression that _produces_
|
||||
246
|
||||
```
|
||||
|
||||
The difference is subtle: the first cast has an already-created gate in the subject when we called it, while the latter involves producing a gate that doesn't exist anywhere in the subject, and then calling it.
|
||||
The difference is subtle: the first case has an already-created gate in the subject when we called it, while the latter involves producing a gate that doesn't exist anywhere in the subject, and then calling it.
|
||||
|
||||
Are calls to `++add` and `++mul` of the Hoon standard library of the first kind, or the second?
|
||||
|
||||
@ -453,7 +453,7 @@ What is that cell? Wasn't the value stored as `0xff.8833`? Well, one fundament
|
||||
|
||||
- What does `[~ ~]` mean when returned from a `map`?
|
||||
|
||||
`unit`s are common enough that they have their own syntax and set of operational functions. We'll look at them more in [the next module](/guides/core/hoon-school/K-doors).
|
||||
`unit`s are common enough that they have their own syntax and set of operational functions. We'll look at them more in [the next module](/guides/core/hoon-school/L-struct).
|
||||
|
||||
```hoon
|
||||
> (~(get by colors) %brown)
|
||||
|
@ -222,7 +222,7 @@ It's important to remember to include a cast rune with each gate and trap expres
|
||||
|
||||
By now you've used the `|=` rune to define several gates. This rune is used to produce a _dry gate_, which has different type-checking and type-inference properties than a _wet gate_ does. We won't explain the distinction until [a later module](/guides/core/hoon-school/R-metals)—for now, just keep in mind that we're only dealing with one kind of gate (albeit the more common kind).
|
||||
|
||||
The first subexpression after the `|=` defines the sample type. Any faces used in this definition have the type declared for it in this definition. Consider an addition generator `/gen/add.hoon`:
|
||||
The first subexpression after the `|=` defines the sample type. Any faces used in this definition have the type declared for it in this definition. Consider an addition generator `/gen/sum.hoon`:
|
||||
|
||||
```hoon {% copy=true %}
|
||||
|= [a=@ b=@]
|
||||
@ -235,10 +235,10 @@ $(a +(a), b (dec b))
|
||||
We run it in the Dojo using a cell to pass the two arguments:
|
||||
|
||||
```hoon
|
||||
> +add 12 14
|
||||
> +sum [12 14]
|
||||
26
|
||||
|
||||
> +add 22
|
||||
> +sum 22
|
||||
nest-fail
|
||||
-need.[a=@ b=@]
|
||||
-have.@ud
|
||||
|
@ -28,7 +28,7 @@ has three folders inside:
|
||||
1. `bare-desk`: just the hoon files created here without any dependencies.
|
||||
2. `full-desk`: `bare-desk` plus all dependencies. Note some files are
|
||||
symlinked, so if you're copying them you'll need to do `cp -rL`.
|
||||
3. `react-frontend`: the React front-end files.
|
||||
3. `ui`: the React front-end files.
|
||||
|
||||
Let's get started.
|
||||
|
||||
@ -241,7 +241,7 @@ a hut by poking our agent with a `%post` action. Likewise, we'll be able to
|
||||
subscribe to huts for groups on other ships and poke them to post messages.
|
||||
Remember, all Urbit ships are both clients and servers.
|
||||
|
||||
There's three main agent arms we use for this:
|
||||
There are three main agent arms we use for this:
|
||||
|
||||
1. `on-poke`: This arm handles one-off actions/requests, such as posting a
|
||||
message to a hut.
|
||||
@ -368,7 +368,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in
|
||||
|= old-vase=vase
|
||||
^- (quip card _this)
|
||||
[~ this(state !<(state-0 old-vase))]
|
||||
:: on-poke handles "pokes", one-off requests/actions intiated either
|
||||
:: on-poke handles "pokes", one-off requests/actions initiated either
|
||||
:: by our local ship, the front-end or other ships on the network.
|
||||
::
|
||||
++ on-poke
|
||||
@ -570,7 +570,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in
|
||||
:: the subscribe succeeded or failed
|
||||
::
|
||||
%watch-ack
|
||||
:: if there's no error message it succceeded,
|
||||
:: if there's no error message it succeeded,
|
||||
:: do nothing further
|
||||
::
|
||||
?~ p.sign `this
|
||||
@ -784,7 +784,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in
|
||||
:: switch on the kind of event
|
||||
::
|
||||
?+ -.sign (on-agent:def wire sign)
|
||||
:: if it was a response to a subscriiption request...
|
||||
:: if it was a response to a subscription request...
|
||||
%watch-ack
|
||||
:: if there's no error message the subscription succeeded,
|
||||
:: no nothing
|
||||
@ -1252,7 +1252,7 @@ in `hut/mar/hut/do.hoon` and `hut/mar/hut/did.hoon` respectively.
|
||||
['joined' (en-joined joined.u)]
|
||||
==
|
||||
==
|
||||
:: this function creates an array of the the members of a the
|
||||
:: this function creates an array of the members of a the
|
||||
:: huts for a squad
|
||||
::
|
||||
++ en-joined
|
||||
@ -1333,248 +1333,256 @@ in `hut/mar/hut/do.hoon` and `hut/mar/hut/did.hoon` respectively.
|
||||
Our back-end is complete, so we can now work on our React front-end. We'll just
|
||||
look at the basic setup process here, but you can get the full React app by
|
||||
cloning [this repo on Github](https://github.com/urbit/docs-examples) and run
|
||||
`npm i` in `chat-app/react-frontend`. Additional commentary on the code is in
|
||||
the [additional commentary](#additional-commentary) section below.
|
||||
`npm i` in `chat-app/ui`. Additional commentary on the code is in the
|
||||
[additional commentary](#additional-commentary) section below.
|
||||
|
||||
#### Basic setup process
|
||||
|
||||
When creating it from scratch, we can first run `create-react-app` like usual:
|
||||
When creating it from scratch, first make sure you have Node.js installed on
|
||||
your computer (you can download it from their
|
||||
[website](https://nodejs.org/en/download)) and then run `create-landscape-app`:
|
||||
|
||||
```shell {% copy=true %}
|
||||
npx create-react-app hut-ui
|
||||
cd hut-ui
|
||||
```shell
|
||||
npx @urbit/create-landscape-app
|
||||
✔ What should we call your application? … hut
|
||||
✔ What URL do you use to access Urbit? … http://127.0.0.1:8080
|
||||
```
|
||||
|
||||
To make talking to our ship easy, we'll install the `@urbit/http-api` module:
|
||||
This will generate a React project in the `hut/ui` directory with all the
|
||||
basic necessities for Urbit front-end development. Next, run the following
|
||||
commands to install the project's dependencies:
|
||||
|
||||
```
|
||||
npm i @urbit/http-api
|
||||
```shell
|
||||
cd hut/ui
|
||||
npm i
|
||||
```
|
||||
|
||||
`http-api` handles most of the tricky parts of communicating with our ship for
|
||||
us, and has a simple set of methods for doing things like pokes, subscriptions,
|
||||
receiving updates, etc.
|
||||
|
||||
The next thing we need to do is edit `package.json`. We'll change the name of
|
||||
the app, and we'll also add an additional `"homepage"` entry. Front-ends are
|
||||
serve at `/apps/<name>`, so we need to set that as the root for when we build
|
||||
it:
|
||||
|
||||
```json
|
||||
"name": "hut",
|
||||
"homepage": "/apps/hut/",
|
||||
```
|
||||
|
||||
Next, we need to edit `public/index.html` and add a script import to the
|
||||
`<head>` section. `http-api` needs to know the name of our ship in order to talk
|
||||
to it, so our ship serves a simple script at `/session.js` that just does
|
||||
`window.ship = "sampel-palnet";`.
|
||||
|
||||
```html
|
||||
<script src="/session.js"></script>
|
||||
```
|
||||
|
||||
We can now open `src/App.js`, wipe its contents, and start writing our own app.
|
||||
The first thing is to import the `Urbit` class from `@urbit/http-api`:
|
||||
We can now open `src/app.jsx`, wipe its contents, and start writing our own
|
||||
app. The first thing is to import the `Urbit` class from `@urbit/http-api`:
|
||||
|
||||
```javascript
|
||||
import React, {Component} from "react";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import Urbit from "@urbit/http-api";
|
||||
// .....
|
||||
```
|
||||
|
||||
In our App class, we'll create a new `Urbit` instance and tell it our ship name.
|
||||
We'll also add some connection state callbacks. Our app is simple and will just
|
||||
display the connection status in the top-right corner.
|
||||
We'll create an `App` component that will create a new `Urbit` instance on load
|
||||
to monitor our front-end's connection with our ship. Our app is simple and will
|
||||
just display the connection status in the top-left corner:
|
||||
|
||||
```javascript
|
||||
constructor(props) {
|
||||
super(props);
|
||||
window.urbit = new Urbit("");
|
||||
window.urbit.ship = window.ship;
|
||||
// ......
|
||||
window.urbit.onOpen = () => this.setState({conn: "ok"});
|
||||
window.urbit.onRetry = () => this.setState({conn: "try"});
|
||||
window.urbit.onError = () => this.setState({conn: "err"});
|
||||
// ......
|
||||
};
|
||||
export function App() {
|
||||
const [status, setStatus] = useState("try");
|
||||
|
||||
useEffect(() => {
|
||||
window.urbit = new Urbit("");
|
||||
window.urbit.ship = window.ship;
|
||||
|
||||
window.urbit.onOpen = () => setStatus("con");
|
||||
window.urbit.onRetry = () => setStatus("try");
|
||||
window.urbit.onError = () => setStatus("err");
|
||||
|
||||
const subscription = window.urbit.subscribe({
|
||||
app: "hut",
|
||||
path: "/all",
|
||||
event: (e) => console.log(e),
|
||||
});
|
||||
|
||||
return () => window.urbit.unsubscribe(subscription);
|
||||
}, []);
|
||||
|
||||
return (<h1>{status}</h1>);
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
constructor(props) {
|
||||
super(props);
|
||||
window.urbit = new Urbit("");
|
||||
window.urbit.ship = window.ship;
|
||||
// ......
|
||||
window.urbit.onOpen = () => this.setState({conn: "ok"});
|
||||
window.urbit.onRetry = () => this.setState({conn: "try"});
|
||||
window.urbit.onError = () => this.setState({conn: "err"});
|
||||
// ......
|
||||
};
|
||||
```
|
||||
After we've finished writing our React app, we can build it and view the
|
||||
resulting files in the `dist` directory:
|
||||
|
||||
After we've finished writing our React app, we can build it:
|
||||
|
||||
```shell {% copy=true %}
|
||||
```shell
|
||||
npm run build
|
||||
ls dist
|
||||
```
|
||||
|
||||
#### Additional commentary
|
||||
|
||||
There are a fair few functions our front-end uses, so we'll just look at a
|
||||
handful. The first is `doPoke`, which (as the name suggests) sends a poke to a
|
||||
ship. It takes the poke in JSON form. It then calls the `poke` method of our
|
||||
`Urbit` object to perform the poke.
|
||||
There are a fair few functions in the
|
||||
[complete front-end source for `%hut`](https://github.com/urbit/docs-examples);
|
||||
we'll just look at a handful to cover the basics. The first is the `appPoke`
|
||||
in `src/lib.js`, which (as the name suggests) sends a poke to a ship. It takes
|
||||
the poke in JSON form and calls the `poke` method of our `Urbit` object to
|
||||
perform the poke:
|
||||
|
||||
```javascript
|
||||
doPoke = jon => {
|
||||
window.urbit.poke({
|
||||
export function appPoke(jon) {
|
||||
return api.poke({
|
||||
app: "hut",
|
||||
mark: "hut-do",
|
||||
json: jon,
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Here's an example of a `%join`-type `act` in JSON form:
|
||||
An example of sending a `poke` with a `%join`-type `act` in JSON form can be
|
||||
found in the `src/components/SelectGid.jsx` source file:
|
||||
|
||||
```javascript
|
||||
joinGid = () => {
|
||||
const joinSelect = this.state.joinSelect
|
||||
if (joinSelect === "def") return;
|
||||
const [host, name] = joinSelect.split("/");
|
||||
this.doPoke(
|
||||
{"join": {
|
||||
"gid" : {"host": host, "name": name},
|
||||
"who" : this.our
|
||||
}}
|
||||
);
|
||||
this.setState({joinSelect: "def"})
|
||||
const handleJoin = () => {
|
||||
if (joinSelect !== "def") {
|
||||
const [host, name] = joinSelect.split("/");
|
||||
appPoke({
|
||||
"join": {
|
||||
"gid" : {"host": host, "name": name},
|
||||
"who" : OUR
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Our front-end will subscribe to updates for all groups our `%hut` agent is
|
||||
currently tracking. To do so, it calls the `subscribe` method of the `Urbit`
|
||||
object with the `path` to subscribe to and an `event` callback to handle each
|
||||
update it receives. Our agent publishes all updates on the local-only `/all`
|
||||
path.
|
||||
object (aliased to `api` in our example) with the `path` to subscribe to and an
|
||||
`event` callback to handle each update it receives. Our agent publishes all
|
||||
updates on the local-only `/all` path. Here's the source in the `src/app.jsx`
|
||||
file:
|
||||
|
||||
```javascript
|
||||
subscribe = () => {
|
||||
window.urbit.subscribe({
|
||||
app: "hut",
|
||||
path: "/all",
|
||||
event: this.handleUpdate
|
||||
});
|
||||
};
|
||||
const subscription = api.subscribe({
|
||||
app: "hut",
|
||||
path: "/all",
|
||||
event: setSubEvent,
|
||||
});
|
||||
```
|
||||
|
||||
Here's the `handleUpdate` function we gave as a callback. The update will be one
|
||||
of our `hut-upd` types in JSON form, so we just switch on the type and handle it
|
||||
as appropriate.
|
||||
Notice that the above call to `subscribe` passes the `setSubEvent` function.
|
||||
This is part of a common pattern for Urbit React applications wherein a state
|
||||
variable is used to track new events and cause component re-rendering. The
|
||||
broad outline for this workflow is as follows:
|
||||
|
||||
1. Create a component subscription event variable with:
|
||||
```javascript
|
||||
const [subEvent, setSubEvent] = useState();
|
||||
```
|
||||
2. Call the `subscribe` function, passing `setSubEvent` as the `event` keyword
|
||||
argument:
|
||||
```javascript
|
||||
urbit.subscribe({ /* ... */, event: setSubEvent });
|
||||
```
|
||||
3. Create a subscription handler function that updates when new events are
|
||||
available with:
|
||||
```javascript
|
||||
useEffect(() => {/* handler goes here */}, [subEvent]);
|
||||
```
|
||||
|
||||
The source for the final `useEffect` portion of this workflow (found in the
|
||||
`src/app.jsx` file) can be found below:
|
||||
|
||||
```javascript {% mode="collapse" %}
|
||||
handleUpdate = upd => {
|
||||
const {huts, msgJar, joined, currentGid, currentHut} = this.state;
|
||||
if ("initAll" in upd) {
|
||||
upd.initAll.huts.forEach(obj =>
|
||||
huts.set(this.gidToStr(obj.gid), new Set(obj.names))
|
||||
);
|
||||
this.setState({
|
||||
huts: huts,
|
||||
msgJar: new Map(
|
||||
upd.initAll.msgJar.map(obj => [this.hutToStr(obj.hut), obj.msgs])
|
||||
),
|
||||
joined: new Map(
|
||||
upd.initAll.joined.map(obj =>
|
||||
[this.gidToStr(obj.gid), new Set(obj.ppl)]
|
||||
)
|
||||
)
|
||||
})
|
||||
} else if ("init" in upd) {
|
||||
upd.init.msgJar.forEach(obj =>
|
||||
msgJar.set(this.hutToStr(obj.hut), obj.msgs)
|
||||
);
|
||||
this.setState({
|
||||
msgJar: msgJar,
|
||||
huts: huts.set(
|
||||
this.gidToStr(upd.init.huts[0].gid),
|
||||
new Set(upd.init.huts[0].names)
|
||||
),
|
||||
joined: joined.set(
|
||||
this.gidToStr(upd.init.joined[0].gid),
|
||||
new Set(upd.init.joined[0].ppl)
|
||||
)
|
||||
})
|
||||
} else if ("new" in upd) {
|
||||
const gidStr = this.gidToStr(upd.new.hut.gid);
|
||||
const hutStr = this.hutToStr(upd.new.hut);
|
||||
(huts.has(gidStr))
|
||||
? huts.get(gidStr).add(upd.new.hut.name)
|
||||
: huts.set(gidStr, new Set(upd.new.hut.name));
|
||||
this.setState({
|
||||
huts: huts,
|
||||
msgJar: msgJar.set(hutStr, upd.new.msgs)
|
||||
})
|
||||
} else if ("post" in upd) {
|
||||
const hutStr = this.hutToStr(upd.post.hut);
|
||||
(msgJar.has(hutStr))
|
||||
? msgJar.get(hutStr).push(upd.post.msg)
|
||||
: msgJar.set(hutStr, [upd.post.msg]);
|
||||
this.setState(
|
||||
{msgJar: msgJar},
|
||||
() => {
|
||||
(hutStr === this.state.currentHut)
|
||||
&& this.scrollToBottom();
|
||||
useEffect(() => {
|
||||
const updateFuns = {
|
||||
"initAll": (update) => {
|
||||
update.huts.forEach(obj =>
|
||||
huts.set(gidToStr(obj.gid), new Set(obj.names))
|
||||
);
|
||||
|
||||
setHuts(new Map(huts));
|
||||
setChatContents(new Map(
|
||||
update.msgJar.map(o => [hutToStr(o.hut), o.msgs])
|
||||
));
|
||||
setChatMembers(new Map(
|
||||
update.joined.map(o => [gidToStr(o.gid), new Set(o.ppl)])
|
||||
));
|
||||
}, "init": (update) => {
|
||||
setChatContents(new Map(update.msgJar.reduce(
|
||||
(a, n) => a.set(hutToStr(n.hut), n.msgs)
|
||||
, chatContents)));
|
||||
setHuts(new Map(huts.set(
|
||||
gidToStr(update.huts[0].gid),
|
||||
new Set(update.huts[0].names)
|
||||
)));
|
||||
setChatMembers(new Map(chatMembers.set(
|
||||
gidToStr(update.joined[0].gid),
|
||||
new Set(update.joined[0].ppl)
|
||||
)));
|
||||
}, "new": (update) => {
|
||||
const gidStr = gidToStr(update.hut.gid);
|
||||
const hutStr = hutToStr(update.hut);
|
||||
if (huts.has(gidStr)) {
|
||||
huts.get(gidStr).add(update.hut.name);
|
||||
} else {
|
||||
huts.set(gidStr, new Set(update.hut.name));
|
||||
}
|
||||
)
|
||||
} else if ("join" in upd) {
|
||||
const gidStr = this.gidToStr(upd.join.gid);
|
||||
(joined.has(gidStr))
|
||||
? joined.get(gidStr).add(upd.join.who)
|
||||
: joined.set(gidStr, new Set([upd.join.who]));
|
||||
this.setState({joined: joined})
|
||||
} else if ("quit" in upd) {
|
||||
const gidStr = this.gidToStr(upd.quit.gid);
|
||||
if ("~" + window.ship === upd.quit.who) {
|
||||
(huts.has(gidStr)) &&
|
||||
huts.get(gidStr).forEach(name =>
|
||||
msgJar.delete(gidStr + "/" + name)
|
||||
|
||||
setHuts(new Map(huts));
|
||||
setChatMembers(new Map(chatMembers.set(hutStr, update.msgs)));
|
||||
}, "post": (update) => {
|
||||
const newHut = hutToStr(update.hut);
|
||||
if (chatContents.has(newHut)) {
|
||||
chatContents.set(newHut, [...chatContents.get(newHut), update.msg]);
|
||||
} else {
|
||||
chatContents.set(newHut, [update.msg]);
|
||||
}
|
||||
|
||||
setChatContents(new Map(chatContents));
|
||||
}, "join": (update) => {
|
||||
const gidStr = gidToStr(update.gid);
|
||||
if (chatMembers.has(gidStr)) {
|
||||
chatMembers.get(gidStr).add(update.who)
|
||||
} else {
|
||||
chatMembers.set(gidStr, new Set([update.who]));
|
||||
}
|
||||
|
||||
setChatMembers(new Map(chatMembers));
|
||||
setJoinSelect("def");
|
||||
}, "quit": (update) => {
|
||||
const gidStr = gidToStr(update.gid);
|
||||
if (update.who === OUR) {
|
||||
huts.delete(gidStr);
|
||||
chatMembers.delete(gidStr);
|
||||
if(huts.has(gidStr)) {
|
||||
huts.get(gidStr).forEach(name =>
|
||||
chatContents.delete(gidStr + "/" + name)
|
||||
);
|
||||
}
|
||||
|
||||
setHuts(new Map(huts));
|
||||
setChatMembers(new Map(chatMembers));
|
||||
setChatContents(new Map(chatContents));
|
||||
setCurrGid((currGid === gidStr) ? null : currGid);
|
||||
setCurrHut((currHut === null)
|
||||
? null
|
||||
: (`${currHut.split("/")[0]}/${currHut.split("/")[1]}` === gidStr)
|
||||
? null
|
||||
: currHut
|
||||
);
|
||||
huts.delete(gidStr);
|
||||
joined.delete(gidStr);
|
||||
this.setState({
|
||||
msgJar: msgJar,
|
||||
huts: huts,
|
||||
joined: joined,
|
||||
currentGid: (currentGid === gidStr)
|
||||
? null : currentGid,
|
||||
currentHut: (currentHut === null) ? null :
|
||||
(
|
||||
currentHut.split("/")[0] + "/" + currentHut.split("/")[1]
|
||||
=== gidStr
|
||||
)
|
||||
? null : currentHut,
|
||||
make: (currentGid === gidStr) ? "" : this.state.make
|
||||
})
|
||||
} else {
|
||||
(joined.has(gidStr)) &&
|
||||
joined.get(gidStr).delete(upd.quit.who);
|
||||
this.setState({joined: joined})
|
||||
}
|
||||
} else if ("del" in upd) {
|
||||
const gidStr = this.gidToStr(upd.del.hut.gid);
|
||||
const hutStr = this.hutToStr(upd.del.hut);
|
||||
(huts.has(gidStr)) &&
|
||||
huts.get(gidStr).delete(upd.del.hut.name);
|
||||
msgJar.delete(hutStr);
|
||||
this.setState({
|
||||
huts: huts,
|
||||
msgJar: msgJar,
|
||||
currentHut: (currentHut === hutStr) ? null : currentHut
|
||||
})
|
||||
setViewSelect("def");
|
||||
setHutInput((currGid === gidStr) ? "" : hutInput);
|
||||
} else {
|
||||
if (chatMembers.has(gidStr)) {
|
||||
chatMembers.get(gidStr).delete(update.who);
|
||||
}
|
||||
|
||||
setChatMembers(new Map(chatMembers));
|
||||
}
|
||||
}, "del": (update) => {
|
||||
const gidStr = gidToStr(update.hut.gid);
|
||||
const hutStr = hutToStr(update.hut);
|
||||
if (huts.has(gidStr)) {
|
||||
huts.get(gidStr).delete(update.hut.name);
|
||||
}
|
||||
chatContents.delete(hutStr);
|
||||
|
||||
setHuts(new Map(huts));
|
||||
setChatContents(new Map(chatContents));
|
||||
setCurrHut((currHut === hutStr) ? null : currHut);
|
||||
},
|
||||
};
|
||||
|
||||
const eventTypes = Object.keys(subEvent);
|
||||
if (eventTypes.length > 0) {
|
||||
const eventType = eventTypes[0];
|
||||
updateFuns[eventType](subEvent[eventType]);
|
||||
}
|
||||
};
|
||||
}, [subEvent]);
|
||||
```
|
||||
|
||||
## Desk config
|
||||
@ -1587,7 +1595,7 @@ this by adding a `sys.kelvin` file to the root of our `hut` directory:
|
||||
|
||||
```shell {% copy=true %}
|
||||
cd hut
|
||||
echo "[%zuse 417]" > sys.kelvin
|
||||
echo "[%zuse 414]" > sys.kelvin
|
||||
```
|
||||
|
||||
We also need to specify which agents to start when our desk is installed. We do
|
||||
@ -1627,10 +1635,10 @@ the moment.
|
||||
## Put it together
|
||||
|
||||
Our app is now complete, so let's try it out. In the Dojo of our comet,
|
||||
we'll create a new desk by forking from an existing one:
|
||||
we'll create a new desk with the `|new-desk` generator:
|
||||
|
||||
``` {% copy=true %}
|
||||
|merge %hut our %webterm
|
||||
|new-desk %hut
|
||||
```
|
||||
|
||||
Next, we'll mount the desk so we can access it from the host OS:
|
||||
@ -1639,12 +1647,12 @@ Next, we'll mount the desk so we can access it from the host OS:
|
||||
|mount %hut
|
||||
```
|
||||
|
||||
Currently its contents are the same as the `%webterm` desk, so we'll need to
|
||||
delete those files and copy in our own instead. In the normal shell, do the
|
||||
It'll have a handful of skeleton files in it, but we can just delete those and
|
||||
add our own instead. In the normal shell, do the
|
||||
following:
|
||||
|
||||
```shell {% copy=true %}
|
||||
rm -r dev-comet/hut/*
|
||||
rm -rI dev-comet/hut/*
|
||||
cp -r hut/* dev-comet/hut/
|
||||
```
|
||||
|
||||
@ -1660,7 +1668,7 @@ to `localhost:8080` (or just `localhost` on a Mac). Login with the comet's web
|
||||
code, which you can get by running `+code` in the Dojo. Next, go to
|
||||
`localhost:8080/docket/upload` (or `localhost/docket/upload` on a Mac) and
|
||||
it'll bring up the Docket Globulator tool. Select the `hut` desk from the
|
||||
drop-down menu, then navigate to `hut-ui/build` and select the whole folder.
|
||||
drop-down menu, then navigate to `hut/ui/dist` and select the whole folder.
|
||||
Finally, hit `glob!` and it'll upload our React app.
|
||||
|
||||
If we return to `localhost:8080` (or `localhost` on a Mac), we should see a
|
||||
|
@ -139,7 +139,7 @@ For the actions/requests our app will accept, we'll need the following:
|
||||
6. Leave a squad.
|
||||
7. Make a private squad public.
|
||||
8. Make a public squad private.
|
||||
9. Change the title of a squad .
|
||||
9. Change the title of a squad.
|
||||
|
||||
These actions will only be allowed to be taken by the host ship.
|
||||
|
||||
@ -256,7 +256,7 @@ host and receive updates such as access control list changes or members joining
|
||||
and leaving. Likewise, we'll be able to subscribe to squads on other ships and
|
||||
receive their updates. Remember, all Urbit ships are both clients and servers.
|
||||
|
||||
There's three main agent arms we use for this:
|
||||
There are three main agent arms we use for this:
|
||||
|
||||
1. `on-poke`: This arm handles one-off actions/requests (our `act` structure).
|
||||
It will also handle requests from the front-end, which we'll create in the
|
||||
@ -1103,7 +1103,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in
|
||||
==
|
||||
::
|
||||
:: if it's a kick alert, it may or may not be intentional,
|
||||
:: so we just automaticall try to resubscribe
|
||||
:: so we just automatically try to resubscribe
|
||||
::
|
||||
%kick
|
||||
?. (~(has by squads) gid) `this
|
||||
@ -1208,7 +1208,7 @@ Gall agents live in the `/app` directory of a desk, so you can save this code in
|
||||
:: check whether it's US, and do nothing if so
|
||||
::
|
||||
?: =(our.bol ship.upd) `this
|
||||
:: otherwise, update the member list and and let local
|
||||
:: otherwise, update the member list and let local
|
||||
:: subscribers know
|
||||
::
|
||||
:- ~[(fact:io cage.sign ~[/local/all])]
|
||||
@ -1399,7 +1399,7 @@ mark in `squad/mar/squad/did.hoon`.
|
||||
:: first we import our /sur/squad.hoon type defs and expose them directly
|
||||
::
|
||||
/- *squad
|
||||
:: the mark door takes an $act action in in the outbound case
|
||||
:: the mark door takes an $act action in the outbound case
|
||||
::
|
||||
|_ a=act
|
||||
:: the grow arm converts from an $act to other things
|
||||
@ -1656,7 +1656,7 @@ Save the code below in `squad/app/squad/index.hoon`.
|
||||
?:(success.page "success" "failed")
|
||||
success.page
|
||||
==
|
||||
:: This component lets a group host change whther a squad is private
|
||||
:: This component lets a group host change whether a squad is private
|
||||
:: or public. It's a form that POSTs the new state to either the /squad/private
|
||||
:: or /squad/public URL path, and our Gall agent processes the request.
|
||||
::
|
||||
@ -1916,7 +1916,7 @@ this by adding a `sys.kelvin` file to the root of our `squad` directory:
|
||||
|
||||
```shell {% copy=true %}
|
||||
cd squad
|
||||
echo "[%zuse 417]" > sys.kelvin
|
||||
echo "[%zuse 414]" > sys.kelvin
|
||||
```
|
||||
|
||||
We also need to specify which agents to start when our desk is installed. We do
|
||||
@ -1967,10 +1967,10 @@ squad
|
||||
```
|
||||
|
||||
Let's now try it out. In the Dojo of our comet,
|
||||
we'll create a new desk by forking from an existing one:
|
||||
we'll create a new desk with the `|new-desk` generator:
|
||||
|
||||
``` {% copy=true %}
|
||||
|merge %squad our %webterm
|
||||
|new-desk %squad
|
||||
```
|
||||
|
||||
Next, we'll mount the desk so we can access it from the host OS:
|
||||
@ -1979,9 +1979,8 @@ Next, we'll mount the desk so we can access it from the host OS:
|
||||
|mount %squad
|
||||
```
|
||||
|
||||
Currently its contents are the same as the `%webterm` desk, so we'll need to
|
||||
delete those files and copy in our own instead. In the normal shell, do the
|
||||
following:
|
||||
Currently it just contains some skeleton files, but we can just delete those
|
||||
and add our own instead. In the normal shell, do the following:
|
||||
|
||||
```shell {% copy=true %}
|
||||
rm -r dev-comet/squad/*
|
||||
|
@ -116,7 +116,7 @@ across that our app will depend on:
|
||||
mkdir -p tally/{app,sur,mar,lib}
|
||||
cp -r dev-comet/squad/mar/{bill*,hoon*,json*,kelvin*,mime*,noun*,ship*,txt*,docket-0*} tally/mar/
|
||||
cp -r dev-comet/squad/lib/{agentio*,dbug*,default-agent*,skeleton*,docket*} tally/lib/
|
||||
cp -r dev-comet/squad/sur/{docket*, squad*} tally/sur/
|
||||
cp -r dev-comet/squad/sur/{docket*,squad*} tally/sur/
|
||||
cp -r dev-comet/base/sur/ring.hoon tally/sur/
|
||||
cp -r dev-comet/garden/lib/mip.hoon tally/lib/
|
||||
```
|
||||
@ -146,10 +146,10 @@ be sent or received in *pokes* (one-off messages):
|
||||
- `%withdraw`: delete a poll.
|
||||
|
||||
|
||||
We'll also define an `update` structure, which wil be the kinds of events that
|
||||
We'll also define an `update` structure, which will be the kinds of events that
|
||||
subscribers may be notified about:
|
||||
|
||||
- `%init`: given the intial polls and their state to a new subscriber.
|
||||
- `%init`: given the initial polls and their state to a new subscriber.
|
||||
- `%vote`: someone has voted on a poll.
|
||||
- `%new`: someone has created a new poll.
|
||||
- `%withdraw`: someone has withdrawn an existing poll.
|
||||
@ -317,12 +317,12 @@ groups we're a member of. It additionally handles group updates from the
|
||||
`%squad` agent. In the former case, the events we'll receive will contain
|
||||
`update`s, which we'll process in a similar manner to the `action`s in
|
||||
`on-poke`. All incoming votes will be validated here also. In the latter case,
|
||||
we'll receive `%squad` `upd` updates, such as members joining or leave groups,
|
||||
we'll receive `%squad` `upd` updates, such as members joining or leaving groups,
|
||||
and we'll handle them as appropriate.
|
||||
|
||||
We differentiate between these two cases by testing the `wire`, which is a
|
||||
message tag we set when we initially subscribed. For `%squad` updates it will be
|
||||
`/squad`, and for the rest it'll be the group ID we've subscribe to.
|
||||
`/squad`, and for the rest it'll be the group ID we've subscribed to.
|
||||
|
||||
#### The code
|
||||
|
||||
@ -428,7 +428,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in
|
||||
%1 `this(state old)
|
||||
%0 `this(state [%1 by-group.old voted.old withdrawn.old %subs])
|
||||
==
|
||||
:: on-poke handles "pokes", which are one-off requests/actions intiated
|
||||
:: on-poke handles "pokes", which are one-off requests/actions initiated
|
||||
:: locally or by remote ships. If it's a %tally-action we call
|
||||
:: a handle-action function, and if it's a %handle-http-request from the
|
||||
:: front-end we call the handle-http function
|
||||
@ -454,7 +454,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in
|
||||
?. authenticated.req
|
||||
:_ state(section %subs)
|
||||
(give-http:hc rid [307 ['Location' '/~/login?redirect='] ~] ~)
|
||||
:: otherwise, switch on the method, prodicing a 405 response for
|
||||
:: otherwise, switch on the method, producing a 405 response for
|
||||
:: unhandled methods
|
||||
::
|
||||
?+ method.request.req
|
||||
@ -616,7 +616,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in
|
||||
:: calculate the expiry data
|
||||
::
|
||||
=/ expiry=@da (add now.bol (yule days.act 0 0 0 ~))
|
||||
:: retreive all polls from state for this gid
|
||||
:: retrieve all polls from state for this gid
|
||||
::
|
||||
=/ polls=(map pid [=poll =votes])
|
||||
(fall (~(get by by-group) gid.act) *(map pid [=poll =votes]))
|
||||
@ -647,7 +647,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in
|
||||
:: vote on a poll
|
||||
::
|
||||
%vote
|
||||
:: retreive the target poll from state
|
||||
:: retrieve the target poll from state
|
||||
::
|
||||
=/ [=poll =votes] (~(got bi by-group) gid.act pid.act)
|
||||
:: make sure it hasn't expired
|
||||
@ -737,7 +737,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in
|
||||
?> ?| =(our.bol src.bol)
|
||||
&(=(src.bol creator.poll) (gte expiry.poll now.bol))
|
||||
==
|
||||
:: update state and notify subscribers of the withdrawl
|
||||
:: update state and notify subscribers of the withdrawal
|
||||
::
|
||||
:_ %= state
|
||||
by-group (~(del bi by-group) gid.act pid.act)
|
||||
@ -777,7 +777,7 @@ Gall agents live in the `/app` directory of a desk, so save this code in
|
||||
(fall (~(get by by-group) gid) *(map pid [=poll =votes]))
|
||||
==
|
||||
:: on-agent handles either responses to requests we've
|
||||
:: initated or subscription updates from people or agents
|
||||
:: initiated or subscription updates from people or agents
|
||||
:: to which we've previously subscribed
|
||||
::
|
||||
++ on-agent
|
||||
@ -1329,7 +1329,7 @@ Save the code below in `tally/app/tally/index.hoon`.
|
||||
==
|
||||
==
|
||||
==
|
||||
:: we retreive a list of all squads and sort them
|
||||
:: we retrieve a list of all squads and sort them
|
||||
:: alphabetically by title
|
||||
::
|
||||
=/ all-squads=(list (pair gid squad))
|
||||
@ -1902,7 +1902,7 @@ this by adding a `sys.kelvin` file to the root of our `tally` directory:
|
||||
|
||||
```shell {% copy=true %}
|
||||
cd tally
|
||||
echo "[%zuse 417]" > sys.kelvin
|
||||
echo "[%zuse 414]" > sys.kelvin
|
||||
```
|
||||
|
||||
We also need to specify which agents to start when our desk is installed. We do
|
||||
@ -1933,10 +1933,10 @@ following:
|
||||
## Put it together
|
||||
|
||||
Our app is now complete, so let's try it out. In the Dojo of our comet, we'll
|
||||
create a new desk (filesystem repo) by forking from an existing one:
|
||||
create a new desk with the `|new-desk` generator:
|
||||
|
||||
```{% copy=true %}
|
||||
|merge %tally our %webterm
|
||||
|new-desk %tally
|
||||
```
|
||||
|
||||
Next, we'll mount the desk so we can access it from the host OS:
|
||||
@ -1945,13 +1945,12 @@ Next, we'll mount the desk so we can access it from the host OS:
|
||||
|mount %tally
|
||||
```
|
||||
|
||||
Currently its contents are the same as the `%webterm` desk, so we'll need to
|
||||
delete those files and copy in our own instead. In the normal shell, do the
|
||||
following:
|
||||
Currently it just contains some skeleton files, but we can delete those and add
|
||||
our own instead. In the normal shell, do the following:
|
||||
|
||||
```shell {% copy=true %}
|
||||
rm -r dev-comet/tally/*
|
||||
cp -r tally/* dev-comet/tally/*
|
||||
cp -r tally/* dev-comet/tally/
|
||||
```
|
||||
|
||||
Back in the Dojo again, we can now commit those files and install the app:
|
||||
|
@ -1,6 +1,6 @@
|
||||
+++
|
||||
title = "Hoon School Live Spring '23"
|
||||
image = "https://storage.googleapis.com/media.urbit.org/site/featured/hoon-school-march-23.png"
|
||||
url="https://developers.urbit.org/courses/hsl"
|
||||
description = "The next session of Hoon School Live begins in March. Sign up to learn the fundamentals of programming on Urbit."
|
||||
title = "Assembly Hackathon 2023"
|
||||
image = "https://storage.googleapis.com/media.urbit.org/developers/hackathon/hackathon_logo.svg"
|
||||
url="/hackathon"
|
||||
description = "This year’s Assembly Hackathon has the highest prize pool on Urbit ever. Are you prepared to rise to the challenge, showcase your skills, and claim victory?"
|
||||
+++
|
@ -20,7 +20,7 @@ about 3000 lines of Hoon.
|
||||
to build Urbit in.
|
||||
* [Hoon School](/guides/core/hoon-school/): A collection of tutorials
|
||||
designed to teach you the Hoon language.
|
||||
* [Guides](/guides/additional/hoon/): Guides to specific Hoon tasks,
|
||||
* [Guides](/guides/additional/): Guides to specific Hoon tasks,
|
||||
including testing, command-line interface apps, and parsing.
|
||||
* [Reference](/reference/hoon/): Reference material primarily
|
||||
intended for Hoon developers with some experience.
|
||||
|
@ -11,13 +11,13 @@ cryptography on Urbit. We first summarize the two categories of keys and how
|
||||
they are utilized by each ship type, then cover how different parts of Urbit are
|
||||
involved in cryptography.
|
||||
|
||||
### Types of keys
|
||||
## Types of keys
|
||||
|
||||
The two categories of keys are your Azimuth/Ethereum keys and your networking
|
||||
keys. In both cases, these are public/private key pairs utilized for public key
|
||||
cryptography.
|
||||
|
||||
#### Azimuth keys
|
||||
### Azimuth keys
|
||||
|
||||
Your Urbit ID exists as an ERC-721 non-fungible token on the Ethereum
|
||||
blockchain, and as such is contained in a wallet whose private key you possess.
|
||||
@ -44,7 +44,7 @@ private keys there is no way to retrieve them somehow from your ship.
|
||||
For more information on the usage of these keys and the associated proxies, see
|
||||
the [Azimuth documentation](/reference/azimuth/azimuth).
|
||||
|
||||
#### Networking keys
|
||||
### Networking keys
|
||||
|
||||
All communications in Urbit over the [Ames](/reference/glossary/ames) network
|
||||
are end-to-end encrypted, and thus your ship stores its own public/private pair
|
||||
@ -83,7 +83,7 @@ other method. This is merely a technical limitation imposed by the design of the
|
||||
system, not an intentional handicapping of comet abilities. A workaround to this
|
||||
limitation is slated to be implemented as of May 2021.
|
||||
|
||||
### System components
|
||||
## System components
|
||||
|
||||
[Ames](/reference/arvo/ames/ames) is Arvo's networking vane. All packets sent by
|
||||
Ames are encrypted utilizing a cryptosuite found in `zuse`. The only exception
|
||||
@ -113,7 +113,7 @@ infrastructure utilized by Urbit. `azimuth-tracker` obtains networking public
|
||||
keys for planets, stars, and galaxies from this store, which are then stored in
|
||||
Jael and utilized by Ames for end-to-end encrypted communication.
|
||||
|
||||
### Additional documentation
|
||||
## Additional documentation
|
||||
|
||||
The following pages contained more detailed information about the cryptography
|
||||
utilized by each of the system components.
|
||||
|
2
content/reference/additional/dist/glob.md
vendored
2
content/reference/additional/dist/glob.md
vendored
@ -85,7 +85,7 @@ There's a different process for globs to be distributed over HTTP from a webserv
|
||||
|
||||
To begin, you'll need to spin up a ship (typically a fake ship) and `|mount` a desk for which to add the files. In order for Clay to add the files, the desk must contain `mark` files in its `/mar` directory for all file extensions your folder contains. The `%garden` desk is a good bet because it includes `mark` files for `.js`, `.html`, `.png`, `.svg`, `.woff2` and a couple of others. If there's no desk with a mark for a particular file type you want included in your glob, you may need to add a new mark file. A very rudimentary mark file like the `png.hoon` mark will suffice.
|
||||
|
||||
With the desk mounted, add the folder to be globbed to the root of the desk in Unix. It's imporant it's in the root because the `%make-glob` thread will only strip the first level of the folder heirarchy.
|
||||
With the desk mounted, add the folder to be globbed to the root of the desk in Unix. It's imporant it's in the root because the `%make-glob` thread will only strip the first level of the folder heirarchy. Additionally ensure that all file names in the folder you're globbing are lowercase, otherwise this next step will not work correctly.
|
||||
|
||||
Next, `|commit` the files to the desk, then run `-garden!make-glob %the-desk /folder-name`, where `%the-desk` is the desk containing the folder to be globbed and `/folder-name` is its name.
|
||||
|
||||
|
@ -65,6 +65,15 @@ Application-level message, as a `%pass`.
|
||||
- `path` - Internal route on the receiving ship.
|
||||
- `payload` - Semantic message contents.
|
||||
|
||||
## `$spar`
|
||||
|
||||
```hoon
|
||||
+$ spar [=ship =path]
|
||||
```
|
||||
|
||||
Instead of a fully qualifying scry path, Ames infers rift and life based on the
|
||||
ship.
|
||||
|
||||
## `$bone`
|
||||
|
||||
```hoon
|
||||
@ -167,6 +176,7 @@ All Ames knows about a peer.
|
||||
$: messages=(list [=duct =plea])
|
||||
packets=(set =blob)
|
||||
heeds=(set duct)
|
||||
keens=(jug path duct)
|
||||
==
|
||||
```
|
||||
|
||||
@ -175,6 +185,7 @@ What to do when Ames learns a peer's life and keys.
|
||||
- `messages` - [$plea](#plea)s local vanes have asked Ames to send.
|
||||
- `packets` - Packets we've tried to send.
|
||||
- `heeds` - Local tracking requests; passed through into [$peer-state](#peer-state).
|
||||
- `keens` - Subscribers to remote scry paths.
|
||||
|
||||
## `$peer-state`
|
||||
|
||||
@ -182,6 +193,7 @@ What to do when Ames learns a peer's life and keys.
|
||||
+$ peer-state
|
||||
$: $: =symmetric-key
|
||||
=life
|
||||
=rift
|
||||
=public-key
|
||||
sponsor=ship
|
||||
==
|
||||
@ -192,6 +204,9 @@ What to do when Ames learns a peer's life and keys.
|
||||
rcv=(map bone message-sink-state)
|
||||
nax=(set [=bone =message-num])
|
||||
heeds=(set duct)
|
||||
closing=(set bone)
|
||||
corked=(set bone)
|
||||
keens=(map path keen-state)
|
||||
==
|
||||
```
|
||||
|
||||
@ -204,6 +219,137 @@ State for a peer with known life and keys.
|
||||
- `rcv` - Per-`bone` message sinks to assemble messages from fragments.
|
||||
- `nax` - Unprocessed nacks (negative acknowledgments).
|
||||
- `heeds` - Listeners for `%clog` notifications.
|
||||
- `closing`: Bones closed on the sender side.
|
||||
- `corked`: Bones closed on both sender and receiver.
|
||||
- `keens`: Remote scry state.
|
||||
|
||||
## `$keen-state`
|
||||
|
||||
```hoon
|
||||
+$ keen-state
|
||||
$: wan=((mop @ud want) lte) :: request packets, sent
|
||||
nex=(list want) :: request packets, unsent
|
||||
hav=(list have) :: response packets, backward
|
||||
num-fragments=@ud
|
||||
num-received=@ud
|
||||
next-wake=(unit @da)
|
||||
listeners=(set duct)
|
||||
metrics=pump-metrics
|
||||
==
|
||||
```
|
||||
|
||||
Remote scry state for a peer.
|
||||
|
||||
- `wan`: Request packets, sent.
|
||||
- `nex`: Request packets, unsent.
|
||||
- `hav`: Response packets, backwards.
|
||||
- `num-fragments`: Total fragment count.
|
||||
- `num-received`: Fragments received.
|
||||
- `next-wake`: Retry timing.
|
||||
- `listeners`: Ducts waiting for a response.
|
||||
- `metrics`: Stats.
|
||||
|
||||
## `$want`
|
||||
|
||||
```hoon
|
||||
+$ want
|
||||
$: fra=@ud
|
||||
=hoot
|
||||
packet-state
|
||||
==
|
||||
```
|
||||
|
||||
Remote scry request fragment.
|
||||
|
||||
## `$have`
|
||||
|
||||
```hoon
|
||||
+$ have
|
||||
$: fra=@ud
|
||||
meow
|
||||
==
|
||||
```
|
||||
|
||||
Remote scry response fragment.
|
||||
|
||||
## `$meow`
|
||||
|
||||
```hoon
|
||||
+$ meow
|
||||
$: sig=@ux
|
||||
num=@ud
|
||||
dat=@ux
|
||||
==
|
||||
```
|
||||
|
||||
Remote scry response fragment data.
|
||||
|
||||
- `sig`: signature.
|
||||
- `num`: number of fragments.
|
||||
- `dat`: contents.
|
||||
|
||||
## `$peep`
|
||||
|
||||
```hoon
|
||||
+$ peep
|
||||
$: =path
|
||||
num=@ud
|
||||
==
|
||||
```
|
||||
|
||||
Remote scry fragment request.
|
||||
|
||||
## `$wail`
|
||||
|
||||
```hoon
|
||||
+$ wail
|
||||
$% [%0 peep]
|
||||
==
|
||||
```
|
||||
|
||||
Tagged remote scry request fragment.
|
||||
|
||||
## `$roar`
|
||||
|
||||
```hoon
|
||||
+$ roar
|
||||
(tale:pki:jael (pair path (unit (cask))))
|
||||
```
|
||||
|
||||
Remote scry response message.
|
||||
|
||||
A `tale:pki:jael` is a:
|
||||
|
||||
```hoon
|
||||
++ tale :: urbit-signed *
|
||||
|$ [typ] :: payload mold
|
||||
$: dat=typ :: data
|
||||
syg=(map ship (pair life oath)) :: signatures
|
||||
== ::
|
||||
```
|
||||
|
||||
Therefore, a `$roar` looks like:
|
||||
|
||||
```
|
||||
> *roar:ames
|
||||
[dat=[p=/ q=~] syg=~]
|
||||
```
|
||||
|
||||
In `dat`, for the `(pair path (unit (cask)))`, the `path` is the remote scry
|
||||
path and the `(unit (cask))` contains the value, or is null if there's no value
|
||||
at this path and will never be one (equivalent to the `[~ ~]` case of a local
|
||||
scry).
|
||||
|
||||
## `$purr`
|
||||
|
||||
```hoon
|
||||
+$ purr
|
||||
$: peep
|
||||
meow
|
||||
==
|
||||
```
|
||||
|
||||
Response packet payload.
|
||||
|
||||
## `$qos`
|
||||
|
||||
|
@ -123,3 +123,21 @@ A scry with a `%x` `care` and a `path` of `/snd-bones/[ship]/[bone]`, where `[sh
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
## /snubbed
|
||||
|
||||
A scry with a `%x` `care` and a `path` of `/snubbed` will return Ames' current
|
||||
ship whitelist/blacklist. The type is a `[form=?(%allow %deny) ships=(list
|
||||
ship)]`, where `form` says whether it's a whitelist or blacklist and `ships`
|
||||
are the ships on that list.
|
||||
|
||||
#### Example
|
||||
|
||||
|
||||
```
|
||||
> |ames-snub %deny ~wet ~sampel
|
||||
>=
|
||||
|
||||
> .^([form=?(%allow %deny) ships=(list ship)] %ax /=//=/snubbed)
|
||||
[form=%deny ships=[i=~sampel t=[i=~wet t=~]]]
|
||||
```
|
||||
|
@ -10,8 +10,8 @@ Some `task`s appear to have more than one arm associated to them, e.g. there are
|
||||
four `+on-hear` arms. We denote this where it occurs, but always refer to the
|
||||
`+on-hear:event-core` arm.
|
||||
|
||||
Ames `task`s can be naturally divided into two categories: messaging tasks and
|
||||
system/lifecycle tasks.
|
||||
Ames `task`s can be naturally divided into three categories: messaging tasks,
|
||||
system/lifecycle tasks, and remote scry tasks.
|
||||
|
||||
## Messaging Tasks
|
||||
|
||||
@ -33,6 +33,8 @@ There are multiple `+on-hear` arms in `ames.hoon`. Here we refer to `+on-hear:ev
|
||||
|
||||
`%hear` can trigger a number of possible returns. It can trigger the release of zero or more additional packets via `%send` `gift`s. It may also trigger a `%boon` or `%plea` `gift` (collectively referred to as a `%memo` within Ames) to a local vane in the case of a completed message.
|
||||
|
||||
---
|
||||
|
||||
### `%heed`
|
||||
|
||||
```hoon
|
||||
@ -51,6 +53,8 @@ If the `ship` is indeed being unresponsive, as measured by backed up `%boon`s,
|
||||
Ames will `give` a `%clog` `gift` to the requesting vane containing the
|
||||
unresponsive peer's urbit address.
|
||||
|
||||
---
|
||||
|
||||
### `%jilt`
|
||||
|
||||
```hoon
|
||||
@ -68,6 +72,8 @@ The `ship` field specifies the peer we want to stop tracking.
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
### `%plea`
|
||||
|
||||
```hoon
|
||||
@ -93,6 +99,8 @@ A `%plea` `task` takes in the `ship` the `plea` is addressed to, and a [$plea](/
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
## System Tasks
|
||||
|
||||
### `%born`
|
||||
@ -111,6 +119,8 @@ The `duct` along which `%born` comes is Ames' only duct to Unix, so `%send`
|
||||
`gift`s (which are instructions for Unix to send a packet) are also returned in
|
||||
response to `%born`.
|
||||
|
||||
---
|
||||
|
||||
### `%init`
|
||||
|
||||
```hoon
|
||||
@ -134,6 +144,8 @@ contained by Jael.
|
||||
|
||||
`%init` sends two moves that subscribe to `%turf` and `%private-keys` in Jael.
|
||||
|
||||
---
|
||||
|
||||
### `%sift`
|
||||
|
||||
```hoon
|
||||
@ -148,6 +160,41 @@ The `ships` field specifies the ships for which debug output is desired.
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
### `%snub`
|
||||
|
||||
```hoon
|
||||
[%snub form=?(%allow %deny) ships=(list ship)]
|
||||
```
|
||||
|
||||
This `task` blacklists/whitelists ships in Ames.
|
||||
|
||||
The `form` field specifies whether the given ships should be blacklisted or whitelisted. The `ships` field are the ships to blacklist/whitelist.
|
||||
|
||||
The Ames `snub` settings can only have one form at a time: an `%allow` list or
|
||||
`%deny` list. If an `%allow` form is set, packets from **all ships not on the
|
||||
list will be blocked**. If a `%deny` form is set, packets from **any ship on
|
||||
the list will be blocked, and all others allowed**.
|
||||
|
||||
{% callout %}
|
||||
|
||||
Note: a `%snub` `task` overrides the existing snub list and form entirely,
|
||||
it does not merely add/remove ships from the existing list.
|
||||
|
||||
If you just want to add/remove a ship from an existing blacklist/whitelist,
|
||||
you'll need to first [scry out the existing snub
|
||||
settings](/reference/arvo/ames/scry#snubbed), make your changes, and send the
|
||||
whole modified list and form in a new `%snub` `task`.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
#### Returns
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
### `%spew`
|
||||
|
||||
```hoon
|
||||
@ -164,6 +211,8 @@ Sets verbosity toggles on debug output. This `task` is used internally when the
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
### `%stir`
|
||||
|
||||
```hoon
|
||||
@ -178,6 +227,8 @@ The `arg` field is unused.
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
### `%vega`
|
||||
|
||||
```hoon
|
||||
@ -190,3 +241,67 @@ anything in response to this.
|
||||
#### Returns
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
## Remote scry tasks
|
||||
|
||||
### `%keen`
|
||||
|
||||
```hoon
|
||||
[%keen =ship =path]
|
||||
```
|
||||
|
||||
A `%keen` `task` asks Ames to perform a remote scry, retrieving the value of
|
||||
`path` on the given `ship`. The `path` has the general format of
|
||||
`/[vane-letter]/[care]/[revision]/[rest-of-path]`. For a regular read into
|
||||
Gall, it's `/g/x/[revision]/[agent]//[rest-of-path]`. Note the empty element in
|
||||
between the agent and the rest of the path.
|
||||
|
||||
#### Returns
|
||||
|
||||
A `%tune` gift. A `%tune` gift looks like:
|
||||
|
||||
```hoon
|
||||
[%tune spar roar=(unit roar)]
|
||||
```
|
||||
|
||||
It represents a *result*. The `roar` field is null if Ames doesn't have a
|
||||
response, but may have one in the future. The
|
||||
[`$roar`](/reference/arvo/ames/data-types#roar) contains a signature and the
|
||||
data. The data in the `$roar` will be null if there is no value at the path in
|
||||
question and will never be. These two cases are equivalent to `~` and `[~ ~]` of
|
||||
a local scry.
|
||||
|
||||
---
|
||||
|
||||
### `%yawn`
|
||||
|
||||
```hoon
|
||||
[%keen =ship =path]
|
||||
```
|
||||
|
||||
A `%yawn` task asks Ames to cancel an existing remote scry request to the given
|
||||
`path` on the given `ship`.
|
||||
|
||||
#### Returns
|
||||
|
||||
This `task` returns no `gift`s.
|
||||
|
||||
---
|
||||
|
||||
### `%wham`
|
||||
|
||||
```hoon
|
||||
[%wham =ship =path]
|
||||
```
|
||||
|
||||
A `%wham` task asks Ames to cancel all existing remote scry requests from all
|
||||
vanes on all ducts for the given `path` on the given `ship`.
|
||||
|
||||
#### Returns
|
||||
|
||||
A `%tune` gift with a null `data` is given to all listeners. See the
|
||||
[`%keen`](#keen) entry for more details of the `%tune` gift.
|
||||
|
||||
---
|
||||
|
@ -43,7 +43,7 @@ DVCSes, and more.
|
||||
Clay has two other unique properties that we'll cover later on:
|
||||
it supports typed data and is referentially transparent.
|
||||
|
||||
### Revision Control
|
||||
## Revision Control
|
||||
|
||||
Every urbit has one or more desks, which are independently
|
||||
revision-controlled branches. Each desk contains its own `mark`
|
||||
@ -113,7 +113,7 @@ from revision 5 of the `%base` desk on `~sampel-sipnym`, we refer to
|
||||
`/~sampel-sipnym/base/5/try/readme/md`. Clay's namespace is thus
|
||||
global and referentially transparent.
|
||||
|
||||
### A Typed Filesystem
|
||||
## A Typed Filesystem
|
||||
|
||||
Since Clay is a general filesystem for storing data of arbitrary
|
||||
types, in order to revision control correctly it needs to be
|
||||
@ -169,7 +169,7 @@ As far as we are aware, Clay is the first generalized,
|
||||
type-aware revision control system. We'll go into the workings
|
||||
of this system in some detail.
|
||||
|
||||
### Marks
|
||||
## Marks
|
||||
|
||||
Central to a typed filesystem is the idea of file types. In Clay, we
|
||||
call these `mark`s. See the [Marks](/reference/arvo/clay/marks/marks)
|
||||
|
@ -20,14 +20,14 @@ zuse. Second is the write, query, and subscription logic. Finally, there
|
||||
is the logic for communicating requests to, and receiving requests from,
|
||||
foreign ships.
|
||||
|
||||
### User documentation
|
||||
## User documentation
|
||||
|
||||
[Filesystem](https://urbit.org/using/os/filesystem)
|
||||
|
||||
How to interact with the Clay filesystem via Dojo. This includes basics such as
|
||||
mounting to Unix, changing directory, merging, and listing files.
|
||||
|
||||
### Developer Documentation
|
||||
## Developer Documentation
|
||||
|
||||
[Architecture](/reference/arvo/clay/architecture)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,9 +5,9 @@ weight = 4
|
||||
|
||||
These are the files used in the [Writing Marks](/reference/arvo/clay/marks/writing-marks) guide.
|
||||
|
||||
## `/lib/csv/hoon`
|
||||
## `/lib/csv-utils/hoon`
|
||||
|
||||
```hoon
|
||||
```hoon {% mode="collapse" copy="true" %}
|
||||
|%
|
||||
++ validate :: All rows same length?
|
||||
|= csv=(list (list @t))
|
||||
@ -177,8 +177,8 @@ These are the files used in the [Writing Marks](/reference/arvo/clay/marks/writi
|
||||
|
||||
## `/mar/csv/hoon`
|
||||
|
||||
```hoon
|
||||
/+ *csv
|
||||
```hoon {% mode="collapse" copy="true" %}
|
||||
/+ *csv-utils
|
||||
|_ csv=(list (list @t))
|
||||
++ grab
|
||||
|%
|
||||
@ -232,7 +232,7 @@ These are the files used in the [Writing Marks](/reference/arvo/clay/marks/writi
|
||||
|
||||
## `/mar/csv-diff/hoon`
|
||||
|
||||
```hoon
|
||||
```hoon {% copy="true" %}
|
||||
|_ dif=(urge:clay (list @t))
|
||||
++ grab
|
||||
|%
|
||||
|
@ -5,7 +5,7 @@ weight = 2
|
||||
|
||||
Here we'll walk through a practical example of writing a `mark` file.
|
||||
|
||||
We'll create a `mark` for CSV (comma separated values) files, a simple format for storing tabular data in a text file. Note that there's already a `csv.hoon` `mark` and library, but we'll create new ones for demonstrative purposes. It shouldn't be an issue to overwrite the existing ones on a fakezod.
|
||||
We'll create a `mark` for CSV (comma separated values) files, a simple format for storing tabular data in a text file.
|
||||
|
||||
CSV files separate fields with commas and rows with line breaks. They look something like:
|
||||
|
||||
@ -69,7 +69,7 @@ The `%mime` `mark` is used by Clay to store and convert `$mime` data. It's an im
|
||||
|
||||
So with the nature of the `%mime` `mark` hopefully now clear, the reason we want conversion methods to and from `%mime` in our `%csv` `mark` is so we can import CSV files from Unix and vice versa.
|
||||
|
||||
Since a CSV file on Unix will just be a long string with ASCII or UTF-8 encoding, we can treat `q.q` in the `$mime` as a `cord`, and thus write a parser to convert it to a `(list (list @t))`. For this purpose, here's a library: `csv.hoon`, which you can view in full on the [Examples](/reference/arvo/clay/marks/examples#libcsvhoon) page.
|
||||
Since a CSV file on Unix will just be a long string with ASCII or UTF-8 encoding, we can treat `q.q` in the `$mime` as a `cord`, and thus write a parser to convert it to a `(list (list @t))`. For this purpose, here's a library: `csv-utils.hoon`, which you can view in full on the [Examples](/reference/arvo/clay/marks/examples#libcsv-utilshoon) page.
|
||||
|
||||
The library contains four functions:
|
||||
|
||||
@ -83,27 +83,27 @@ The decoding and encoding arms use parsing functions from the Hoon standard libr
|
||||
Let's try the library in the dojo. After we've added it to `/lib` and run `|commit`, we can build the file:
|
||||
|
||||
```
|
||||
> =csv -build-file %/lib/csv/hoon
|
||||
> =csv -build-file %/lib/csv-utils/hoon
|
||||
```
|
||||
|
||||
...try decode a CSV-format `@t`:
|
||||
|
||||
```
|
||||
> (de-csv:csv 'foo,bar,baz\0ablah,blah,blah\0a1,2,3')
|
||||
> (de-csv:csv-utils 'foo,bar,baz\0ablah,blah,blah\0a1,2,3')
|
||||
~[<|foo bar baz|> <|blah blah blah|> <|1 2 3|>]
|
||||
```
|
||||
|
||||
...and try encode a `(list (list @t))` as a CSV-format `@t`:
|
||||
|
||||
```
|
||||
> (en-csv:csv [['foo' 'bar' 'baz' ~] ['blah' 'blah' 'blah' ~] ['1' '2' '3' ~] ~])
|
||||
> (en-csv:csv-utils [['foo' 'bar' 'baz' ~] ['blah' 'blah' 'blah' ~] ['1' '2' '3' ~] ~])
|
||||
'foo,bar,baz\0ablah,blah,blah\0a1,2,3\0a'
|
||||
```
|
||||
|
||||
With that working, we can add an import for our library to our `%csv` `mark` defintion and add a `+mime` arm to both our `+grab` and `+grow` arms:
|
||||
|
||||
```hoon
|
||||
/+ *csv
|
||||
/+ *csv-utils
|
||||
|_ csv=(list (list @t))
|
||||
++ grab
|
||||
|%
|
||||
@ -184,12 +184,12 @@ For demonstrative purposes, we can just poach the algorithms used in the `+grad`
|
||||
|
||||
Our diff format will be a `(urge:clay (list @t))`, and we'll use some `differ` functions from `zuse.hoon` like `+loss`, `+lusk` and `+lurk` to produce diffs and apply patches.
|
||||
|
||||
The [csv.hoon library](/reference/arvo/clay/marks/examples#libcsvhoon) we imported also contains a `+csv-join` function which we'll use in the `+join` arm, just to save space here.
|
||||
The [csv.hoon library](/reference/arvo/clay/marks/examples#libcsv-utilshoon) we imported also contains a `+csv-join` function which we'll use in the `+join` arm, just to save space here.
|
||||
|
||||
Here's the new `%csv` `mark` defintion:
|
||||
|
||||
```hoon
|
||||
/+ *csv
|
||||
/+ *csv-utils
|
||||
|_ csv=(list (list @t))
|
||||
++ grab
|
||||
|%
|
||||
|
@ -49,7 +49,7 @@ Example:
|
||||
### `/rang` - Get `rang`
|
||||
|
||||
A buc scry with a path of `/rang` will return the full
|
||||
[`rang`](/reference/arvo/clay/data-types#rangclay) from Clay's state.
|
||||
[`rang`](/reference/arvo/clay/data-types#rang) from Clay's state.
|
||||
|
||||
Example:
|
||||
|
||||
@ -79,10 +79,50 @@ Example:
|
||||
|
||||
---
|
||||
|
||||
### `/cult/[desk]` - Subscribers
|
||||
|
||||
A buc scry with a path of `/cult/[desk]` will return the current subscriptions
|
||||
for the specified `desk`.
|
||||
|
||||
The type returned is:
|
||||
|
||||
```hoon
|
||||
(set [@p rave:clay])
|
||||
```
|
||||
|
||||
See the [`$rave:clay`](/reference/arvo/clay/data-types#rave) data
|
||||
type entry for more details of the `$rave` structure.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
> .^((set [@p rave:clay]) %cx /=//=/cult/kids)
|
||||
{ [~zod [%next mood=[care=%z case=[%da p=~2023.7.27..13.41.30..0536] path=/]]]
|
||||
[~zod [%next mood=[care=%z case=[%da p=~2023.7.27..13.41.30..0536] path=/desk/docket-0]]]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `/flow` - Build cache
|
||||
|
||||
A buc scry with a path of `/flow` will return the global build cache.
|
||||
The type returned is a
|
||||
[`$flow:clay`](/reference/arvo/clay/data-types#flow).
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
> ~(wyt by .^(flow:clay %cx /=//=/flow))
|
||||
960
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `/domes` - All domes
|
||||
|
||||
A buc scry with a path of `/domes` will return a
|
||||
[`cone:clay`](/reference/arvo/clay/data-types#coneclay) containing the `dome`s
|
||||
[`cone`](/reference/arvo/clay/data-types#cone) containing the `dome`s
|
||||
and associated metadata for all desks, foreign and local.
|
||||
|
||||
Example:
|
||||
@ -109,7 +149,7 @@ Example:
|
||||
|
||||
A buc scry with a path of `/tire` will return the `rock:tire:clay` for all
|
||||
domestic desks, which is a `(map desk [=zest wic=(set weft)])`. The
|
||||
[`zest`](/reference/arvo/clay/data-types#zestclay) specifies whether apps on the
|
||||
[`zest`](/reference/arvo/clay/data-types#zest) specifies whether apps on the
|
||||
desk are running or suspended. The `wic` set contains the `weft`s (kernel
|
||||
versions) of any queued updates.
|
||||
|
||||
@ -158,7 +198,7 @@ Example:
|
||||
|
||||
## `%b` - Dyn. mark core
|
||||
|
||||
A scry with a `care` of `%b` will produce a `dais:clay` processed `mark` core
|
||||
A scry with a `care` of `%b` will produce a `dais` processed `mark` core
|
||||
for the specified `mark`. The `path` in the scry is a `mark`.
|
||||
|
||||
Example:
|
||||
@ -169,7 +209,7 @@ Example:
|
||||
|
||||
## %c - Dyn. mark convert
|
||||
|
||||
A scry with a `care` of `%c` will produce a `tube:clay` dynamically typed `mark`
|
||||
A scry with a `care` of `%c` will produce a `tube` dynamically typed `mark`
|
||||
conversion gate. The `path` specifies two `mark`s - _from_ and _to_, like
|
||||
`/txt/noun`.
|
||||
|
||||
@ -188,10 +228,14 @@ Example:
|
||||
A scry with a `care` of `%d` will return a `(set desk)` of the `desk`s that
|
||||
exist on your ship.
|
||||
|
||||
Note this scry should be performed with an empty `desk` field (`%$`) in
|
||||
the `beak` (e.g. `/=//=`). If it's not empty, it'll work but Clay will
|
||||
complain in the terminal.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
> .^((set desk) %cd %)
|
||||
> .^((set desk) %cd /=//=)
|
||||
{%bitcoin %base %landscape %webterm %garden %kids}
|
||||
```
|
||||
|
||||
@ -199,7 +243,7 @@ Example:
|
||||
|
||||
## `%e` - Static mark core
|
||||
|
||||
A scry with a `care` of `%e` will return a statically typed `nave:clay` `mark`
|
||||
A scry with a `care` of `%e` will return a statically typed `nave` `mark`
|
||||
core. The `path` in the scry specifies the `mark`. The type returned is a
|
||||
`(nave:clay [type] [diff])`, where `[type]` is the type the `mark` takes and
|
||||
`[diff]` is the type taken by the `mark` specified in `+form:grad`.
|
||||
@ -229,7 +273,7 @@ A scry with a `care` of `%f` will return a static `mark` conversion gate. The
|
||||
|
||||
A scry with a `care` of `%p` will return the permissions of the file or
|
||||
directory in question. The type returned is a [`[dict:clay
|
||||
dict:clay]`](/reference/arvo/clay/data-types#dictclay) where the head is read
|
||||
dict:clay]`](/reference/arvo/clay/data-types#dict) where the head is read
|
||||
permissions and the tail is write permissions.
|
||||
|
||||
If the specified file or directory has no permissions set, it will default to
|
||||
@ -285,14 +329,14 @@ you use. We'll look at each in turn.
|
||||
|
||||
### `%yaki` - Commit
|
||||
|
||||
This will return the [yaki:clay](/reference/arvo/clay/data-types#yakiclay) of
|
||||
This will return the [yaki:clay](/reference/arvo/clay/data-types#yaki) of
|
||||
the specified commit. It takes a
|
||||
[tako:clay](/reference/arvo/clay/data-types#takoclay).
|
||||
[tako:clay](/reference/arvo/clay/data-types#tako).
|
||||
|
||||
Example:
|
||||
|
||||
Here we scry the [dome:clay](/reference/arvo/clay/data-types#domeclay) for `%`,
|
||||
get the latest `tako:clay` and the do a `%s` scry for the `yaki:clay` in
|
||||
Here we scry the [dome:clay](/reference/arvo/clay/data-types#dome) for `%`,
|
||||
get the latest `tako` and the do a `%s` scry for the `yaki` in
|
||||
question.
|
||||
|
||||
```
|
||||
@ -316,13 +360,13 @@ question.
|
||||
|
||||
### `%blob` - File blob
|
||||
|
||||
This will return the [page:clay](/reference/arvo/clay/data-types#pageclay) of
|
||||
some file. It takes a [lobe:clay](/reference/arvo/clay/data-types#lobeclay).
|
||||
This will return the [page:clay](/reference/arvo/clay/data-types#page) of
|
||||
some file. It takes a [lobe:clay](/reference/arvo/clay/data-types#lobe).
|
||||
|
||||
Example:
|
||||
|
||||
Here we grab the `lobe:clay` of `/gen/hood/hi/hoon` with a `%y` scry, then use
|
||||
it to do a `%s` scry for the `blob:clay` of the file.
|
||||
Here we grab the `lobe` of `/gen/hood/hi/hoon` with a `%y` scry, then use
|
||||
it to do a `%s` scry for the `blob` of the file.
|
||||
|
||||
```
|
||||
> =/ =arch .^(arch %cy %/gen/hood/hi/hoon)
|
||||
@ -340,13 +384,13 @@ it to do a `%s` scry for the `blob:clay` of the file.
|
||||
### `%hash` - Commit hash
|
||||
|
||||
This will return the `@uvI` (256-bit) content hash of the specified commit. It
|
||||
takes a [`tako:clay`](/reference/arvo/clay/data-types#takoclay).
|
||||
takes a [`tako`](/reference/arvo/clay/data-types#tako).
|
||||
|
||||
Example:
|
||||
|
||||
Here we grab the [`dome:clay`](/reference/arvo/clay/data-types#domeclay) for `%`
|
||||
Here we grab the [`dome`](/reference/arvo/clay/data-types#dome) for `%`
|
||||
with a `%v` scry, get the latest
|
||||
[`tako:clay`](/reference/arvo/clay/data-types#takoclay) and then do a `%s`
|
||||
[`tako`](/reference/arvo/clay/data-types#tako) and then do a `%s`
|
||||
`%hash` scry for it.
|
||||
|
||||
```
|
||||
@ -360,11 +404,11 @@ with a `%v` scry, get the latest
|
||||
|
||||
### `%cage` - File as cage
|
||||
|
||||
This will return a `cage` of the data of some file. It takes a `lobe:clay`.
|
||||
This will return a `cage` of the data of some file. It takes a `lobe`.
|
||||
|
||||
Example:
|
||||
|
||||
Here we grab the `lobe:clay` of `/gen/hood/hi/hoon` with a `%y` scry, then use it to do a `%s` scry for the `cage` of the data.
|
||||
Here we grab the `lobe` of `/gen/hood/hi/hoon` with a `%y` scry, then use it to do a `%s` scry for the `cage` of the data.
|
||||
|
||||
```
|
||||
> =/ =arch .^(arch %cy %/gen/hood/hi/hoon)
|
||||
@ -394,10 +438,10 @@ documentaton at a later date.
|
||||
|
||||
This will return the most recent revision number of a `desk` that has been fully
|
||||
downloaded. The type it returns is a
|
||||
[`cass:clay`](/reference/arvo/clay/data-types#cassclay). The `case` in the
|
||||
[`cass`](/reference/arvo/clay/data-types#cass). The `case` in the
|
||||
`beak` must be a revision number rather than a date. You can just provide a case
|
||||
of `1` since it returns the latest regardless. If we have nothing for the
|
||||
specified `desk`, this will just return the bunt of a `cass:clay` like
|
||||
specified `desk`, this will just return the bunt of a `cass` like
|
||||
`cass=[ud=0 da=~2000.1.1]`.
|
||||
|
||||
Example:
|
||||
@ -498,7 +542,7 @@ Examples:
|
||||
## `%v` - Desk state
|
||||
|
||||
A scry with a care of `%v` will return the entire state of a `desk` as a
|
||||
[`dome:clay`](/reference/arvo/clay/data-types#domeclay).
|
||||
[`dome`](/reference/arvo/clay/data-types#dome).
|
||||
|
||||
Example:
|
||||
|
||||
@ -516,7 +560,7 @@ Note: If you try printing this it will take forever and probably OOM your ship.
|
||||
|
||||
A scry with a `care` of `%w` will return the revision number and date of a given
|
||||
`case`. The type returned is a
|
||||
[`cass:clay`](/reference/arvo/clay/data-types#cassclay) like `[ud=@ud da=@da]`
|
||||
[`cass`](/reference/arvo/clay/data-types#cass) like `[ud=@ud da=@da]`
|
||||
where `ud` is the revision number and `da` is the date.
|
||||
|
||||
Example:
|
||||
@ -530,19 +574,20 @@ Example:
|
||||
|
||||
## `%x` - Read file
|
||||
|
||||
A scry with a `care` of `%x` will return the raw data of a file as an `@` or
|
||||
crash if it's a directory.
|
||||
A scry with a `care` of `%x` will return the data of a file or crash if it's a
|
||||
directory. The type returned will be whatever is defined by the `mark` of the
|
||||
file.
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
> .^(@ %cx %/gen/hood/hi/hoon)
|
||||
3.548.750.706.400.251.607.252.023.288.575.526.190.856.734.474.077.821.289.791.377.301.707.878.697.553.411.219.689.905.949.957.893.633.811.025.757.107.990.477.902.858.170.125.439.223.250.551.937.540.468.638.902.955.378.837.954.792.031.592.462.617.422.136.386.332.469.076.584.061.249.923.938.374.214.925.312.954.606.277.212.923.859.309.330.556.730.410.200.952.056.760.727.611.447.500.996.168.035.027.753.417.869.213.425.113.257.514.474.700.810.203.348.784.547.006.707.150.406.298.809.062.567.217.447.347.357.039.994.339.342.906
|
||||
> .^(@t %cx %/gen/hood/hi/hoon)
|
||||
':: Helm: send message to an urbit\0a::\0a:::: /hoon/hi/hood/gen\0a ::\0a/? 310\0a:- %say\0a|=([^ [who=ship mez=$@(~ [a=tape ~])] ~] helm-send-hi+[who ?~(mez ~ `a.mez)])\0a'
|
||||
```
|
||||
|
||||
```
|
||||
> .^(@t %cx %/gen/hood/hi/hoon)
|
||||
':: Helm: send message to an urbit\0a::\0a:::: /hoon/hi/hood/gen\0a ::\0a/? 310\0a:- %say\0a|=([^ [who=ship mez=$@(~ [a=tape ~])] ~] helm-send-hi+[who ?~(mez ~ `a.mez)])\0a'
|
||||
> .^(waft:clay %cx %/sys/kelvin)
|
||||
[lal=%zuse num=413]
|
||||
```
|
||||
|
||||
```
|
||||
@ -557,7 +602,7 @@ Crash!
|
||||
A scry with a `care` of `%y` will return the `arch` of a file or directory.
|
||||
|
||||
An `arch` is a `[fil=(unit lobe:clay) dir=(map @ta ~)]`. The `fil` will contain
|
||||
the [`lobe:clay`](/reference/arvo/clay/data-types#lobeclay) hash if it's a file,
|
||||
the [`lobe`](/reference/arvo/clay/data-types#lobe) hash if it's a file,
|
||||
otherwise it will be null. The `dir` will contain a map of the files and
|
||||
directories it contains, otherwise it will be null.
|
||||
|
||||
|
@ -23,7 +23,10 @@ Clay from a kernel development perspective.
|
||||
|
||||
A `%warp` `task` is for reading and subscribing to files and directories.
|
||||
|
||||
The `wer` field is the target ship. The `(unit rave)` of the [riff](/reference/arvo/clay/data-types#riff-clay-request-desist) is null to cancel an existing subscription, otherwise the [rave](/reference/arvo/clay/data-types#rave-clay-general-subscription-request) is tagged with one of:
|
||||
The `wer` field is the target ship. The `(unit rave)` of the
|
||||
[riff](/reference/arvo/clay/data-types#riff) is null to cancel an existing
|
||||
subscription, otherwise the [rave](/reference/arvo/clay/data-types#rave) is
|
||||
tagged with one of:
|
||||
|
||||
- `%sing` - Read a single file or directory.
|
||||
- `%next` - Subscribe for the next change to a file or directory.
|
||||
@ -48,7 +51,12 @@ A `%wris` `gift` looks like:
|
||||
[%writ p=riot] :: response
|
||||
```
|
||||
|
||||
The `unit` of the [riot](/reference/arvo/clay/data-types#riot-clay-response) will be null if the target file cannot be found or if a subscription has ended (depending on context). Otherwise it will have a [rant](/reference/arvo/clay/data-types#rant-clay-response-data) with a `cage` containing the data you requested. Its contents will vary depending on the kind of request and `care`.
|
||||
The `unit` of the [riot](/reference/arvo/clay/data-types#riot) will be null
|
||||
if the target file cannot be found or if a subscription has ended (depending on
|
||||
context). Otherwise it will have a
|
||||
[rant](/reference/arvo/clay/data-types#rant) with a `cage` containing the
|
||||
data you requested. Its contents will vary depending on the kind of request and
|
||||
`care`.
|
||||
|
||||
Now we'll look at each of the `rave` request types in turn.
|
||||
|
||||
@ -60,9 +68,16 @@ Now we'll look at each of the `rave` request types in turn.
|
||||
|
||||
This `rave` is for reading a single file or directory immediately.
|
||||
|
||||
The `care` of the [mood](/reference/arvo/clay/data-types#mood-clay-single-subscription-request) will determine what you can read and what type of data will be returned. See the [care](/reference/arvo/clay/data-types#care-clay-clay-submode) documentation and [scry](/reference/arvo/clay/scry) documentation for details on the various `care`s.
|
||||
The `care` of the [mood](/reference/arvo/clay/data-types#mood) will
|
||||
determine what you can read and what type of data will be returned. See the
|
||||
[care](/reference/arvo/clay/data-types#care) documentation and
|
||||
[scry](/reference/arvo/clay/scry) documentation for details on the various
|
||||
`care`s.
|
||||
|
||||
The [case](/reference/arvo/clay/data-types#case-specifying-a-commit) specifies the `desk` revision and you can use whichever kind you prefer. The `path` will usually be a path to a file or directory like `/gen/hood/hi/hoon` but may be something else depending on the `care`.
|
||||
The [case](/reference/arvo/clay/data-types#case) specifies the `desk`
|
||||
revision and you can use whichever kind you prefer. The `path` will usually be
|
||||
a path to a file or directory like `/gen/hood/hi/hoon` but may be something
|
||||
else depending on the `care`.
|
||||
|
||||
#### Example
|
||||
|
||||
@ -76,7 +91,9 @@ The [case](/reference/arvo/clay/data-types#case-specifying-a-commit) specifies t
|
||||
[%next =mood] :: await next version
|
||||
```
|
||||
|
||||
This subscribes to the next version of the specified file. See [here](/reference/arvo/clay/data-types#mood-clay-single-subscription-request) for details of the `mood` structure.
|
||||
This subscribes to the next version of the specified file. See
|
||||
[here](/reference/arvo/clay/data-types#mood) for details of the `mood`
|
||||
structure.
|
||||
|
||||
If you subscribe to the current `case` of the `desk`, Clay will not respond until the file changes. If you subscribe to a previous `case` of the `desk` and the file has changed in between then and now, it will immediately return the first change it comes across in that range. For example, if you're currently at `case` `100`, subscribe to case `50` and the file in question has been modified at both `60` and `80`, clay will immediately return the version of the file at `case` `60`.
|
||||
|
||||
@ -126,9 +143,17 @@ If the `track` is `%.y` it will just return a `%writ` like:
|
||||
|
||||
...that merely informs you of a change. If you want the actual data you'll have to request it separately.
|
||||
|
||||
If the `track` is `%.n`, the `cage` of the `%writ` will contain a [nako](/reference/arvo/clay/data-types#nako-subscription-response-data) with the relevant data for all changes to a desk between what you have and the `case` requested. It is very large and fairly complicated. The `nako` structure is defined in the `clay.hoon` source file itself rather than in `lull.hoon` or elsewhere since you're unlikely to work with it yourself.
|
||||
If the `track` is `%.n`, the `cage` of the `%writ` will contain a
|
||||
[nako](/reference/arvo/clay/data-types#nako) with the relevant data for all
|
||||
changes to a desk between what you have and the `case` requested. It is very
|
||||
large and fairly complicated. The `nako` structure is defined in the
|
||||
`clay.hoon` source file itself rather than in `lull.hoon` or elsewhere since
|
||||
you're unlikely to work with it yourself.
|
||||
|
||||
The `from` and `to` fields of the [moat](/reference/arvo/clay/data-types#moat-clay-range-subscription-request) specify the range of `case`s for which to subscribe. The range is _inclusive_. It can be specified by date or by revision number, whichever you prefer.
|
||||
The `from` and `to` fields of the
|
||||
[moat](/reference/arvo/clay/data-types#moat) specify the range of `case`s
|
||||
for which to subscribe. The range is _inclusive_. It can be specified by date
|
||||
or by revision number, whichever you prefer.
|
||||
|
||||
The `path` in the `moat` is a path to a file or directory. If it's `~` it refers to the root of the `desk` in question. This lets you say "only inform me of changes to the `desk` if the specified file or directory exists". If it doesn't exist, Clay will not send you anything.
|
||||
|
||||
@ -164,7 +189,19 @@ To cancel a subscription, you just send a `%warp` with a null `(unit rave)` in t
|
||||
|
||||
To write or modify a file, we send Clay a `%info` `task`.
|
||||
|
||||
The `%|` tag in the [nori](/reference/arvo/clay/data-types#nori-clay-repository-action) is not currently supported and will crash with a `%labelling-not-implemented` if used, so you can focus on the `%&` part. The [soba](/reference/arvo/clay/data-types#soba-clay-delta) in the `nori` is just a list of changes so you can make more than one change in one request. Its `path` is just the path to a file like `/gen/hood/hi/hoon` and the [miso](/reference/arvo/clay/data-types#miso-clay-ankh-delta) is one of these types of requests:
|
||||
If the head of the [nori](/reference/arvo/clay/data-types#nori) `dit` is
|
||||
`%|`, it's a request to add a label to a commit, and the `nori` looks like `[%|
|
||||
p=@tas q=(unit aeon)]` where `p` is the label and `q` is the
|
||||
[`aeon`](/reference/arvo/clay/data-types#aeon) (commit reference). If `q`
|
||||
is null, the label is applied to the latest commit in the desk.
|
||||
|
||||
If the head of the `nori` is `%&`, it's a request to add, delete or modify one
|
||||
or more files in the given desk, and looks like `[%& p=soba]`. The
|
||||
[soba](/reference/arvo/clay/data-types#soba) in the `nori` is just a list
|
||||
of changes so you can make more than one change in one request. Its `path` is
|
||||
just the path to a file like `/gen/hood/hi/hoon` and the
|
||||
[miso](/reference/arvo/clay/data-types#miso) is one of these types of
|
||||
requests:
|
||||
|
||||
- `%del` - Delete a file.
|
||||
- `%ins` - Insert file. This will also replace an existing file.
|
||||
@ -195,7 +232,7 @@ Here are examples of using each of these as well as making multiple changes in o
|
||||
```
|
||||
|
||||
Force on/off apps on a desk. A
|
||||
[`rein:clay`](/reference/arvo/clay/data-types#rein) is a `map` from Gall agent
|
||||
[`rein`](/reference/arvo/clay/data-types#rein) is a `map` from Gall agent
|
||||
name to `?`, where `%.y` is *on* and `%.n` is *off*. By default, a live desk
|
||||
will run the agents defined in its `desk.bill` manifest, so this is used to
|
||||
either stop agents in its manifest or start agents which aren't in its manifest.
|
||||
@ -228,7 +265,7 @@ A `rock:tire` is a:
|
||||
+$ rock (map desk [=zest wic=(set weft)])
|
||||
```
|
||||
|
||||
The [`zest:clay`](/reference/arvo/clay/data-types#zestclay) says whether the
|
||||
The [`zest`](/reference/arvo/clay/data-types#zest) says whether the
|
||||
desk is running (`%live`), suspended (`%dead`), or suspended pending a
|
||||
kernel-compatible update (`%held`). The `wic` set contains the `weft`s (kernel
|
||||
versions) of any queued updates.
|
||||
@ -264,7 +301,7 @@ Try to apply a queued kernel update.
|
||||
```
|
||||
|
||||
A `%zest` `task` suspends or unsuspends a desk. the
|
||||
[`zest:clay`](/reference/arvo/clay/data-types#zestclay) in `liv` is one of:
|
||||
[`zest`](/reference/arvo/clay/data-types#zest) in `liv` is one of:
|
||||
|
||||
- `%live`: running.
|
||||
- `%dead`: suspended.
|
||||
@ -279,7 +316,7 @@ A `%zest` `task` suspends or unsuspends a desk. the
|
||||
```
|
||||
|
||||
Tombstoning is the deletion of data for old desk revisions. Clay has a single
|
||||
`%tomb` `task`, but its [`clue:clay`](/reference/arvo/clay/data-types#clueclay)
|
||||
`%tomb` `task`, but its [`clue`](/reference/arvo/clay/data-types#clue)
|
||||
has a number of different possible actions:
|
||||
|
||||
```hoon
|
||||
@ -302,7 +339,7 @@ We'll look at each of these in turn.
|
||||
```
|
||||
|
||||
A `%tomb` `task` with a `%lobe` `clue` will tombstone the `page` matching the
|
||||
given [`lobe:clay`](/reference/arvo/clay/data-types#lobeclay). If the `page` in
|
||||
given [`lobe`](/reference/arvo/clay/data-types#lobe). If the `page` in
|
||||
question is used in the current revision of any desks, it will fail. Otherwise,
|
||||
it will be tombstoned globally.
|
||||
|
||||
@ -327,7 +364,7 @@ by current desk revisions, globally. This should be used with caution.
|
||||
|
||||
A `%tomb` `task` with a `%pick` `clue` will perform garbage collection,
|
||||
tombstoning any data that should be tombstoned according to current tombstoning
|
||||
policy ([`norm`](/reference/arvo/clay/data-types#normclay)s).
|
||||
policy ([`norm`](/reference/arvo/clay/data-types#norm)s).
|
||||
|
||||
---
|
||||
|
||||
@ -339,7 +376,7 @@ policy ([`norm`](/reference/arvo/clay/data-types#normclay)s).
|
||||
|
||||
A `%tomb` `task` with a `%norm` `clue` will set the default tombstoning policy
|
||||
for the given `desk` and `ship`. A
|
||||
[`norm:clay`](/referende/arvo/clay/data-types#normclay) is an `(axal ?)`. An
|
||||
[`norm`](/referende/arvo/clay/data-types#norm) is an `(axal ?)`. An
|
||||
`axal` is like a recursive `arch`, and is defined in `arvo.hoon`. The `?` says
|
||||
whether to *keep* the given file or directory. You may want to look at the `+of`
|
||||
axal engine in `arvo.hoon` for constructing and manipulating the `norm`.
|
||||
@ -358,7 +395,7 @@ make your changes.
|
||||
|
||||
A `%tomb` `task` with a `%worn` `clue` is like
|
||||
[`%norm`](#norm---default-policy), except it only applies to a specific commit
|
||||
for a ship/desk. The [`tako:clay`](/reference/arvo/clay/data-types#takoclay)
|
||||
for a ship/desk. The [`tako`](/reference/arvo/clay/data-types#tako)
|
||||
denotes the commit to apply the policy.
|
||||
|
||||
---
|
||||
@ -371,10 +408,10 @@ denotes the commit to apply the policy.
|
||||
|
||||
A `%tomb` `task` with a `%seek` `clue` will attempt to retrieve missing,
|
||||
tombstoned data and integrate it into Clay's object store. The
|
||||
[`cash:clay`](/reference/arvo/clay/data-types#cashclay) is a reference to a
|
||||
[`cash`](/reference/arvo/clay/data-types#cash) is a reference to a
|
||||
commit on the given ship/desk as either a
|
||||
[`tako:clay`](/reference/arvo/clay/data-types#takoclay) or a
|
||||
[`case:clay`](/reference/arvo/clay/data-types#caseclay).
|
||||
[`tako`](/reference/arvo/clay/data-types#tako) or a
|
||||
[`case`](/reference/arvo/clay/data-types#case).
|
||||
|
||||
---
|
||||
|
||||
@ -421,7 +458,7 @@ The type it returns is a `%hill` `gift`, which looks like:
|
||||
|
||||
A `%mont` `task` mounts the specified `beam` to the specified `term` mount point.
|
||||
|
||||
A `beam:clay` is the following structure:
|
||||
A `beam` is the following structure:
|
||||
|
||||
```hoon
|
||||
+$ beam [[p=ship q=desk r=case] s=path] :: global name
|
||||
@ -447,7 +484,7 @@ Clay does not return a `gift` in response to a `%mont` `%task`.
|
||||
|
||||
A `%ogre` `task` unmounts the specified mount.
|
||||
|
||||
It's defined in `lull.hoon` as taking `$@(desk beam)` but in fact it will only unmount the target when specified as a `term` mount name. Passing it a `desk` will incidentally work if the mount is named the same as the `desk` but otherwise it won't work. Passing it a `beam:clay` will simply not work.
|
||||
It's defined in `lull.hoon` as taking `$@(desk beam)` but in fact it will only unmount the target when specified as a `term` mount name. Passing it a `desk` will incidentally work if the mount is named the same as the `desk` but otherwise it won't work. Passing it a `beam` will simply not work.
|
||||
|
||||
#### Returns
|
||||
|
||||
@ -532,9 +569,9 @@ If permissions are not set for a particular file, they will be inherited from th
|
||||
|
||||
A group is called a `crew` and is just a `set` of ships with a `@ta` name.
|
||||
|
||||
The permissions for each file or directory are a pair of `dict:clay` where the head is read permissions and the tail is write permissions.
|
||||
The permissions for each file or directory are a pair of `dict` where the head is read permissions and the tail is write permissions.
|
||||
|
||||
A `dict:clay` is this structure:
|
||||
A `dict` is this structure:
|
||||
|
||||
```hoon
|
||||
+$ dict [src=path rul=real] :: effective permission
|
||||
@ -703,7 +740,7 @@ To read files on a foreign `desk`, you just send Clay a `%warp` `task` (as you w
|
||||
Clay only allows a subset of `care`s to be used remotely. They are:
|
||||
|
||||
- `%u` - Check for existence of file.
|
||||
- `%v` - Get entire `dome:clay` state of a desk.
|
||||
- `%v` - Get entire `dome` state of a desk.
|
||||
- `%w` - Get revision number.
|
||||
- `%x` - Get data of file.
|
||||
- `%y` - Get `arch` of file or directory.
|
||||
@ -711,6 +748,11 @@ Clay only allows a subset of `care`s to be used remotely. They are:
|
||||
|
||||
Any other `care` will crash with a `%clay-bad-foreign-request-care` error.
|
||||
|
||||
In addition, Clay only allows `%sing` and `%many` requests -- not `%next` or
|
||||
`%mult`. One way to get the next revision is to first `%sing` the current
|
||||
revision number using `%w` and case `da+now`, then `%sing` again with the next
|
||||
revision number using case `ud+<next-revision-number>`.
|
||||
|
||||
The foreign ship will respond only if correct permissions have been set. See the [Permissions](#permissions) section for details.
|
||||
|
||||
Note that if you're reading a whole `desk` or directory, all subfolders and files must also permit reading. If even a single file does not permit you reading it, the foreign ship will not respond to the request.
|
||||
|
@ -6,7 +6,7 @@ weight = 3
|
||||
## Reading and Subscribing
|
||||
|
||||
When reading from Clay, there are three types of requests. A
|
||||
`%sing` request asks for data at single revsion. A `%next`
|
||||
`%sing` request asks for data at a single revision. A `%next`
|
||||
request asks to be notified the next time there's a change to
|
||||
given file. A `%many` request asks to be notified on every
|
||||
change in a `desk` for a range of changes.
|
||||
|
@ -3,17 +3,19 @@ title = "Scries"
|
||||
weight = 40
|
||||
+++
|
||||
|
||||
### What is a scry?
|
||||
This document mostly covers local scries. Remote scries have recently been introduced, and are documented in a [separate guide](/guides/additional/remote-scry).
|
||||
|
||||
## What is a scry?
|
||||
|
||||
A scry is a read-only request to Arvo's global namespace.
|
||||
|
||||
Vanes and agents define _scry endpoints_ which allow one to request data from their respective states. The endpoints can process the data in any way before returning it, but they cannot alter the actual state - scries can _only_ read, not modify.
|
||||
|
||||
### Why scry?
|
||||
## Why scry?
|
||||
|
||||
The subject available in something like a Gall agent or thread contains a great many functions and structures from the standard library as well as `zuse` and `lull`, but it does not include any of the actual data stored elsewhere in the ship. All it has is its own state, a `bowl` and any `card`s it has been passed. Ordinarily, in order to access such data, one would need to `%poke` or `%watch` other agents, or `%pass` `task`s to vanes, then wait for a response. Arvo's scry system is the one exception to this; it allows direct retrieval of data from other vanes or agents in situ, from any context, without any of the normal messaging rigmarole.
|
||||
|
||||
### How do I scry?
|
||||
## How do I scry?
|
||||
|
||||
Scries are performed exclusively with the dotket rune: `.^`
|
||||
|
||||
@ -25,11 +27,11 @@ One further note on `care`s (which can sometimes be confusing): While `care`s ar
|
||||
|
||||
Most other vanes also make use of `care`s in their scry endpoints. While such vanes don't have corresponding submodules with strictly defined behaviour like Clay, the `care`s still confer the general nature of the endpoint. The most widely used `care` is `%x`, which implies reading data in a general sense. Gall has special handling of `%x` scries as described in the [Gall agents](#gall-agents) section below, but otherwise `care`s have no special behaviour for non-Clay vanes (though they must still be included if the endpoint specifies it).
|
||||
|
||||
### What can I scry?
|
||||
## What can I scry?
|
||||
|
||||
There are two places where scry endpoints are defined:
|
||||
|
||||
#### Vanes
|
||||
### Vanes
|
||||
|
||||
Each of Arvo's nine vanes (kernel modules) include a `+scry` arm which defines
|
||||
that vane's scry endpoints. The number of endpoints and extent of data available
|
||||
@ -43,13 +45,22 @@ endpoints you'd not typically use in your applications).
|
||||
|
||||
To explore what scry endpoints are available for vanes, you can refer to the Scry Reference section of each vane in the [Arvo](/reference/arvo/overview) section of the documents.
|
||||
|
||||
#### Gall agents
|
||||
### Gall agents
|
||||
|
||||
Gall has a single scry endpoint of its own to check for the existence of an agent, but otherwise all Gall scries are passed through to one of the agents it manages. The target agent to scry is specified in place of the `desk` as described in the diagram above. Each Gall agent includes a `+on-peek` arm that defines its own scry endpoints. For example, `%graph-store` has a number of scry endpoints to access the data it stores, such as chat messages and the like.
|
||||
|
||||
Gall agents can expose scry endpoints with any `care`, but most commonly they'll take a `%x` `care`. Gall handles `%x` scries specially - it expects an extra field at the end of the `path` that specifies a `mark`. Gall will attempt to perform a `mark` conversion from the `mark` returned by the scry endpoint to the `mark` specified. Note the trailing `mark` in the `path` will not be passed through to the agent itself.
|
||||
|
||||
### What is an endpoint?
|
||||
{% callout %}
|
||||
|
||||
**Note:** you should not perform agent scries from within the
|
||||
`++on-load` arm of your agent. All Gall agents are suspended during
|
||||
kernel upgrade, and then reloaded one-by-one. If the agent you scry
|
||||
wasn't reloaded before yours, the scry will fail.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## What is an endpoint?
|
||||
|
||||
"Endpoint" refers to a specific scry path in a vane or agent. They will sometimes informally be noted in documentation or source comments like `/x/foo/bar/baz` or maybe just `/foo/bar/baz`. The first part of the former example is the `care`, then the rest is the `path` portion as noted in the diagram earlier.
|
||||
|
||||
@ -69,11 +80,11 @@ The case in the beginning says it takes a `%x` `care` and has a `path` of `/keys
|
||||
.^(json %gx /(scot %p our)/graph-store/(scot %da now)/keys/json)
|
||||
```
|
||||
|
||||
### Web scries
|
||||
## Web scries
|
||||
|
||||
The webserver vane Eyre has a system which allows clients like web browsers to perform scries over HTTP. For details, refer to the [Scry section of Eyre's External API Reference](/reference/arvo/eyre/external-api-ref#scry).
|
||||
|
||||
### Further reading
|
||||
## Further reading
|
||||
|
||||
[dotket](/reference/hoon/rune/dot#-dotket) - Documentation of the `.^` rune which performs scries.
|
||||
|
||||
|
@ -7,180 +7,253 @@ Here are the data types used by Dill, as defined in `/sys/lull.hoon`.
|
||||
|
||||
## `$blew`
|
||||
|
||||
Terminal dimension.
|
||||
|
||||
```hoon
|
||||
+$ blew [p=@ud q=@ud]
|
||||
+$ blew [p=@ud q=@ud]
|
||||
```
|
||||
|
||||
Terminal dimension; `p` is columns, `q` is rows. This structure is passed to Dill by the runtime in a [%blew](/reference/arvo/dill/tasks#blew) `task` whenever the dimensions of the terminal changes.
|
||||
`p` is columns, `q` is rows. This structure is passed to Dill by the
|
||||
runtime in a [%blew](/reference/arvo/dill/tasks#blew) `task` whenever
|
||||
the dimensions of the terminal changes.
|
||||
|
||||
---
|
||||
|
||||
## `$belt`
|
||||
|
||||
```hoon
|
||||
+$ belt
|
||||
$? bolt
|
||||
$% [%mod mod=?(%ctl %met %hyp) key=bolt]
|
||||
[%txt p=(list @c)]
|
||||
[%ctl p=@c]
|
||||
[%met p=@c]
|
||||
== ==
|
||||
```
|
||||
|
||||
Terminal client input.
|
||||
|
||||
A `$belt` is passed to Dill in a [%belt](/reference/arvo/dill/tasks#belt) `task` by the runtime whenever there is input, such as a user typing in the console. This is only used between the terminal client and Dill, a [$dill-belt](#dill-belt) is used between Dill and Arvo.
|
||||
```hoon
|
||||
+$ belt :: client input
|
||||
$? bolt :: simple input
|
||||
[%mod mod=?(%ctl %met %hyp) key=bolt] :: w/ modifier
|
||||
[%txt p=(list @c)] :: utf32 text
|
||||
::TODO consider moving %hey, %rez, %yow here ::
|
||||
== ::
|
||||
```
|
||||
|
||||
A `$belt` is passed to Dill in a
|
||||
[%belt](/reference/arvo/dill/tasks#belt) `task` by the runtime whenever
|
||||
there is input, such as a user typing in the console. This is only used
|
||||
between the terminal client and Dill, a [$dill-belt](#dill-belt) is used
|
||||
between Dill and Arvo.
|
||||
|
||||
May either be a [$bolt](#bolt) or one of:
|
||||
|
||||
- `%mod` - Modifier (Ctrl, Meta or Hyper) plus [key].
|
||||
- `%txt` - A series of characters
|
||||
- `%ctl` - Ctrl+[key], deprecated in favour of `%mod`.
|
||||
- `%met` - Meta+[key], deprecated in favour of `%mod`.
|
||||
- `%mod` - Modifier (Ctrl, Meta or Hyper) plus a key (see
|
||||
[`$bolt`](#bolt).
|
||||
- `%txt` - A series of UTF-32 characters.
|
||||
|
||||
--
|
||||
|
||||
## `$bolt`
|
||||
|
||||
```hoon
|
||||
+$ bolt
|
||||
$@ @c
|
||||
$% [%aro p=?(%d %l %r %u)]
|
||||
[%bac ~]
|
||||
[%del ~]
|
||||
[%hit r=@ud c=@ud]
|
||||
[%ret ~]
|
||||
==
|
||||
```
|
||||
|
||||
Simple input.
|
||||
|
||||
Either a single simple character or one of:
|
||||
```hoon
|
||||
+$ bolt :: simple input
|
||||
$@ @c :: simple keystroke
|
||||
$% [%aro p=?(%d %l %r %u)] :: arrow key
|
||||
[%bac ~] :: true backspace
|
||||
[%del ~] :: true delete
|
||||
[%hit x=@ud y=@ud] :: mouse click
|
||||
[%ret ~] :: return
|
||||
== ::
|
||||
```
|
||||
|
||||
Either a single UTF-32 character or one of:
|
||||
|
||||
- `%aro` - Arrow keys.
|
||||
- `%bac` - Backspace key.
|
||||
- `%del` - Delete key.
|
||||
- `%hit` - Mouse click - `r` is row and `c` is column. Note these are zero-indexed, with `[0 0]` being the _bottom left_ corner.
|
||||
- `%hit` - Mouse click - `r` is row and `c` is column. Note these are
|
||||
zero-indexed, with `[0 0]` being the _bottom left_ corner.
|
||||
- `%ret` - Return (Enter) key.
|
||||
|
||||
---
|
||||
|
||||
## `$blit`
|
||||
|
||||
```hoon
|
||||
+$ blit
|
||||
$% [%bel ~]
|
||||
[%clr ~]
|
||||
[%hop p=@ud]
|
||||
[%klr p=stub]
|
||||
[%lin p=(list @c)]
|
||||
[%mor ~]
|
||||
[%sag p=path q=*]
|
||||
[%sav p=path q=@]
|
||||
[%url p=@t]
|
||||
==
|
||||
```
|
||||
|
||||
Terminal client output.
|
||||
|
||||
A `$blit` is given to the terminal client by Dill in a `%blit` `gift` when it wants to print some text, clear the screen, go _ding_ or what have you.
|
||||
```hoon
|
||||
+$ blit :: client output
|
||||
$% [%bel ~] :: make a noise
|
||||
[%clr ~] :: clear the screen
|
||||
[%hop p=$@(@ud [x=@ud y=@ud])] :: set cursor col/pos
|
||||
[%klr p=stub] :: put styled
|
||||
[%mor p=(list blit)] :: multiple blits
|
||||
[%nel ~] :: newline
|
||||
[%put p=(list @c)] :: put text at cursor
|
||||
[%sag p=path q=*] :: save to jamfile
|
||||
[%sav p=path q=@] :: save to file
|
||||
[%url p=@t] :: activate url
|
||||
[%wyp ~] :: wipe cursor line
|
||||
== ::
|
||||
```
|
||||
|
||||
This is only used between Dill and the terminal client, a [$dill-blit](#dill-blit) is used instead between Arvo and Dill.
|
||||
A `$blit` is given to the terminal client by Dill in a `%blit` `gift`
|
||||
when it wants to print some text, clear the screen, go _ding_ or what
|
||||
have you.
|
||||
|
||||
This is directly used between Dill and the terminal client, while a
|
||||
[$dill-blit](#dill-blit) is used between Arvo and Dill. A `$dill-blit`
|
||||
includes the `$blit` union as a subset.
|
||||
|
||||
A `$blit` is one of:
|
||||
|
||||
- `%bel` - Ring the terminal bell.
|
||||
- `%clr` - Clear the screen.
|
||||
- `%hop` - Set cursor position, `p` specifies its position on the prompt line.
|
||||
- `%hop` - Set cursor position. If `p` is an atom, it specifies the
|
||||
horizontal position on the prompt line. If `p` is a cell, it
|
||||
represents a 2D location where `x` is columns and `y` is
|
||||
rows.
|
||||
- `%klr` - Set styled line, the `$stub` specifies the text and style.
|
||||
- `%lin` - Set current line, `p` contains the text.
|
||||
- `%mor` - Newline.
|
||||
- `%sag` - Save to jamfile, typically in `/[pier]/.urb/put/`. `p` is `/[path]/[filename]/[extension]`. For example, `/foo/bar` will save it in `/[pier]/.urb/put/foo.bar`, `/a/b/c/foo/bar` will save it in `/[pier]/.urb/put/a/b/c/foo.bar`, and `/foo` will save it in `/[pier]/.urb/put.foo`. `q` is the `noun` to `jam` and save in the file.
|
||||
- `%sav` - Save to file. Same behaviour as `%sag` except `q` is an `atom` rather than a `noun` and therefore doesn't need to be `jam`med. The `atom` is written to disk as if it were the bytestring in the tail of an `$octs`. That is, `%sav`ing the `cord` `'abcdef'`, whose `@ux` value is `0x6665.6463.6261`, results in a unix file whose hex dump renders as `61 62 63 64 65 66`.
|
||||
- `%mor` - multiple `$blit`s.
|
||||
- `%nel` - a newline.
|
||||
- `%put` - put text (as a list of UTF-32 characters) at the current
|
||||
cursor position.
|
||||
- `%sag` - Save to jamfile, typically in `/[pier]/.urb/put/`. `p` is
|
||||
`/[path]/[filename]/[extension]`. For example, `/foo/bar` will save it
|
||||
in `/[pier]/.urb/put/foo.bar`, `/a/b/c/foo/bar` will save it in
|
||||
`/[pier]/.urb/put/a/b/c/foo.bar`, and `/foo` will save it in
|
||||
`/[pier]/.urb/put.foo`. `q` is the `noun` to `jam` and save in the
|
||||
file.
|
||||
- `%sav` - Save to file. Same behaviour as `%sag` except `q` is an
|
||||
`atom` rather than a `noun` and therefore doesn't need to be `jam`med.
|
||||
The `atom` is written to disk as if it were the bytestring in the tail
|
||||
of an `$octs`. That is, `%sav`ing the `cord` `'abcdef'`, whose `@ux`
|
||||
value is `0x6665.6463.6261`, results in a unix file whose hex dump
|
||||
renders as `61 62 63 64 65 66`.
|
||||
- `%url` - Activate URL, `p` is the URL.
|
||||
- `%wyp` - clear the cursor line.
|
||||
|
||||
---
|
||||
|
||||
## `$dill-belt`
|
||||
|
||||
```hoon
|
||||
+$ dill-belt
|
||||
$% [%aro p=?(%d %l %r %u)]
|
||||
[%bac ~]
|
||||
[%cru p=@tas q=(list tank)]
|
||||
[%ctl p=@]
|
||||
[%del ~]
|
||||
[%hey ~]
|
||||
[%met p=@]
|
||||
[%ret ~]
|
||||
[%rez p=@ud q=@ud]
|
||||
[%txt p=(list @c)]
|
||||
[%yow p=gill:gall]
|
||||
==
|
||||
```
|
||||
|
||||
Terminal input for Arvo.
|
||||
|
||||
While [$belt](#belt) is used between the terminal client and Dill, `$dill-belt` is used between Dill and Arvo.
|
||||
```hoon
|
||||
+$ dill-belt :: arvo input
|
||||
$% belt :: client input
|
||||
[%cru p=@tas q=(list tank)] :: errmsg (deprecated)
|
||||
[%hey ~] :: refresh
|
||||
[%rez p=@ud q=@ud] :: resize, cols, rows
|
||||
[%yow p=gill:gall] :: connect to app
|
||||
== ::
|
||||
```
|
||||
|
||||
a `$dill-belt` is one of:
|
||||
A [$belt](#belt) is used between the terminal client and Dill, while a
|
||||
`$dill-belt` is used between Dill and Arvo. A `$dill-belt` includes the
|
||||
`$belt` union as a subset.
|
||||
|
||||
- `%aro` - Arrow keys.
|
||||
- `%bac` - Backspace key.
|
||||
- `%cru` - Echo error, `p` is an error `@tas` and `q` is a traceback.
|
||||
- `%ctl` - Ctrl+[key].
|
||||
- `%del` - Delete key.
|
||||
a `$dill-belt` is either [`$belt`](#belt) or one of:
|
||||
|
||||
- `%cru` - Echo error, `p` is some error tag and `q` is a stack trace.
|
||||
- `%hey` - Refresh.
|
||||
- `%met` - Meta+[key].
|
||||
- `%ret` - Return key (Enter).
|
||||
- `%rez` - Terminal resized, `p` is columns and `q` is rows.
|
||||
- `%txt` - Text input.
|
||||
- `%yow` - Connect to app.
|
||||
|
||||
---
|
||||
|
||||
## `$dill-blit`
|
||||
|
||||
```hoon
|
||||
+$ dill-blit
|
||||
$% [%bel ~]
|
||||
[%clr ~]
|
||||
[%hop p=@ud]
|
||||
[%klr p=stub]
|
||||
[%mor p=(list dill-blit)]
|
||||
[%pom p=stub]
|
||||
[%pro p=(list @c)]
|
||||
[%qit ~]
|
||||
[%out p=(list @c)]
|
||||
[%sag p=path q=*]
|
||||
[%sav p=path q=@]
|
||||
[%url p=@t]
|
||||
==
|
||||
```
|
||||
|
||||
Terminal output from Arvo.
|
||||
|
||||
While [$blit](#blit) is used between Dill and the terminal client, `$dill-blit` is used between Arvo and Dill.
|
||||
```hoon
|
||||
+$ dill-blit :: arvo output
|
||||
$% blit :: client output
|
||||
[%qit ~] :: close console
|
||||
== ::
|
||||
```
|
||||
|
||||
A `$dill-blit` is one of:
|
||||
While [$blit](#blit) is used between Dill and the terminal client,
|
||||
`$dill-blit` is used between Arvo and Dill. A `$blit` is a subset of a
|
||||
`$dill-blit`.
|
||||
|
||||
A `$dill-blit` is either a [`$blit`](#blit) or a:
|
||||
|
||||
- `%bel` - Terminal bell.
|
||||
- `%clr` - Clear screen.
|
||||
- `%hop` - Set cursor position, `p` is its horizontal position on the prompt line.
|
||||
- `%klr` - Styled text, the `$stub` specifies both the text and style.
|
||||
- `%mor` - Multiple `$dill-blit`.
|
||||
- `%pom` - Styled prompt, the `$stub` specifies both the text and style.
|
||||
- `%pro` - Set prompt, `p` is the text for the prompt.
|
||||
- `%qit` - Close console.
|
||||
- `%out` - Print text.
|
||||
- `%sag` - Save `noun` to jamfile. See [$blit](#blit) section for further details.
|
||||
- `%sav` - Save `atom` to file. See [$blit](#blit) section for further details.
|
||||
- `%url` - Activate URL.
|
||||
|
||||
---
|
||||
|
||||
## `$flog`
|
||||
|
||||
```hoon
|
||||
+$ flog
|
||||
$% [%crop p=@ud]
|
||||
[%crud p=@tas q=(list tank)]
|
||||
[%heft ~]
|
||||
[%meld ~]
|
||||
[%pack ~]
|
||||
[%text p=tape]
|
||||
[%verb ~]
|
||||
==
|
||||
```
|
||||
|
||||
Wrapped Dill `task`s.
|
||||
|
||||
These are a subset of Dill's `task`s which can be wrapped in a `%flog` `task`. See the [API Reference](/reference/arvo/dill/tasks) document for details of each of these `task`s.
|
||||
```hoon
|
||||
+$ flog :: sent to %dill
|
||||
$% [%crop p=@ud] :: trim kernel state
|
||||
$>(%crud told) ::
|
||||
[%heft ~] ::
|
||||
[%meld ~] :: unify memory
|
||||
[%pack ~] :: compact memory
|
||||
$>(%text told) ::
|
||||
[%verb ~] :: verbose mode
|
||||
== ::
|
||||
```
|
||||
|
||||
These are a subset of Dill's `task`s which can be wrapped in a `%flog`
|
||||
`task`. See the [API Reference](/reference/arvo/dill/tasks) document for
|
||||
details of each of these `task`s.
|
||||
|
||||
---
|
||||
|
||||
## `$poke`
|
||||
|
||||
Dill to userspace.
|
||||
|
||||
```hoon
|
||||
+$ poke :: dill to userspace
|
||||
$: ses=@tas :: target session
|
||||
dill-belt :: input
|
||||
== ::
|
||||
```
|
||||
|
||||
A [`$dill-belt`](#dill-belt) (client input) for a particular session.
|
||||
|
||||
---
|
||||
|
||||
## `$session-task`
|
||||
|
||||
A subset of [Dill's `task`s](/reference/arvo/dill/tasks#session-tasks)
|
||||
for interacting with a particular session.
|
||||
|
||||
```hoon
|
||||
+$ session-task :: session request
|
||||
$% [%belt p=belt] :: terminal input
|
||||
[%blew p=blew] :: terminal config
|
||||
[%flee ~] :: unwatch session
|
||||
[%hail ~] :: terminal refresh
|
||||
[%open p=dude:gall q=(list gill:gall)] :: setup session
|
||||
[%shut ~] :: close session
|
||||
[%view ~] :: watch session blits
|
||||
== ::
|
||||
```
|
||||
|
||||
This type is used in the [`%shot`](/reference/arvo/dill/tasks#shot)
|
||||
wrapper `task`.
|
||||
|
||||
See the [Session Tasks](/reference/arvo/dill/tasks#session-tasks) entry
|
||||
in the API reference for more details of these `task`s.
|
||||
|
||||
---
|
||||
|
||||
## `$told`
|
||||
|
||||
A subset of [Dill's `task`s](/reference/arvo/dill/tasks#session-tasks)
|
||||
for basic text printing.
|
||||
|
||||
```hoon
|
||||
+$ told :: system output
|
||||
$% [%crud p=@tas q=tang] :: error
|
||||
[%talk p=(list tank)] :: tanks (in order)
|
||||
[%text p=tape] :: tape
|
||||
== ::
|
||||
```
|
||||
|
||||
See the [Told Tasks](/reference/arvo/dill/tasks#told-tasks) entry
|
||||
in the API reference for more details of these `task`s.
|
||||
|
||||
---
|
||||
|
@ -5,35 +5,72 @@ weight = 1
|
||||
|
||||
The terminal driver vane.
|
||||
|
||||
Keyboard events and the like from Unix are received by Dill as [%belt](/reference/arvo/dill/tasks#belt) `task`s, and Dill sends `%blit` `gift`s containing [$blit](/reference/arvo/dill/data-types#blit)s back to the runtime to be displayed in the Unix terminal. The manner of interacting with Dill differs depending on whether you're in userspace or kernelspace, as we'll explore below.
|
||||
Keyboard events and the like from Unix are received by Dill as
|
||||
[%belt](/reference/arvo/dill/tasks#belt) `task`s, and Dill sends `%blit`
|
||||
`gift`s containing [$blit](/reference/arvo/dill/data-types#blit)s back to the
|
||||
runtime to be displayed in the Unix terminal. The manner of interacting with
|
||||
Dill differs depending on whether you're in userspace or kernelspace, as we'll
|
||||
explore below.
|
||||
|
||||
## Kernelspace
|
||||
|
||||
For technical reasons, Dill performs a handful of system tasks related to booting a ship and some memory operations. Aside from those, other Vanes mostly just pass Dill [tasks](/reference/arvo/dill/tasks) to print error messages and the like to the terminal.
|
||||
For technical reasons, Dill performs a handful of system tasks related to
|
||||
booting a ship and some memory operations. Aside from those, other Vanes mostly
|
||||
just pass Dill [tasks](/reference/arvo/dill/tasks) to print error messages and
|
||||
the like to the terminal.
|
||||
|
||||
## Userspace
|
||||
|
||||
Unlike in kernelspace, userspace applications are unlikely to `%pass` Dill `task`s directly. Instead, Dill looks at things in terms of sessions. A session is a pipeline between a client and a handler, where:
|
||||
Unlike in kernelspace, userspace applications are unlikely to `%pass` Dill
|
||||
`task`s directly. Instead, Dill looks at things in terms of sessions. A session
|
||||
is a pipeline between a client and a handler, where:
|
||||
|
||||
- The client is an external input source and output sink; a terminal with with dimensions and so forth.
|
||||
- The handler is an application in Urbit that interprets input, maybe does something with it, maybe produces output to be displayed in the client, etc.
|
||||
- The client is an external input source and output sink; a terminal with
|
||||
dimensions and so forth.
|
||||
- The handler is an application in Urbit that interprets input, maybe does
|
||||
something with it, maybe produces output to be displayed in the client,
|
||||
etc. The handler may itself handle and multiplex terminal interfaces for
|
||||
other applications, as is the case with the `%hood` module `%drum`, or it
|
||||
may be a stand-alone application.
|
||||
|
||||
There are plans for Dill to support multiple independent sessions (i.e. independent terminal clients displaying and processing different information at the same time), but at this stage Dill only supports a single session. The client for this default session is just the Unix terminal via the Urbit runtime. The handler for the default session is the `%hood` app, whose `drum` module does the heavy lifting.
|
||||
Currently, Dill supports multiple *sessions*, but Vere (the runtime) only
|
||||
supports a single Unix terminal *client* for the default session (`%$`). This
|
||||
means any non-default sessions will need to be linked through the default
|
||||
session handler `%drum` (a module of the `%hood` app) if they are to work in
|
||||
the Unix terminal. Alternatively, a client could be built that talks to Dill
|
||||
via the HTTP server Eyre in a similar way to the `%webterm` app, and
|
||||
interacts with sessions entirely separately from the Unix terminal and its
|
||||
`%drum` handler.
|
||||
|
||||
`drum` is the CLI app manager. By default you'll have a couple of CLI applications running—`%dojo` and `%chat-cli`—which you can switch between with `ctrl+x`. You may also have additional CLI apps which you have attached with the `|link` command. It's `drum` that keeps track of which one is active, which one input should be routed to, which one should be displayed, what each prompt should look like, and so forth. Dill itself is oblivious to the distinction between these CLI apps. It only sees the default session with `drum`, so it just passes all input to `drum` and display whatever `drum` gives it.
|
||||
`%drum` is Arvo's CLI app manager. By default you'll have one CLI application
|
||||
running: the `%dojo`. You may also have additional CLI apps which you have
|
||||
started or attached with the `|link` command. It's `%drum` that keeps track of
|
||||
which one is active, which one input should be routed to, which one should be
|
||||
displayed, what each prompt should look like, and so forth. Dill itself is
|
||||
oblivious to the distinction between these CLI apps. It only sees the session
|
||||
with `%drum`, so it just passes all input to `%drum` and display whatever
|
||||
`%drum` gives it.
|
||||
|
||||
While `drum` talks with Dill in `$dill-belt`s and `$dill-blit`s, it talks to CLI apps with `$sole-action`s and `$sole-event`s, which are defined in the `sole` library. For more information on the `sole` library and the related `shoe` library, and for information on how to build CLI apps, you can refer to the [CLI app tutorial](/guides/additional/cli-tutorial).
|
||||
While `%drum` talks with Dill in `$dill-belt`s and `$dill-blit`s, it talks to
|
||||
CLI apps with `$sole-action`s and `$sole-event`s, which are defined in the
|
||||
`sole` library. For more information on the `sole` library and the related
|
||||
`shoe` library, and for information on how to build CLI apps, you can refer to
|
||||
the [CLI app tutorial](/guides/additional/cli-tutorial).
|
||||
|
||||
To give a basic idea of how keyboard events flow through these systems and produce terminal output, here's a diagram showing the messages in pseudo-Hoon:
|
||||
To give a basic idea of how keyboard events flow through these systems and
|
||||
produce terminal output, here's a diagram showing the messages in pseudo-Hoon:
|
||||
|
||||
![Dill userspace diagram](https://media.urbit.org/docs/arvo/dill/dill-userspace.svg)
|
||||
|
||||
You can use a [move trace](/reference/arvo/tutorials/move-trace) to get a hands-on feel for this data flow.
|
||||
You can use a [move trace](/reference/arvo/tutorials/move-trace) to get a
|
||||
hands-on feel for this data flow.
|
||||
|
||||
## Sections
|
||||
|
||||
[API Reference](/reference/arvo/dill/tasks) - The `task`s Dill takes and the `gift`s it returns.
|
||||
[API Reference](/reference/arvo/dill/tasks) - The `task`s Dill takes and the
|
||||
`gift`s it returns.
|
||||
|
||||
[Scry Reference](/reference/arvo/dill/scry) - The scry endpoints of Dill.
|
||||
|
||||
[Data Types](/reference/arvo/dill/data-types) - Reference documentation of the data types used by Dill.
|
||||
[Data Types](/reference/arvo/dill/data-types) - Reference documentation of the
|
||||
data types used by Dill.
|
||||
|
@ -3,42 +3,39 @@ title = "Scry Reference"
|
||||
weight = 3
|
||||
+++
|
||||
|
||||
Here are the scry endpoints of Dill. They take a `%x` `care` and require the `desk` in the path prefix be empty, so the general format is `.^([type] %dx /=//=/[some-path])`.
|
||||
Here are the scry endpoints of Dill. They require the `desk` in the path prefix
|
||||
be empty, so the general format is `.^([type] %d[care] /=//=/[some-path])`.
|
||||
|
||||
Dill only has a couple of scry endpoints, both of which begin with `/session//`. The empty `//` would be where the target session would be specified, but at this stage Dill can only return a result for the default session, so it's always empty.
|
||||
## `%x` - `/sessions`
|
||||
|
||||
## /sessions//line
|
||||
Get all sessions.
|
||||
|
||||
A scry with a `care` of `%x` and a `path` of `/sessions//line` returns the current text of the prompt line of the default session. The type returned is a [$blit](/reference/arvo/dill/data-types#blit).
|
||||
A scy with a `care` of `%x` and a `path` of `/sessions` returns a `(set @tas)`
|
||||
of the current sessions. The `%$` session is the default session.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
> .^(blit:dill %dx /=//=/sessions//line)
|
||||
[ %lin
|
||||
p
|
||||
~[
|
||||
~-~~
|
||||
~-z
|
||||
~-o
|
||||
~-d
|
||||
~-~3a.
|
||||
~-d
|
||||
~-o
|
||||
~-j
|
||||
~-o
|
||||
...truncated for brevity...
|
||||
]
|
||||
]
|
||||
> .^((set @tas) %dy /=//=/sessions)
|
||||
{%$}
|
||||
```
|
||||
|
||||
## /sessions//cursor
|
||||
---
|
||||
|
||||
A scry with a `care` of `%x` and a `path` of `/sessions//cursor` returns the current horizontal position of the cursor in the default session. The type returned is a `@ud`.
|
||||
## `%u` - `/sessions/[ses]`
|
||||
|
||||
Does session exist?
|
||||
|
||||
A scry with a `care` of `%x` and a `path` of `/sessions/[ses]` where `[ses]` is
|
||||
a session name returns a `?` of whether `[ses]` exists.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
> .^(@ud %dx /=//=/sessions//cursor)
|
||||
44
|
||||
> .^(? %du /=//=/sessions/$)
|
||||
%.y
|
||||
> .^(? %du /=//=/sessions/foo)
|
||||
%.n
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -3,339 +3,478 @@ title = "API Reference"
|
||||
weight = 2
|
||||
+++
|
||||
|
||||
In this document we describe the public interface for Dill. Namely, we describe each `task` that Dill can be `pass`ed, and which `gift`(s) Dill can `give` in return.
|
||||
In this document we describe the public interface for Dill. Namely, we
|
||||
describe each `task` that Dill can be `pass`ed, and which `gift`(s) Dill
|
||||
can `give` in return.
|
||||
|
||||
## `%belt`
|
||||
Dill's `task`s are organized into three categories: [Session
|
||||
`task`s](#session-tasks) for interacting with a particular session,
|
||||
[Told `task`s](#told-tasks) for basic terminal printing, and
|
||||
[system/miscellaneous `task`s](#system-misc-tasks) which are a combination of
|
||||
general system `task`s and Dill `task`s which don't fit in the previous
|
||||
two categories.
|
||||
|
||||
```hoon
|
||||
[%belt p=belt]
|
||||
```
|
||||
## System & Misc. Tasks
|
||||
|
||||
Terminal input.
|
||||
These are the Dill `task`s not otherwise categorized as [session
|
||||
tasks](#session-tasks) or [told tasks](#told-tasks). Most of them are
|
||||
interfaces for Vere or Arvo and do thing like toggle verbose mode,
|
||||
defragment state, etc. The most notable Dill-specific `task` is
|
||||
[`%shot`](#shot), which is used as a wrapper for the [session
|
||||
tasks](#session-tasks) to specify the session. The [`%logs`](#logs)
|
||||
`task` is also useful, letting you subscribe to all system output.
|
||||
|
||||
The runtime sends a `%belt` `task` to Dill whenever there is input in the terminal, such as a keystroke.
|
||||
|
||||
The [$belt](/reference/arvo/dill/data-types#belt) in `p` contains the input such as which key was pressed.
|
||||
|
||||
Dill will convert the `$belt` into a [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke` the session handler (typically `drum`) with it.
|
||||
|
||||
This `task` would not typically be used from userspace.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%belt` `task`.
|
||||
|
||||
## `%blew`
|
||||
|
||||
```hoon
|
||||
[%blew p=blew]
|
||||
```
|
||||
|
||||
Terminal resized.
|
||||
|
||||
The runtime passes Dill a `%blew` `task` whenever the terminal is resized.
|
||||
|
||||
The [$blew](/reference/arvo/dill/data-types#blew) specifies the new dimensions.
|
||||
|
||||
Dill will convert the `$blew` into a `%rez` [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the session handler (typically `drum`) with it.
|
||||
|
||||
This `task` would not typically be used from userspace.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%blew` `task`.
|
||||
|
||||
## `%boot`
|
||||
### `%boot`
|
||||
|
||||
```hoon
|
||||
[%boot lit=? p=*]
|
||||
```
|
||||
|
||||
This `task` is used only once, when Arvo first enters the [adult stage](/reference/arvo/overview#structural-interface-core). Dill is technically the first vane to be activated, via the `%boot` `task`, which then sends Jael (considered the "true" first vane) the `%dawn` or `%fake` `task` wrapped in the `%boot` `task`. Jael then goes on to call `%init` `task`s for other vanes (including Dill).
|
||||
This `task` is used only once, when Arvo first enters the [adult
|
||||
stage](/reference/arvo/overview#structural-interface-core). Dill is
|
||||
technically the first vane to be activated, via the `%boot` `task`,
|
||||
which then sends Jael (considered the "true" first vane) the `%dawn` or
|
||||
`%fake` `task` wrapped in the `%boot` `task`. Jael then goes on to call
|
||||
`%init` `task`s for other vanes (including Dill).
|
||||
|
||||
`lit` specifies whether to boot in lite mode. `p` is either a [%dawn](/reference/arvo/jael/data-types#dawn) or [%fake](/reference/arvo/jael/tasks#fake) `task:jael`. `%dawn` is for an ordinary boot and `%fake` is for booting a fake ship.
|
||||
`lit` specifies whether to boot in lite mode. `p` is either a
|
||||
[%dawn](/reference/arvo/jael/tasks#dawn) or
|
||||
[%fake](/reference/arvo/jael/tasks#fake) `task:jael`. `%dawn` is for an
|
||||
ordinary boot and `%fake` is for booting a fake ship.
|
||||
|
||||
This `task` would not be used from userspace.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%boot` `task`, but it will `%pass` the wrapped `%dawn` or `%fake` `task` to Jael.
|
||||
Dill returns no `gift` in response to a `%boot` `task`, but it will
|
||||
`%pass` the wrapped `%dawn` or `%fake` `task` to Jael.
|
||||
|
||||
## `%crop`
|
||||
---
|
||||
|
||||
### `%crop`
|
||||
|
||||
Trim kernel state.
|
||||
|
||||
```hoon
|
||||
[%crop p=@ud]
|
||||
```
|
||||
|
||||
Trim kernel state.
|
||||
|
||||
This `task` is the same as the [%trim](#trim) `task`. Like `%trim`, Dill does nothing with it.
|
||||
This `task` is the same as the `%trim` vane `task`. Like `%trim`, Dill
|
||||
does nothing with it.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%crop` `task`.
|
||||
|
||||
## `%crud`
|
||||
---
|
||||
|
||||
```hoon
|
||||
[%crud p=@tas q=(list tank)]
|
||||
```
|
||||
|
||||
Print error.
|
||||
|
||||
Dill prints the given error to the terminal. The verbosity for the particular `@tas` error tag specified in `p` is determined by the `level` set by a [%knob](#knob) `task` - either `%hush`, `%soft` or `%loud`. The default is `%loud`, where it will print the full `(list tank)` specified in `q`. See [%knob](#knob) for details of verbosity levels.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%crud` `task`.
|
||||
|
||||
## `%flee`
|
||||
|
||||
```hoon
|
||||
[%flee session=~]
|
||||
```
|
||||
|
||||
Unwatch a session to which you've previously subscribed with [%view](#view).
|
||||
|
||||
The ability to specify a session is not yet implemented in Dill, so `session` is always `~`, the default session.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%flee` `task`.
|
||||
|
||||
## `%flog`
|
||||
### `%flog`
|
||||
|
||||
```hoon
|
||||
[%flog p=flog]
|
||||
```
|
||||
|
||||
A `%flog` `task` is a wrapper over a `task` sent by another vane. Dill removes the wrapper and sends the bare `task` to itself over the default `duct`.
|
||||
A `%flog` `task` is a wrapper over a `task` sent by another vane. Dill
|
||||
removes the wrapper and sends the bare `task` to itself over the default
|
||||
`duct`.
|
||||
|
||||
A `%flog` `task` takes a [$flog](/reference/arvo/dill/data-types#flog) as its argument. A `$flog` is a subset of Dill's `task`s.
|
||||
A `%flog` `task` takes a [$flog](/reference/arvo/dill/data-types#flog)
|
||||
as its argument. A `$flog` is a subset of Dill's `task`s.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%flog` `task`.
|
||||
|
||||
## `%flow`
|
||||
---
|
||||
|
||||
```hoon
|
||||
[%flow p=@tas q=(list gill:gall)]
|
||||
```
|
||||
### `%heft`
|
||||
|
||||
Terminal config.
|
||||
|
||||
This `task` is not used.
|
||||
|
||||
## `%hail`
|
||||
|
||||
```hoon
|
||||
[%hail ~]
|
||||
```
|
||||
|
||||
Refresh.
|
||||
|
||||
Dill converts a `%hail` `task` into a `%hey` [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the session handler (typically `drum`) with it to handle the refresh.
|
||||
|
||||
This `task` would not be used from userspace.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%hail` `task`.
|
||||
|
||||
## `%heft`
|
||||
Produce memory report.
|
||||
|
||||
```hoon
|
||||
[%heft ~]
|
||||
```
|
||||
|
||||
Produce memory report.
|
||||
|
||||
When Dill receives a `%heft` `task` it passes Arvo a `%whey` `waif` to obtain a memory report and prints it to the terminal.
|
||||
When Dill receives a `%heft` `task` it passes Arvo a `%whey` `waif` to
|
||||
obtain a memory report and prints it to the terminal.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%heft` `task`.
|
||||
|
||||
## `%hook`
|
||||
---
|
||||
|
||||
### `%logs`
|
||||
|
||||
Watch system output.
|
||||
|
||||
```hoon
|
||||
[%hook ~]
|
||||
[%logs p=(unit ~)]
|
||||
```
|
||||
|
||||
This terminal hung up.
|
||||
|
||||
This task is not used.
|
||||
|
||||
## `%harm`
|
||||
|
||||
```hoon
|
||||
[%harm ~]
|
||||
```
|
||||
|
||||
All terminals hung up.
|
||||
|
||||
This `task` is not used.
|
||||
|
||||
## `%init`
|
||||
|
||||
```hoon
|
||||
[%init ~]
|
||||
```
|
||||
|
||||
This `task` is called only once, when Arvo first enters the [adult stage](/reference/arvo/overview#structural-interface-core). It performs initial setup for Dill, such as setting the width of the console.
|
||||
|
||||
Note that this is not actually the first `task` passed to Dill - see [%boot](#%boot).
|
||||
|
||||
This `task` would not be used from userspace.
|
||||
A non-null `p` subscribes to system output, and a null `p` unsubscribes.
|
||||
While subscribed, you'll receive each piece of output in a `%logs` gift
|
||||
as it occurs.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%init` `task`.
|
||||
Dill does not immediately return anything, but will give you a `%logs`
|
||||
gifts each time system output occurs. A `%logs` gift looks like:
|
||||
|
||||
## `%meld`
|
||||
```hoon
|
||||
[%logs =told]
|
||||
```
|
||||
|
||||
```
|
||||
[%meld ~]
|
||||
```
|
||||
A `$told` is either a [`%crud`](#crud), [`%talk`](#talk) or
|
||||
[`%text`](#text) task. See the
|
||||
[`$told`](/reference/arvo/dill/data-types#dill-belt) entry in the data
|
||||
types reference for more details.
|
||||
|
||||
---
|
||||
|
||||
### `%meld`
|
||||
|
||||
Deduplicate persistent state.
|
||||
|
||||
```hoon
|
||||
[%meld ~]
|
||||
```
|
||||
|
||||
Dill asks the runtime to perform the memory deduplication.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%meld` `task`.
|
||||
|
||||
## `%noop`
|
||||
---
|
||||
|
||||
```hoon
|
||||
[%noop ~]
|
||||
```
|
||||
### `%pack`
|
||||
|
||||
No operation.
|
||||
|
||||
A `%noop` `task` does nothing, as the name implies.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%noop` `task`.
|
||||
|
||||
## `%pack`
|
||||
Defragment persistent state.
|
||||
|
||||
```hoon
|
||||
[%pack ~]
|
||||
```
|
||||
|
||||
Defragment persistent state.
|
||||
|
||||
Dill asks the runtime to perform the defragmentation.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%meld` `task`.
|
||||
|
||||
## `%talk`
|
||||
---
|
||||
|
||||
### `%seat`
|
||||
|
||||
Install desk.
|
||||
|
||||
```hoon
|
||||
[%talk p=tank]
|
||||
[%seat =desk]
|
||||
```
|
||||
|
||||
This `task` is not used.
|
||||
|
||||
## `%text`
|
||||
|
||||
```hoon
|
||||
[%text p=tape]
|
||||
```
|
||||
|
||||
Print `tape` to terminal.
|
||||
|
||||
Upon receiving a `%text` `task`, Dill will convert the `tape` given in `p` to a `(list @c)` and give it to the runtime in a `%blit` `gift` including a `%lin` [$blit](/reference/arvo/dill/data-types#blit).
|
||||
This indirectly pokes `%hood` with a `%kiln-install` `mark` to install
|
||||
the specified `desk`. You should just poke `%hood` directly rather than
|
||||
using this.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%text` `task`, but it does give a `%blit` `gift` to the runtime which looks like:
|
||||
Dill does not return a `gift` in response to a `%seat` `task`.
|
||||
|
||||
---
|
||||
|
||||
### `%shot`
|
||||
|
||||
Task for session.
|
||||
|
||||
```hoon
|
||||
[%blit p=(list blit)]
|
||||
[%shot ses=@tas task=session-task]
|
||||
```
|
||||
|
||||
## `%view`
|
||||
A `$session-task` is one of these `task`s: [`%belt`](#belt),
|
||||
[`%blew`](#blew), [`%flee`](#flee), [`%hail`](#hail), [`%open`](#open),
|
||||
[`%shut`](#shut), [`%view`](#view). See the
|
||||
[`$session-task`](/reference/arvo/dill/data-types#session-task) entry in
|
||||
the data types reference.
|
||||
|
||||
```hoon
|
||||
[%view session=~]
|
||||
```
|
||||
---
|
||||
|
||||
Watch session.
|
||||
### `%verb`
|
||||
|
||||
A `%view` `task` subscribes for a copy of all `%blit` `gift`s which Dill `%give`s for the default session. This `task` is used by the `%herm` app so it can convert the `$blit`s to JSON and render them in the web terminal.
|
||||
|
||||
The ability to specify a session is not yet implemented in Dill, so `session` is always `~`, the default session.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill will `%give` a copy of all `%blit`s for the default session. A `%blit` `gift` is:
|
||||
|
||||
```hoon
|
||||
[%blit p=(list blit)]
|
||||
```
|
||||
|
||||
## `%trim`
|
||||
|
||||
```hoon
|
||||
[%trim p=@ud]
|
||||
```
|
||||
|
||||
`%trim` is a common vane `task` used to reduce memory usage. It does nothing for Dill because Dill only keeps a minimal necessary state.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%trim` `task`.
|
||||
|
||||
## `%vega`
|
||||
|
||||
```hoon
|
||||
[%vega ~]
|
||||
```
|
||||
|
||||
This is a common vane `task` used to inform the vane that the kernel has been
|
||||
upgraded. Dill does not do anything in response to this.
|
||||
|
||||
This `task` would not be used from userspace.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%vega` `task`.
|
||||
|
||||
## `%verb`
|
||||
Toggle Arvo verbose mode.
|
||||
|
||||
```hoon
|
||||
[%verb ~]
|
||||
```
|
||||
|
||||
This `task` toggles verbose mode for all of Arvo, which is located here since
|
||||
Dill is the vane that prints errors. To be precise, `%verb` toggles the laconic
|
||||
bit `lac` in the [Arvo state](/reference/arvo/overview#the-state) by passing a `%verb` `waif` to Arvo.
|
||||
This `task` toggles verbose mode for all of Arvo, which is located here
|
||||
since Dill is the vane that prints errors. To be precise, `%verb`
|
||||
toggles the laconic bit `lac` in the [Arvo
|
||||
state](/reference/arvo/overview#the-state) by passing a `%verb` `waif`
|
||||
to Arvo.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%verb` `task`.
|
||||
|
||||
## `%knob`
|
||||
---
|
||||
|
||||
## Session Tasks
|
||||
|
||||
These `task`s are for interacting with a particular session. These all
|
||||
would normally be wrapped in a [`%shot`](#shot) `task` to specify the
|
||||
session, rather than sent directly.
|
||||
|
||||
This subset of Dill's `task`s are defined in the [`$session-task`
|
||||
type](/reference/arvo/dill/data-types#session-task).
|
||||
|
||||
### `%belt`
|
||||
|
||||
Terminal input.
|
||||
|
||||
```hoon
|
||||
[%knob tag=term level=?(%hush %soft %loud)]
|
||||
[%belt p=belt]
|
||||
```
|
||||
|
||||
`%knob` sets the verbosity level for each error tag. The error `tag` can be any `@tas`. The given `tag` and `level` will be added to Dill's `veb` which maps tags to levels. Subsequent [%crud](#crud) `task`s will then print with the specified verbosity.
|
||||
The [$belt](/reference/arvo/dill/data-types#belt) in `p` contains the
|
||||
input such as which key was pressed. Dill will convert the `$belt` into
|
||||
a [$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke` it
|
||||
into the session handler agent for the session in question.
|
||||
|
||||
The `level`s behave like so:
|
||||
|
||||
- `%hush` - Completely silent, print nothing.
|
||||
- `%soft` - Just print `crud: %error-tag event failed`, ignore any `tang` given in the `%crud`.
|
||||
- `%loud` - Print the `%soft` message as well as the full `tang` given in the `%crud` `task`.
|
||||
This `task` should be wrapped in a [`%shot`](#shot) `task` to specify
|
||||
the session. Without the `%shot` wrapper, it will use the default
|
||||
session (`%$`).
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%knob` `task`.
|
||||
Dill returns no `gift` in response to a `%belt` `task`.
|
||||
|
||||
---
|
||||
|
||||
### `%blew`
|
||||
|
||||
Terminal resized.
|
||||
|
||||
```hoon
|
||||
[%blew p=blew]
|
||||
```
|
||||
|
||||
The [$blew](/reference/arvo/dill/data-types#blew) specifies the new
|
||||
dimensions.
|
||||
|
||||
Dill will convert the `$blew` into a `%rez`
|
||||
[$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the
|
||||
session handler (typically `drum`) with it.
|
||||
|
||||
This `task` would not typically be used from userspace. Instead, it
|
||||
would come in from the runtime for the default session (`%$`) when the
|
||||
actual terminal were resized. If in an odd scenario it were used from
|
||||
userspace, it would need to be wrapped in a [`%shot`](#shot) `task` to
|
||||
specify a session other than `%$`.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%blew` `task`.
|
||||
|
||||
---
|
||||
|
||||
### `%flee`
|
||||
|
||||
Unwatch a session to which you've previously subscribed with
|
||||
[%view](#view).
|
||||
|
||||
```hoon
|
||||
[%flee ~]
|
||||
```
|
||||
|
||||
This `task` must be wrapped in a [`%shot`](#shot) `task` in order to
|
||||
specify the session. Without that, it will default to the default
|
||||
session (`%$`). The subscription to end will be determined implicitly by
|
||||
the `duct`.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%flee` `task`.
|
||||
|
||||
---
|
||||
|
||||
### `%hail`
|
||||
|
||||
Refresh.
|
||||
|
||||
```hoon
|
||||
[%hail ~]
|
||||
```
|
||||
|
||||
Dill converts a `%hail` `task` into a `%hey`
|
||||
[$dill-belt](/reference/arvo/dill/data-types#dill-belt) and `%poke`s the
|
||||
session handler (typically `%drum`) with it.
|
||||
|
||||
This `task` would not typically be used from userspace. If in an odd
|
||||
scenario it were used from userspace, it would need to be wrapped in a
|
||||
[`%shot`](#shot) `task` to specify a session other than `%$`.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill returns no `gift` in response to a `%hail` `task`.
|
||||
|
||||
---
|
||||
|
||||
### `%open`
|
||||
|
||||
Setup session.
|
||||
|
||||
```hoon
|
||||
[%open p=dude:gall q=(list gill:gall)]
|
||||
```
|
||||
|
||||
This `task` is always wrapped in a [`%shot`](#shot) `task`, and creates
|
||||
the new session specified in that wrapper. If it's not wrapped in a
|
||||
`%shot` task, it will default to the default session (`%$`) and fail
|
||||
because it already exists. This `task` is designed to be used by a
|
||||
userspace session handler like `%drum` that multiplexes terminal
|
||||
interfaces for multiple userspace applications, but could also be used
|
||||
by a stand-alone application that talks to Dill directly.
|
||||
|
||||
The Gall agent specified in `p` is the session handler or stand-alone
|
||||
application. The `q` field contains a list of `gill:gall`, which are
|
||||
pairs of `[ship term]`, representing an app on a ship. The `gill`s in
|
||||
`q` are the list of apps being managed by the session handler `q` that
|
||||
should be notified of being connected to this session. If `p` were a
|
||||
stand-alone application, `q` could be empty, or else just contain that
|
||||
one app.
|
||||
|
||||
Dill will poke every agent listed in `q` (local or remote) with a
|
||||
[`%yow` `$dill-belt`](/reference/arvo/dill/data-types#dill-belt), to let
|
||||
it know it's been connected. It will also `%watch` the agent `p` for
|
||||
[`$dill-blit`](/reference/arvo/dill/data-types#dill-blit)s in `%fact`s
|
||||
with a `%dill-blit` `mark` on the `path` `/dill/[ses]` where `ses` is
|
||||
the session specified in the [`%shot`](#shot) wrapper.
|
||||
|
||||
Additionally, the source of the `%open` request (as determined by the
|
||||
`duct`, typically the agent in `p`) will begin receiving terminal output
|
||||
gifts for the session in question. Essentially, it behaves as though you
|
||||
also passed it a [`%view`](#view) task.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not give a `gift` in direct response to an `%open` `task`. It
|
||||
will, however, start giving terminal output `%blit` `gift`s to the
|
||||
requester as the occur for the session. A `%blit` `gift` looks like:
|
||||
|
||||
```hoon
|
||||
[%blit p=(list blit)]
|
||||
```
|
||||
|
||||
See the [`$blit`](/reference/arvo/dill/data-types#blit) entry in the
|
||||
data type reference for details of what it can contain.
|
||||
|
||||
This subscription for `$blit`s can be stopped with a [`%flee`](#flee)
|
||||
`task` at any time.
|
||||
|
||||
---
|
||||
|
||||
### `%shut`
|
||||
|
||||
Close session.
|
||||
|
||||
```hoon
|
||||
[%shut ~]
|
||||
```
|
||||
|
||||
This `task` needs to be wrapped in [`%shot`](#shot) `task` to specify
|
||||
the session to close. Otherwise, it will default to the default session
|
||||
(`%$`).
|
||||
|
||||
The session handler will be passed a `%leave`. Subscribers for `$blit`s
|
||||
will not be notified, they'll just stop receiving `$blit`s.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not give a `gift` in response to a `%shut` `task`. It will,
|
||||
however, pass a `%leave` to the session handler agent.
|
||||
|
||||
---
|
||||
|
||||
### `%view`
|
||||
|
||||
Watch session.
|
||||
|
||||
```hoon
|
||||
[%view ~]
|
||||
```
|
||||
|
||||
A `%view` `task` subscribes for a copy of all `%blit` `gift`s which Dill
|
||||
`%give`s for the session in question. This `task` must be wrapped in a
|
||||
[`%shot`](#shot) `task` which specifies the session if you want to
|
||||
subscribe to anything other than the default session (`%$`).
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill will begin giving a copy of all `%blit`s for the session specified
|
||||
in the [`%shot`](#shot) wrapper, or the default session (`%$`) if a
|
||||
`%shot` wrapper is not used. A `%blit` `gift` is:
|
||||
|
||||
```hoon
|
||||
[%blit p=(list blit)]
|
||||
```
|
||||
|
||||
See the [`$blit`](/reference/arvo/dill/data-types#blit) entry in the
|
||||
data type reference for more details.
|
||||
|
||||
The subscription can be ended with a [`%flee`](#flee) `task`.
|
||||
|
||||
---
|
||||
|
||||
## Told Tasks
|
||||
|
||||
This subset of Dill `task`s are for printing things. They are defined in
|
||||
the [`$told` type](/reference/arvo/dill/data-types#told).
|
||||
|
||||
### `%crud`
|
||||
|
||||
Print error.
|
||||
|
||||
```hoon
|
||||
[%crud p=@tas q=tang]
|
||||
```
|
||||
|
||||
Dill prints the given error to the terminal.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%crud` `task`.
|
||||
|
||||
---
|
||||
|
||||
### `%talk`
|
||||
|
||||
Print `tang` to terminal.
|
||||
|
||||
```hoon
|
||||
[%talk p=(list tank)]
|
||||
```
|
||||
|
||||
The `tank`s in `p` will be printed to the terminal, first to last.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%talk` `task`.
|
||||
|
||||
---
|
||||
|
||||
### `%text`
|
||||
|
||||
Print `tape` to terminal.
|
||||
|
||||
```hoon
|
||||
[%text p=tape]
|
||||
```
|
||||
|
||||
The `tape` in `p` will be printed to the terminal.
|
||||
|
||||
#### Returns
|
||||
|
||||
Dill does not return a `gift` in response to a `%text` `task`.
|
||||
|
||||
---
|
||||
|
||||
|
@ -11,6 +11,24 @@ This document describes the data types used by Eyre as defined in `/sys/lull.hoo
|
||||
|
||||
## Eyre
|
||||
|
||||
### `$cache-entry`
|
||||
|
||||
```hoon
|
||||
+$ cache-entry
|
||||
$: auth=?
|
||||
$= body
|
||||
$% [%payload =simple-payload:http]
|
||||
== ==
|
||||
```
|
||||
|
||||
- `auth`: Whether the request must include a valid session cookie or otherwise
|
||||
be authenticated. If this is false, the entry will be publicly accessible.
|
||||
- `body`: The HTTP response to give. This contains a `[%payload
|
||||
=simple-payload:http]`. See the [`$simple-payload:http`](#simple-payloadhttp)
|
||||
for more details of the data.
|
||||
|
||||
---
|
||||
|
||||
### `$origin`
|
||||
|
||||
```hoon
|
||||
@ -19,6 +37,8 @@ This document describes the data types used by Eyre as defined in `/sys/lull.hoo
|
||||
|
||||
A single CORS origin as used in an HTTP Origin header and the [$cors-registry](#cors-registry).
|
||||
|
||||
---
|
||||
|
||||
### `$cors-registry`
|
||||
|
||||
```hoon
|
||||
@ -31,6 +51,8 @@ A single CORS origin as used in an HTTP Origin header and the [$cors-registry](#
|
||||
|
||||
CORS origins categorised by approval status. The `requests` `set` contains all [$origin](#origin)s Eyre has received in the headers of HTTP requests that have not been explicitly approved or rejected. The `approved` and `rejected` `set`s are those that have been explicitly approved or rejected.
|
||||
|
||||
---
|
||||
|
||||
### `$outstanding-connection`
|
||||
|
||||
```hoon
|
||||
@ -44,6 +66,8 @@ CORS origins categorised by approval status. The `requests` `set` contains all [
|
||||
|
||||
An HTTP connection that is currently open. The [$action](#action) is how it's being handled (e.g. by a Gall app, the channel system, etc). The [$inbound-request](#inbound-request) is the original request which opened the connection. The `response-header` contains the status code and headers. The `bytes-sent` is the total bytes sent so far in response.
|
||||
|
||||
---
|
||||
|
||||
### `$authentication-state`
|
||||
|
||||
```hoon
|
||||
@ -52,6 +76,8 @@ An HTTP connection that is currently open. The [$action](#action) is how it's be
|
||||
|
||||
This represents the authentication state of all sessions. It maps session cookies (without the `urbauth-{SHIP}=` prefix) to [$session](#session)s.
|
||||
|
||||
---
|
||||
|
||||
### `$session`
|
||||
|
||||
```hoon
|
||||
@ -63,6 +89,8 @@ This represents the authentication state of all sessions. It maps session cookie
|
||||
|
||||
This represents server-side data about a session. The `expiry-time` is when the `session` expires and `channels` is the `set` of [$channel](#channel) names opened by the session.
|
||||
|
||||
---
|
||||
|
||||
### `$channel-state`
|
||||
|
||||
```hoon
|
||||
@ -74,6 +102,8 @@ This represents server-side data about a session. The `expiry-time` is when the
|
||||
|
||||
The state used by the channel system. The `session` is a `map` between channel names and [$channel](#channel)s and the `duct-to-key` `map`s `duct`s to `channel` names.
|
||||
|
||||
---
|
||||
|
||||
### `$timer`
|
||||
|
||||
```hoon
|
||||
@ -85,6 +115,8 @@ The state used by the channel system. The `session` is a `map` between channel n
|
||||
|
||||
A reference to a timer so it can be cancelled or updated. The `date` is when it will fire and the `duct` is what set the timer.
|
||||
|
||||
---
|
||||
|
||||
### `$channel-event`
|
||||
|
||||
```hoon
|
||||
@ -98,21 +130,42 @@ A reference to a timer so it can be cancelled or updated. The `date` is when it
|
||||
|
||||
An unacknowledged event in the channel system.
|
||||
|
||||
---
|
||||
|
||||
### `$channel`
|
||||
|
||||
```hoon
|
||||
+$ channel
|
||||
$: state=(each timer duct)
|
||||
next-id=@ud
|
||||
last-ack=@da
|
||||
events=(qeu [id=@ud request-id=@ud =channel-event])
|
||||
unacked=(map @ud @ud)
|
||||
subscriptions=(map @ud [ship=@p app=term =path duc=duct])
|
||||
heartbeat=(unit timer)
|
||||
==
|
||||
+$ channel
|
||||
$: mode=?(%json %jam)
|
||||
state=(each timer duct)
|
||||
next-id=@ud
|
||||
last-ack=@da
|
||||
events=(qeu [id=@ud request-id=@ud =channel-event])
|
||||
unacked=(map @ud @ud)
|
||||
subscriptions=(map @ud [ship=@p app=term =path duc=duct])
|
||||
heartbeat=(unit timer)
|
||||
==
|
||||
|
||||
```
|
||||
|
||||
This is the state of a particular channel in the channel system. The `state` is either the expiration time or the duct currently listening. The `next-id` is the next event ID to be used in the event stream. The `last-ack` is the date of the last client ack and is used for clog calculations in combination with `unacked`. The `events` queue contains all unacked events - `id` is the server-set event ID, `request-id` is the client-set request ID and the [$channel-event](#channel-event) is the event itself. The `unacked` `map` contains the unacked event count per `request-id` and is used for clog calculations. The `subscriptions` `map` contains gall subscriptions by `request-id`. The `heartbeat` is the SSE heartbeat [$timer](#timer).
|
||||
This is the state of a particular channel in the channel system.
|
||||
|
||||
- `mode` says whether the channel sends/received JSON or
|
||||
[nouns](/guides/additional/noun-channels).
|
||||
- `state` is either the expiration time or the duct currently listening.
|
||||
- `next-id` is the next event ID to be used in the event stream.
|
||||
- `last-ack` is the date of the last client ack and is used for clog
|
||||
calculations in combination with `unacked`.
|
||||
- `events` queue contains all unacked events:
|
||||
- `id` is the server-set event ID.
|
||||
- `request-id` is the client-set request ID.
|
||||
- [$channel-event](#channel-event) is the event itself.
|
||||
- `unacked` `map` contains the unacked event count per `request-id` and is used
|
||||
for clog calculations.
|
||||
- `subscriptions` `map` contains gall subscriptions by `request-id`.
|
||||
- `heartbeat` is the SSE heartbeat [$timer](#timer).
|
||||
|
||||
---
|
||||
|
||||
### `$binding`
|
||||
|
||||
@ -125,22 +178,27 @@ This is the state of a particular channel in the channel system. The `state` is
|
||||
|
||||
A `binding` is a rule to match a URL `path` and optional `site` domain which can then be tied to an [$action](#action). A `path` of `/foo` will also match `/foo/bar`, `/foo/bar/baz`, etc. If the `site` is `~` it will be determined implicitly. A binding must be unique.
|
||||
|
||||
---
|
||||
|
||||
### `$action`
|
||||
|
||||
```hoon
|
||||
+$ action
|
||||
$% [%gen =generator] :: dispatch to a generator
|
||||
[%app app=term] :: dispatch to an application
|
||||
[%authentication ~] :: internal authentication page
|
||||
[%logout ~] :: internal logout page
|
||||
[%channel ~] :: gall channel system
|
||||
[%scry ~] :: gall scry endpoint
|
||||
[%four-oh-four ~] :: respond with the default file not found page
|
||||
==
|
||||
+$ action
|
||||
$% [%gen =generator]
|
||||
[%app app=term]
|
||||
[%authentication ~]
|
||||
[%logout ~]
|
||||
[%channel ~]
|
||||
[%scry ~]
|
||||
[%name ~]
|
||||
[%four-oh-four ~]
|
||||
==
|
||||
```
|
||||
|
||||
The action to take when a [$binding](#binding) matches an incoming HTTP request.
|
||||
|
||||
---
|
||||
|
||||
### `$generator`
|
||||
|
||||
```hoon
|
||||
@ -153,6 +211,8 @@ The action to take when a [$binding](#binding) matches an incoming HTTP request.
|
||||
|
||||
This refers to a generator on a local ship that can handle requests. Note that serving generators via Eyre is not fully implmented and should not be used.
|
||||
|
||||
---
|
||||
|
||||
### `$http-config`
|
||||
|
||||
```hoon
|
||||
@ -166,6 +226,8 @@ This refers to a generator on a local ship that can handle requests. Note that s
|
||||
|
||||
The configuration of the runtime HTTP server itself. The `secure` field contains the PEM-encoded RSA private key and certificate or certificate chain when using HTTPS, and otherwise is `~` when using plain HTTP. The `proxy` field is not currently used. The `log` field turns on HTTP(S) access logs but is not currently implemented. The `redirect` field turns on 301 redirects to upgrade HTTP to HTTPS if the `key` and `cert` are set in `secure`.
|
||||
|
||||
---
|
||||
|
||||
### `$http-rule`
|
||||
|
||||
```hoon
|
||||
@ -177,6 +239,8 @@ The configuration of the runtime HTTP server itself. The `secure` field contains
|
||||
|
||||
This is for updating the server configuration. In the case of `%cert`, a `cert` of `~` clears the HTTPS cert & key, otherwise `cert` contains the PEM-encoded RSA private key and certificate or certificate chain. In the case of `%turf`, a `%put` `action` sets a domain name and a `%del` `action` removes it. The [$turf](#turf) contains the domain name.
|
||||
|
||||
---
|
||||
|
||||
### `$address`
|
||||
|
||||
```hoon
|
||||
@ -188,6 +252,8 @@ This is for updating the server configuration. In the case of `%cert`, a `cert`
|
||||
|
||||
A client IP address.
|
||||
|
||||
---
|
||||
|
||||
### `$inbound-request`
|
||||
|
||||
```hoon
|
||||
@ -201,6 +267,8 @@ A client IP address.
|
||||
|
||||
An inbound HTTP request and metadata. The `authenticated` field says whether the request was made with a valid session cookie. The `secure` field says whether it was made with HTTPS. The [$address](#address) is the IP address from which the request originated, except if it came from localhost and included a `Forwarded` header, in which case it's the address specified in that header. The [$request:http](#requesthttp) contains the HTTP request itself.
|
||||
|
||||
---
|
||||
|
||||
## HTTP
|
||||
|
||||
### `$header-list:http`
|
||||
@ -211,6 +279,8 @@ An inbound HTTP request and metadata. The `authenticated` field says whether the
|
||||
|
||||
An ordered list of HTTP headers. The `key` is the header name e.g `'Content-Type'` and the `value` is the value e.g. `text/html`.
|
||||
|
||||
---
|
||||
|
||||
### `$method:http`
|
||||
|
||||
```hoon
|
||||
@ -228,6 +298,8 @@ An ordered list of HTTP headers. The `key` is the header name e.g `'Content-Type
|
||||
|
||||
An HTTP method.
|
||||
|
||||
---
|
||||
|
||||
### `$request:http`
|
||||
|
||||
```hoon
|
||||
@ -241,6 +313,8 @@ An HTTP method.
|
||||
|
||||
A single HTTP request. The [$method:http](#methodhttp) is the HTTP method, the `url` is the unescaped URL, the [$header-list:http](#header-listhttp) contains the HTTP headers of the request and the `body` is the actual data. An `octs` is just `[p=@ud q=@]` where `p` is the byte-length of `q`, the data.
|
||||
|
||||
---
|
||||
|
||||
### `$response-header:http`
|
||||
|
||||
```hoon
|
||||
@ -252,6 +326,8 @@ A single HTTP request. The [$method:http](#methodhttp) is the HTTP method, the `
|
||||
|
||||
The status code and [$header-list:http](#header-listhttp) of an HTTP response.
|
||||
|
||||
---
|
||||
|
||||
### `$http-event:http`
|
||||
|
||||
```hoon
|
||||
@ -275,6 +351,8 @@ Urbit treats Earth's HTTP servers as pipes, where Urbit sends or receives one or
|
||||
|
||||
Calculation of control headers such as `'Content-Length'` or `'Transfer-Encoding'` should be performed at a higher level; this structure is merely for what gets sent to or received from Earth.
|
||||
|
||||
---
|
||||
|
||||
### `$simple-payload:http`
|
||||
|
||||
```hoon
|
||||
@ -286,3 +364,5 @@ Calculation of control headers such as `'Content-Length'` or `'Transfer-Encoding
|
||||
```
|
||||
|
||||
A simple, one-event response used for generators. The [$reponse-header:http](#response-headerhttp) contains the status code and HTTP headers. The `octs` in the `data` contains the body of the response and is a `[p=@ud q=@]` where `p` is the byte-length of `q`, the data.
|
||||
|
||||
---
|
||||
|
@ -3,9 +3,12 @@ title = "Scry Reference"
|
||||
weight = 4
|
||||
+++
|
||||
|
||||
Here are all of Eyre's scry endpoints. There's not too many and they mostly deal with either CORS settings or aspects of the state of connections.
|
||||
Here are all of Eyre's scry endpoints. There's not too many and they mostly
|
||||
deal with either CORS settings or aspects of the state of connections.
|
||||
|
||||
The first few have a `care` of `x` and are a scry like `.^({TYPE} %ex /=//=/{SOME-PATH})` (note the empty `desk`). The rest have no `care` and the tag replaces the `desk` like `.^({TYPE} %e /={SOMETHING}=)`.
|
||||
The first few have a `care` of `x` and are a scry like `.^([type] %ex
|
||||
/=//=/[some-path])` (note the empty `desk`). The rest have no `care` and the
|
||||
tag replaces the `desk` like `.^([type] %e /=[something]=)`.
|
||||
|
||||
All examples are run from the dojo.
|
||||
|
||||
@ -13,7 +16,7 @@ All examples are run from the dojo.
|
||||
|
||||
An `x` scry with a `path` of `/cors` will return Eyre's CORS origin registry. The type returned is a [cors-registry](/reference/arvo/eyre/data-types#cors-registry) which contains the `set`s of approved, rejected and requested origins.
|
||||
|
||||
### Example {% #example-1 %}
|
||||
#### Example {% #example-1 %}
|
||||
|
||||
```
|
||||
> .^(cors-registry:eyre %ex /=//=/cors)
|
||||
@ -23,35 +26,48 @@ An `x` scry with a `path` of `/cors` will return Eyre's CORS origin registry. Th
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/cors/requests`
|
||||
|
||||
An `x` scry with a `path` of `/cors/requests` will return the `set` of pending origin requests. These are origins that were in an `Origin: ...` HTTP header but weren't in the existing approved or rejected sets. The type returned is a `(set origin:eyre)`.
|
||||
An `x` scry with a `path` of `/cors/requests` will return the `set` of pending
|
||||
origin requests. These are origins that were in an `Origin: ...` HTTP header
|
||||
but weren't in the existing approved or rejected sets. The type returned is a
|
||||
`(set origin:eyre)`.
|
||||
|
||||
### Example {% #example-2 %}
|
||||
#### Example {% #example-2 %}
|
||||
|
||||
```
|
||||
> .^(requests=(set origin:eyre) %ex /=//=/cors/requests)
|
||||
requests={~~http~3a.~2f.~2f.baz~.example}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/cors/approved`
|
||||
|
||||
An `x` scry with a `path` of `/cors/approved` will return the `set` of approved CORS origins. The type returned is a `(set origin:eyre)`.
|
||||
|
||||
### Example {% #example-3 %}
|
||||
#### Example {% #example-3 %}
|
||||
|
||||
```
|
||||
> .^(approved=(set origin:eyre) %ex /=//=/cors/approved)
|
||||
approved={~~http~3a.~2f.~2f.foo~.example}
|
||||
```
|
||||
|
||||
## `/cors/approved/{ORIGIN}`
|
||||
---
|
||||
|
||||
An `x` scry whose `path` is `/cors/approved/{ORIGIN}` tests whether the given origin URL is in the `approved` set of the CORS registry. The type returned is a simple `?`.
|
||||
## `/cors/approved/[origin]`
|
||||
|
||||
The origin URL is a `@t`, but since `@t` may not be valid in a path, it must be encoded in a `@ta` using `+scot` like `(scot %t 'foo')` rather than just `'foo'`.
|
||||
An `x` scry whose `path` is `/cors/approved/[origin]` tests whether the given
|
||||
origin URL is in the `approved` set of the CORS registry. The type returned is
|
||||
a simple `?`.
|
||||
|
||||
### Examples {% #examples-1 %}
|
||||
The origin URL is a `@t`, but since `@t` may not be valid in a path, it must be
|
||||
encoded in a `@ta` using `+scot` like `(scot %t 'foo')` rather than just
|
||||
`'foo'`.
|
||||
|
||||
#### Examples {% #examples-1 %}
|
||||
|
||||
```
|
||||
> .^(? %ex /=//=/cors/approved/(scot %t 'http://foo.example'))
|
||||
@ -63,24 +79,32 @@ The origin URL is a `@t`, but since `@t` may not be valid in a path, it must be
|
||||
%.n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/cors/rejected`
|
||||
|
||||
An `x` scry with a `path` of `/cors/rejected` will return the `set` of rejected CORS origins. The type returned is a `(set origin:eyre)`.
|
||||
An `x` scry with a `path` of `/cors/rejected` will return the `set` of rejected
|
||||
CORS origins. The type returned is a `(set origin:eyre)`.
|
||||
|
||||
### Example {% #example-4 %}
|
||||
#### Example {% #example-4 %}
|
||||
|
||||
```
|
||||
> .^(rejected=(set origin:eyre) %ex /=//=/cors/rejected)
|
||||
rejected={~~http~3a.~2f.~2f.bar~.example}
|
||||
```
|
||||
|
||||
## `/cors/rejected/{ORIGIN}`
|
||||
---
|
||||
|
||||
An `x` scry whose `path` is `/cors/rejected/{ORIGIN}` tests whether the given origin URL is in the `rejected` set of the CORS registry. The type returned is a simple `?`.
|
||||
## `/cors/rejected/[origin]`
|
||||
|
||||
The origin URL must be a cord-encoded `@t` rather than just the plain `@t`, so you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`.
|
||||
An `x` scry whose `path` is `/cors/rejected/[origin]` tests whether the given
|
||||
origin URL is in the `rejected` set of the CORS registry. The type returned is
|
||||
a simple `?`.
|
||||
|
||||
### Examples {% #examples-2 %}
|
||||
The origin URL must be a cord-encoded `@t` rather than just the plain `@t`, so
|
||||
you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`.
|
||||
|
||||
#### Examples {% #examples-2 %}
|
||||
|
||||
```
|
||||
> .^(? %ex /=//=/cors/rejected/(scot %t 'http://bar.example'))
|
||||
@ -92,13 +116,18 @@ The origin URL must be a cord-encoded `@t` rather than just the plain `@t`, so y
|
||||
%.n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/authenticated/cookie`
|
||||
|
||||
An `x` scry whose `path` is `/authenticated/cookie/{COOKIE}` tests whether the given cookie is currently valid. The type returned is a `?`.
|
||||
An `x` scry whose `path` is `/authenticated/cookie/[cookie]` tests whether the
|
||||
given cookie is currently valid. The type returned is a `?`.
|
||||
|
||||
The cookie must be the full cookie including the `urbauth-{SHIP}=` part. The cookie must be a cord-encoded `@t` rather than just a plain `@t`, so you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`.
|
||||
The cookie must be the full cookie including the `urbauth-{SHIP}=` part. The
|
||||
cookie must be a knot-encoded `@t` rather than just a plain `@t`, so you'll
|
||||
have to do something like `(scot %t 'foo')` rather than just `'foo'`.
|
||||
|
||||
### Examples {% #examples-3 %}
|
||||
#### Examples {% #examples-3 %}
|
||||
|
||||
```
|
||||
> .^(? %ex /=//=/authenticated/cookie/(scot %t 'urbauth-~zod=0vvndn8.bfsjj.j3614.k40ha.8fomi'))
|
||||
@ -110,11 +139,27 @@ The cookie must be the full cookie including the `urbauth-{SHIP}=` part. The coo
|
||||
%.n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/cache/[aeon]/[url]`
|
||||
|
||||
An `%x` `/cache` scry will return the cached value for the given `[url]` at the
|
||||
given `[aeon]` if it exists.
|
||||
|
||||
The `[url]` must be a knot-encoded `@t` rather than just a plain `@t`, so
|
||||
you'll have to do something like `(scot %t 'foo')` rather than just `'foo'`.
|
||||
|
||||
---
|
||||
|
||||
## `%bindings`
|
||||
|
||||
A scry with `bindings` in place of the `desk` in the `beak` will return Eyre's URL path bindings. The type returned is a `(list [binding:eyre duct action:eyre])` (see the [$binding](/reference/arvo/eyre/data-types#binding) & [$action](/reference/arvo/eyre/data-types#action) sections of the Data Types document for details).
|
||||
A scry with `bindings` in place of the `desk` in the `beak` will return Eyre's
|
||||
URL path bindings. The type returned is a `(list [binding:eyre duct
|
||||
action:eyre])` (see the [$binding](/reference/arvo/eyre/data-types#binding) &
|
||||
[$action](/reference/arvo/eyre/data-types#action) sections of the Data Types
|
||||
document for details).
|
||||
|
||||
### Example {% #example-5 %}
|
||||
#### Example {% #example-5 %}
|
||||
|
||||
```
|
||||
> .^((list [binding:eyre duct action:eyre]) %e /=bindings=)
|
||||
@ -133,20 +178,29 @@ A scry with `bindings` in place of the `desk` in the `beak` will return Eyre's U
|
||||
|
||||
## `%connections`
|
||||
|
||||
A scry with `bindings` in place of the `desk` in the `beak` will return all open HTTP connections that aren't fully complete. The type returned is a `(map duct outstanding-connection:eyre)` (see the [$outstanding-connection](/reference/arvo/eyre/data-types#outstanding-connection) section of the Data Types document for details).
|
||||
A scry with `connections` in place of the `desk` in the `beak` will return all
|
||||
open HTTP connections that aren't fully complete. The type returned is a `(map
|
||||
duct outstanding-connection:eyre)` (see the
|
||||
[$outstanding-connection](/reference/arvo/eyre/data-types#outstanding-connection)
|
||||
section of the Data Types document for details).
|
||||
|
||||
### Example {% #example-6 %}
|
||||
#### Example {% #example-6 %}
|
||||
|
||||
```
|
||||
> .^((map duct outstanding-connection:eyre) %e /=connections=)
|
||||
{}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `%authentication-state`
|
||||
|
||||
A scry with `authentication-state` in place of the `desk` in the `beak` will return authentication details of all current sessions. The type returned is a [$authentication-state](/reference/arvo/eyre/data-types#authentication-state). The `p` field is the cookie sans the `urbauth-{SHIP}=` part.
|
||||
A scry with `authentication-state` in place of the `desk` in the `beak` will
|
||||
return authentication details of all current sessions. The type returned is a
|
||||
[$authentication-state](/reference/arvo/eyre/data-types#authentication-state).
|
||||
The `p` field is the cookie sans the `urbauth-[ship]=` part.
|
||||
|
||||
### Example {% #example-7 %}
|
||||
#### Example {% #example-7 %}
|
||||
|
||||
```
|
||||
> .^(authentication-state:eyre %e /=authentication-state=)
|
||||
@ -157,11 +211,15 @@ A scry with `authentication-state` in place of the `desk` in the `beak` will ret
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `%channel-state`
|
||||
|
||||
A scry with `channel-state` in place of the `desk` in the `beak` will return details of the state of each channel. The type returned is a [channel-state](/reference/arvo/eyre/data-types#channel-state).
|
||||
A scry with `channel-state` in place of the `desk` in the `beak` will return
|
||||
details of the state of each channel. The type returned is a
|
||||
[channel-state](/reference/arvo/eyre/data-types#channel-state).
|
||||
|
||||
### Example {% #example-8 %}
|
||||
#### Example {% #example-8 %}
|
||||
|
||||
```
|
||||
> .^(channel-state:eyre %e /=channel-state=)
|
||||
@ -182,13 +240,18 @@ A scry with `channel-state` in place of the `desk` in the `beak` will return det
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `%host`
|
||||
|
||||
A scry with `host` in place of the `desk` in the `beak` will return host details of the ship. The type returned is a `hart:eyre`.
|
||||
A scry with `host` in place of the `desk` in the `beak` will return host
|
||||
details of the ship. The type returned is a `hart:eyre`.
|
||||
|
||||
### Example {% #example-9 %}
|
||||
#### Example {% #example-9 %}
|
||||
|
||||
```
|
||||
> .^(hart:eyre %e /=host=)
|
||||
[p=%.n q=[~ 8.080] r=[%.y p=<|localhost|>]]
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -23,6 +23,8 @@ The `insecure` field is the HTTP port and `secure` is the optional HTTPS port.
|
||||
|
||||
Eyre returns no `gift` in response to a `%live` `task`.
|
||||
|
||||
---
|
||||
|
||||
## `%rule`
|
||||
|
||||
```hoon
|
||||
@ -37,6 +39,8 @@ The [$http-rule](/reference/arvo/eyre/data-types#http-rule) is either tagged wit
|
||||
|
||||
Eyre returns no `gift` in response to a `%rule` `task`.
|
||||
|
||||
---
|
||||
|
||||
## `%request`
|
||||
|
||||
```hoon
|
||||
@ -51,6 +55,8 @@ The `secure` field says whether it's over HTTPS. The `address` is the IP address
|
||||
|
||||
Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the contents of the `%request`, state of the connection, and other factors.
|
||||
|
||||
---
|
||||
|
||||
## `%request-local`
|
||||
|
||||
```hoon
|
||||
@ -63,6 +69,8 @@ This `task` is how Eyre receives an inbound HTTP request over the local loopback
|
||||
|
||||
Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the contents of the `%request`, state of the connection, and other factors.
|
||||
|
||||
---
|
||||
|
||||
## `%cancel-request`
|
||||
|
||||
```hoon
|
||||
@ -77,6 +85,8 @@ This `task` takes no arguments.
|
||||
|
||||
Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the state of the connection and other factors.
|
||||
|
||||
---
|
||||
|
||||
## `%connect`
|
||||
|
||||
```hoon
|
||||
@ -111,6 +121,8 @@ The `accepted` field says whether the binding succeeded and the `binding` is the
|
||||
|
||||
See the [Agents: Direct HTTP](/reference/arvo/eyre/guide#agents-direct-http) section of the [Guide](/reference/arvo/eyre/guide) document for an example.
|
||||
|
||||
---
|
||||
|
||||
## `%serve`
|
||||
|
||||
```hoon
|
||||
@ -147,6 +159,8 @@ Eyre will return a `%bound` `gift` as described at the end of the [%connect](#co
|
||||
|
||||
See the [Generators](/reference/arvo/eyre/guide#generators) section of the [Guide](/reference/arvo/eyre/guide) document for an example.
|
||||
|
||||
---
|
||||
|
||||
## `%disconnect`
|
||||
|
||||
```hoon
|
||||
@ -161,6 +175,8 @@ The [$binding](/reference/arvo/eyre/data-types#binding) is the URL path and doma
|
||||
|
||||
Eyre returns no `gift` in response to a `%disconnect` `task`.
|
||||
|
||||
---
|
||||
|
||||
## `%code-changed`
|
||||
|
||||
```hoon
|
||||
@ -210,3 +226,47 @@ Eyre returns no `gift` in response to a `%reject-origin` `task`.
|
||||
#### Example
|
||||
|
||||
See the [Managing CORS Origins](/reference/arvo/eyre/guide#managing-cors-origins) section of the [Guide](/reference/arvo/eyre/guide) document for an example.
|
||||
|
||||
---
|
||||
## `%set-response`
|
||||
|
||||
```hoon
|
||||
[%set-response url=@t entry=(unit cache-entry)]
|
||||
```
|
||||
|
||||
This `task` tells Eyre to set a cache entry for a URL path. Adding entries to
|
||||
Eyre's cache will make them much faster to load, and more capable of handling
|
||||
many connections.
|
||||
|
||||
The `url` field is the URL path you want to bind with the cache entry. Note this
|
||||
will just be the URL path as a cord like `'/foo/bar/baz'`, it does not include
|
||||
the host, etc.
|
||||
|
||||
The `entry` field is a
|
||||
[`$cache-entry`](/reference/arvo/eyre/data-types#cache-entry) in a `unit`. If
|
||||
the unit is null, the specified `url` will be unbound and the cache entry
|
||||
removed. If non-null, the given `entry` will be added to the cache (or updated
|
||||
if the binding already exists).
|
||||
|
||||
Each time the entry for a URL path is changed, its revision number will be
|
||||
incremented.
|
||||
|
||||
See the [`$cache-entry`](/reference/arvo/eyre/data-types#cache-entry) entry in
|
||||
Eyre's type reference for more details of the entry itself.
|
||||
|
||||
#### Returns
|
||||
|
||||
Eyre gives a `%grow` `gift` in response to a `%set-response` `task`. A `%grow`
|
||||
`gift` looks like:
|
||||
|
||||
```hoon
|
||||
[%grow =path]
|
||||
```
|
||||
|
||||
The `path` will be of the format `/cache/[revision]/[url]`, for example
|
||||
`/cache/12/~~~2f.foo~2f.bar`. The revision number is incremented each time the
|
||||
entry is updated, including if it's removed, and is in `@ud` format. The url
|
||||
element uses `%t` [`++scot`](/reference/hoon/stdlib/4m#scot) encoding, so will
|
||||
need to be decoded with `%t` [`++slav`](/reference/hoon/stdlib/4m#slav).
|
||||
|
||||
---
|
||||
|
@ -4,3 +4,20 @@ weight = 70
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
|
||||
## [Overview](/reference/arvo/gall/gall)
|
||||
|
||||
An overview of Eyre and its capabilities.
|
||||
|
||||
## [API Reference](/reference/arvo/gall/gall-api)
|
||||
|
||||
Reference documentation of both Gall's vane API and the API available to
|
||||
agents.
|
||||
|
||||
## [Scry Reference](/reference/arvo/gall/scry)
|
||||
|
||||
Reference documentation of Gall scry endpoints.
|
||||
|
||||
## [Data Types](/reference/arvo/gall/data-types)
|
||||
|
||||
Reference documentation of Gall's types as defined in `lull.hoon`.
|
||||
|
@ -5,59 +5,79 @@ weight = 5
|
||||
|
||||
This document describes the data types for Gall defined in `lull.hoon`.
|
||||
|
||||
|
||||
## `bitt`
|
||||
|
||||
Incoming subscriptions.
|
||||
|
||||
```hoon
|
||||
+$ bitt (map duct (pair ship path))
|
||||
```
|
||||
|
||||
Incoming subscriptions.
|
||||
This is the structure Gall uses to keep track of incoming subscriptions
|
||||
for a Gall agent. The `sup` field of a [`bowl`](#bowl) contains the
|
||||
`bitt` for our agent.
|
||||
|
||||
This is the structure Gall uses to keep track of subscribers to our Gall agent.
|
||||
The `sup` field of a [`bowl`](#bowl) contains the `bitt` for our agent.
|
||||
---
|
||||
|
||||
## `boat`
|
||||
|
||||
```hoon
|
||||
+$ boat
|
||||
%+ map [=wire =ship =term]
|
||||
[acked=? =path]
|
||||
```
|
||||
|
||||
Outgoing subscriptions.
|
||||
|
||||
This is the structure Gall uses to keep track of subscriptions our agent has
|
||||
initiated. The `wex` field of a [`bowl`](#bowl) contails the `boat` for that
|
||||
agent.
|
||||
```hoon
|
||||
+$ boat (map [=wire =ship =term] [acked=? =path])
|
||||
```
|
||||
|
||||
The `wire` field is the `wire` which [`sign:agent`](#signagent)s will come in on.
|
||||
The `ship` and `term` fields are the ship and the name of the agent to which our
|
||||
agent has subscribed.
|
||||
This is the structure Gall uses to keep track of subscriptions our agent
|
||||
has initiated. The `wex` field of a [`bowl`](#bowl) contails the `boat`
|
||||
for that agent.
|
||||
|
||||
The `acked` field is `%.y` if they have acknowledged our subscription request,
|
||||
and `%.n` if they have not. The `path` field is the `path` on the other agent to
|
||||
which our agent has subscribed.
|
||||
The `wire` field is the `wire` which [`sign:agent`](#signagent)s will
|
||||
come in on. The `ship` and `term` fields are the ship and the name of
|
||||
the agent to which our agent has subscribed.
|
||||
|
||||
The `acked` field is `%.y` if they have acknowledged our subscription
|
||||
request with a `%watch-ack`, and `%.n` if they have not. The `path`
|
||||
field is the `path` on the other agent to which our agent has
|
||||
subscribed.
|
||||
|
||||
---
|
||||
|
||||
## `boar`
|
||||
|
||||
Subscription nonces.
|
||||
|
||||
```hoon
|
||||
+$ boar (map [=wire =ship =term] nonce=@)
|
||||
```
|
||||
|
||||
Gall uses this to keep track of nonces for subscriptions.
|
||||
|
||||
---
|
||||
|
||||
## `bowl`
|
||||
|
||||
Additional agent state.
|
||||
|
||||
```hoon
|
||||
+$ bowl :: standard app state
|
||||
$: $: our=ship :: host
|
||||
src=ship :: guest
|
||||
dap=term :: agent
|
||||
== ::
|
||||
$: wex=boat :: outgoing subs
|
||||
sup=bitt :: incoming subs
|
||||
== ::
|
||||
$: act=@ud :: change number
|
||||
eny=@uvJ :: entropy
|
||||
now=@da :: current time
|
||||
byk=beak :: load source
|
||||
== == ::
|
||||
$: $: our=ship :: host
|
||||
src=ship :: guest
|
||||
dap=term :: agent
|
||||
== ::
|
||||
$: wex=boat :: outgoing subs
|
||||
sup=bitt :: incoming subs
|
||||
$= sky :: scry bindings
|
||||
%+ map path ::
|
||||
((mop @ud (pair @da (each page @uvI))) lte) ::
|
||||
== ::
|
||||
$: act=@ud :: change number
|
||||
eny=@uvJ :: entropy
|
||||
now=@da :: current time
|
||||
byk=beak :: load source
|
||||
== == ::
|
||||
```
|
||||
|
||||
Additional agent state.
|
||||
|
||||
A `bowl` is given to the agent core each time an event comes in. The fields are
|
||||
as follows:
|
||||
|
||||
@ -68,6 +88,9 @@ as follows:
|
||||
See the [`boat`](#boat) section for details of the type.
|
||||
- `sup`: Incoming subscriptions. That is, subscriptions others have made to our
|
||||
agent. See the [`bitt`](#bitt) section for details of the type.
|
||||
- `sky`: Remote scry bindings. A map from binding paths to a
|
||||
[`mop`](/reference/hoon/zuse/2m#mop) (ordered map) of files by revision
|
||||
number. Tombstoned files have an `@uvI` hash rather than `page`.
|
||||
- `act`: The total number of [`move`](/reference/arvo/overview#move)s our agent has
|
||||
processed so far.
|
||||
- `eny`: 512 bits of entropy.
|
||||
@ -76,24 +99,49 @@ as follows:
|
||||
`case` will be `[%da @da]` where the `@da` is the when the agent was loaded. A
|
||||
`beak` is a triple of `[ship desk case]`.
|
||||
|
||||
---
|
||||
|
||||
## `dude`
|
||||
|
||||
Agent name.
|
||||
|
||||
```hoon
|
||||
+$ dude term
|
||||
```
|
||||
|
||||
Agent name.
|
||||
---
|
||||
|
||||
## `gill`
|
||||
|
||||
A general contact.
|
||||
|
||||
```hoon
|
||||
+$ gill (pair ship term)
|
||||
```
|
||||
|
||||
A general contact: A pair of the ship and agent name.
|
||||
A pair of the ship and agent name.
|
||||
|
||||
---
|
||||
|
||||
## `load`
|
||||
|
||||
Loadout.
|
||||
|
||||
```hoon
|
||||
+$ load (list [=dude =beak =agent])
|
||||
```
|
||||
|
||||
The [`dude`](#dude) is the agent name, the `beak` is the ship/desk/case
|
||||
in which it resides, and the [`agent`](#agent) is the built agent
|
||||
itself. Clay passes this to Gall when it builds or modifies the state of
|
||||
running agents.
|
||||
|
||||
---
|
||||
|
||||
## `scar`
|
||||
|
||||
Opaque duct - used internally.
|
||||
|
||||
```hoon
|
||||
+$ scar
|
||||
$: p=@ud
|
||||
@ -102,40 +150,30 @@ A general contact: A pair of the ship and agent name.
|
||||
==
|
||||
```
|
||||
|
||||
Opaque duct - used internally.
|
||||
---
|
||||
|
||||
## `suss`
|
||||
|
||||
Configuration report.
|
||||
|
||||
```hoon
|
||||
+$ suss (trel dude @tas @da)
|
||||
```
|
||||
|
||||
Configuration report.
|
||||
---
|
||||
|
||||
## `well`
|
||||
|
||||
Desk and agent.
|
||||
|
||||
```hoon
|
||||
+$ well (pair desk term)
|
||||
```
|
||||
|
||||
Desk and agent.
|
||||
|
||||
## `neat`
|
||||
|
||||
```hoon
|
||||
+$ neat
|
||||
$% [%arvo =note-arvo]
|
||||
[%agent [=ship name=term] =deal]
|
||||
[%pyre =tang]
|
||||
==
|
||||
```
|
||||
|
||||
Like a [`note:agent`](#noteagent), except the `%agent` case has a [`deal`](#deal) instead
|
||||
of just a [`task:agent`](#taskagent). This is used for messages that come in over the
|
||||
network and would not be used manually.
|
||||
|
||||
## `deal`
|
||||
|
||||
An agent task or raw poke.
|
||||
|
||||
```hoon
|
||||
+$ deal
|
||||
$% [%raw-poke =mark =noun]
|
||||
@ -143,12 +181,17 @@ network and would not be used manually.
|
||||
==
|
||||
```
|
||||
|
||||
Like a [`task:agent`](#taskagent) but with the additional case of a raw poke.
|
||||
This is used for messages that come in over the network and would not be used
|
||||
manually.
|
||||
The additional `%raw-poke` is for pokes which haven't yet been converted
|
||||
to an ordinary `%poke` by molding the `noun` with the specified `mark`
|
||||
core. This structure is passed around on the kernel level, it would not
|
||||
be used in userspace.
|
||||
|
||||
---
|
||||
|
||||
## `unto`
|
||||
|
||||
An agent gift or a raw fact.
|
||||
|
||||
```hoon
|
||||
+$ unto
|
||||
$% [%raw-fact =mark =noun]
|
||||
@ -156,9 +199,24 @@ manually.
|
||||
==
|
||||
```
|
||||
|
||||
Like a [`sign:agent`](#signagent) but with the additional case of a raw fact.
|
||||
This is used for messages that come in over the network and would not be used
|
||||
manually.
|
||||
The additional `%raw-fact` is for facts which haven't yet been converted
|
||||
to an ordinary `%fact` by molding the `noun` it with the specified
|
||||
`mark` core. This structure is passed around on the kernel level, it
|
||||
would not be used in userspace.
|
||||
|
||||
---
|
||||
|
||||
## `verb`
|
||||
|
||||
Verbosity flags.
|
||||
|
||||
```hoon
|
||||
+$ verb ?(%odd)
|
||||
```
|
||||
|
||||
Flags to set Gall verbosity. Currently only `%odd` for unusual errors.
|
||||
|
||||
---
|
||||
|
||||
## `agent`
|
||||
|
||||
@ -169,10 +227,10 @@ manually.
|
||||
```
|
||||
|
||||
Container for Gall agent types. The most significant arm is
|
||||
[`form:agent`](#formagent), which specifies the structure of the agent itself.
|
||||
There are also some additional structures defined here, mostly defining the
|
||||
kinds of messages agents can send. The different arms of the core in `agent`
|
||||
are considered separately below.
|
||||
[`form:agent`](#formagent), which specifies the structure of the agent
|
||||
itself. There are also some additional structures defined here, mostly
|
||||
defining the kinds of messages agents can send. The different arms of
|
||||
the core in `agent` are considered separately below.
|
||||
|
||||
### `step:agent`
|
||||
|
||||
@ -180,8 +238,12 @@ are considered separately below.
|
||||
+$ step (quip card form)
|
||||
```
|
||||
|
||||
A cell of [`card:agent`](#cardagent)s to be sent and a new agent state. This is the
|
||||
type returned by most arms of an agent. A `(quip a b)` is the same as `[(list a) b]`, it's just a more convenient way to specify it.
|
||||
A cell of [`card:agent`](#cardagent)s to be sent and a new agent state.
|
||||
This is the type returned by most arms of an agent. A `(quip a b)` is
|
||||
the same as `[(list a) b]`, it's just a more convenient way to specify
|
||||
it.
|
||||
|
||||
---
|
||||
|
||||
### `card:agent`
|
||||
|
||||
@ -189,9 +251,9 @@ type returned by most arms of an agent. A `(quip a b)` is the same as `[(list a)
|
||||
+$ card (wind note gift)
|
||||
```
|
||||
|
||||
An effect - typically a message to be sent to another agent or vane. A list of
|
||||
these are returned by most agent arms along with a new state in a
|
||||
[`step:agent`](#stepagent). A `wind` is the following:
|
||||
An effect - typically a message to be sent to another agent or vane. A
|
||||
list of these are returned by most agent arms along with a new state in
|
||||
a [`step:agent`](#stepagent). A `wind` is the following:
|
||||
|
||||
```hoon
|
||||
++ wind
|
||||
@ -207,9 +269,11 @@ Gall will not allow a `%slip`, so in practice a `card` will be one of:
|
||||
- `[%pass path note]`
|
||||
- `[%give gift]`
|
||||
|
||||
For `%pass`, `p` specifies the `wire` on which a response should be returned.
|
||||
See [`note:agent`](#noteagent) and [`gift:agent`](#giftagent) below for details of their
|
||||
types.
|
||||
For `%pass`, `p` specifies the `wire` on which a response should be
|
||||
returned. See [`note:agent`](#noteagent) and [`gift:agent`](#giftagent)
|
||||
below for details of their types.
|
||||
|
||||
---
|
||||
|
||||
### `note:agent`
|
||||
|
||||
@ -218,17 +282,23 @@ types.
|
||||
$% [%agent [=ship name=term] =task]
|
||||
[%arvo note-arvo]
|
||||
[%pyre =tang]
|
||||
::
|
||||
[%grow =spur =page]
|
||||
[%tomb =case =spur]
|
||||
[%cull =case =spur]
|
||||
==
|
||||
```
|
||||
|
||||
The type for messages initiated by our agent. This is opposed to
|
||||
[`gift:agent`](#giftagent), which is the type for responding to other agents or
|
||||
vanes, or for sending out updates to subscribers. The three cases are:
|
||||
[`gift:agent`](#giftagent), which is the type for responding to other
|
||||
agents or vanes, or for sending out updates to subscribers. The three
|
||||
cases are:
|
||||
|
||||
- `%agent`: Poke another agent, subscribe to another agent, or cancel a
|
||||
subscription to another agent. The `ship` and `name` fields are the ship and
|
||||
agent to which the `task` should be sent. The `task` is the request itself,
|
||||
see [`task:agent`](#taskagent) below for its possible types.
|
||||
subscription to another agent. The `ship` and `name` fields are the
|
||||
ship and agent to which the `task` should be sent. The `task` is the
|
||||
request itself, see [`task:agent`](#taskagent) below for its possible
|
||||
types.
|
||||
- `%arvo`: Pass a `task` to a vane. The type of a `note-arvo` is:
|
||||
```hoon
|
||||
+$ note-arvo
|
||||
@ -246,16 +316,23 @@ vanes, or for sending out updates to subscribers. The three cases are:
|
||||
[@tas %meta vase]
|
||||
==
|
||||
```
|
||||
You can refer to the `/sys/lull.hoon` source code for all the possible vane
|
||||
tasks, or see each vane's API Reference section in the [Arvo
|
||||
You can refer to the `/sys/lull.hoon` source code for all the possible
|
||||
vane tasks, or see each vane's API Reference section in the [Arvo
|
||||
documentation](/reference/arvo/overview)
|
||||
- `%pyre`: This is for aborting side-effects initiated during agent
|
||||
installation. The `tang` is an error message.
|
||||
- `%grow`/`%tomb`/`%cull`: These are used for publishing and managing
|
||||
data available for remote scries. For more information, see the
|
||||
[remote scries guide](/guides/additional/remote-scry).
|
||||
|
||||
A `note:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent).
|
||||
|
||||
---
|
||||
|
||||
### `task:agent`
|
||||
|
||||
The types of messages initiated by our agent and sent to another agent.
|
||||
|
||||
```hoon
|
||||
+$ task
|
||||
$% [%watch =path]
|
||||
@ -266,10 +343,9 @@ A `note:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent).
|
||||
==
|
||||
```
|
||||
|
||||
The types of messages initiated by our agent and sent to another agent. This is
|
||||
in contrast to [`gift:agent`](#giftagent)s, which are responses to incoming
|
||||
messages from agents or updates to agents already subscribed. The five kinds
|
||||
are:
|
||||
This is in contrast to [`gift:agent`](#giftagent)s, which are responses
|
||||
to incoming messages from agents or updates to agents already
|
||||
subscribed. The five kinds of `task:agent` are:
|
||||
|
||||
- `%watch`: Subscribe to `path` on the target ship and agent.
|
||||
- `%watch-as`: Same as `%watch`, except you ask the target's Gall to convert
|
||||
@ -285,8 +361,13 @@ are:
|
||||
|
||||
A `task:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent).
|
||||
|
||||
---
|
||||
|
||||
### `gift:agent`
|
||||
|
||||
The types of messages our agent can either send in response to messages
|
||||
from other agents, or send to subscribed agents.
|
||||
|
||||
```hoon
|
||||
+$ gift
|
||||
$% [%fact paths=(list path) =cage]
|
||||
@ -296,37 +377,46 @@ A `task:agent` is always wrapped in a `%pass` [`card:agent`](#cardagent).
|
||||
==
|
||||
```
|
||||
|
||||
The types of messages our agent can either send in response to messages from
|
||||
other agents, or send to subscribed agents. This is in contrast to
|
||||
[`task:agent`](#taskagent)s, which are messages to other agents our agent
|
||||
initiates rather than sends in response. The four kinds are:
|
||||
This is in contrast to [`task:agent`](#taskagent)s, which are messages
|
||||
to other agents our agent initiates rather than sends in response. The
|
||||
four kinds of `gift:agent` are:
|
||||
|
||||
- `%fact`: An update to existing subscribers. The `paths` field specifies which
|
||||
subscription paths the update should go out to. The `cage` is the data, and is
|
||||
a `[mark vase]`.
|
||||
- `%fact`: An update to existing subscribers. The `paths` field
|
||||
specifies which subscription paths the update should go out to. The
|
||||
`cage` is the data, and is a `[mark vase]`.
|
||||
- `%kick`: Kick subscriber, ending their subscription. The `paths` field
|
||||
specifies which paths the subscriber should be kicked from, and the `ship`
|
||||
field specifies the ship to kick. If the `ship` field is null, all subscribers
|
||||
on the specified paths are kicked. Gall will automatically remove the
|
||||
subscription from our agent's [`bitt`](#bitt) (inbound subscription `map`),
|
||||
and subscriber will no longer receive updates on the `path`s in question.
|
||||
- `%watch-ack`: Acknowledge a subscription request. If `p` is null, it's an ack
|
||||
(positive acknowledgement), and if `p` is non-null, it's a nack (negative
|
||||
acknowledgement). Simply crashing will caused Gall to nack a subscription
|
||||
request, and not crashing but not explicitly producing a `%watch-ack` `gift`
|
||||
will cause Gall to ack a subscription request. Therefore, you'd typically only
|
||||
explicitly produce a `%watch-ack` `gift` if you wanted to nack a subscription
|
||||
request with a custom error in the `tang`.
|
||||
- `%poke-ack`: Acknowledge a poke. If `p` is null, it's an ack, and if `p` is
|
||||
non-null, it's a nack. Simply crashing will cause Gall to nack a poke, and not
|
||||
crashing but not explicitly producing a `%poke-ack` `gift` will cause Gall to
|
||||
ack a poke. Therefore, you'd typically only explicitly produce a `%poke-ack`
|
||||
`gift` if you wanted to nack a poke with a custom error in the `tang`.
|
||||
specifies which paths the subscriber should be kicked from, and the
|
||||
`ship` field specifies the ship to kick. If the `ship` field is null,
|
||||
all subscribers on the specified paths are kicked. Gall will
|
||||
automatically remove the subscription from our agent's [`bitt`](#bitt)
|
||||
(inbound subscription `map`), and subscriber will no longer receive
|
||||
updates on the `path`s in question.
|
||||
- `%watch-ack`: Acknowledge a subscription request. If `p` is null, it's
|
||||
an ack (positive acknowledgement), and if `p` is non-null, it's a nack
|
||||
(negative acknowledgement). Simply crashing will caused Gall to nack a
|
||||
subscription request, and not crashing but not explicitly producing a
|
||||
`%watch-ack` `gift` will cause Gall to ack a subscription request.
|
||||
Therefore, you'd typically only explicitly produce a `%watch-ack`
|
||||
`gift` if you wanted to nack a subscription request with a custom
|
||||
error in the `tang`.
|
||||
- `%poke-ack`: Acknowledge a poke. If `p` is null, it's an ack, and if
|
||||
`p` is non-null, it's a nack. Simply crashing will cause Gall to nack
|
||||
a poke, and not crashing but not explicitly producing a `%poke-ack`
|
||||
`gift` will cause Gall to ack a poke. Therefore, you'd typically only
|
||||
explicitly produce a `%poke-ack` `gift` if you wanted to nack a poke
|
||||
with a custom error in the `tang`.
|
||||
|
||||
A `gift:agent` is always wrapped in a `%give` [`card:agent`](#cardagent).
|
||||
A `gift:agent` is always wrapped in a `%give`
|
||||
[`card:agent`](#cardagent).
|
||||
|
||||
---
|
||||
|
||||
### `sign:agent`
|
||||
|
||||
A `sign` is like a [`gift:agent`](#giftagent) but it's something that
|
||||
comes _in_ to our agent from another agent rather than something we send
|
||||
out.
|
||||
|
||||
```hoon
|
||||
+$ sign
|
||||
$% [%poke-ack p=(unit tang)]
|
||||
@ -336,31 +426,34 @@ A `gift:agent` is always wrapped in a `%give` [`card:agent`](#cardagent).
|
||||
==
|
||||
```
|
||||
|
||||
A `sign` is like a [`gift:agent`](#giftagent) but it's something that comes
|
||||
_in_ to our agent from another agent rather than something we send out. The
|
||||
possible types are:
|
||||
The possible types are:
|
||||
|
||||
- `%poke-ack`: Another agent has acked (positively acknowledged) or nacked
|
||||
(negatively acknowledged) a `%poke` [`task:agent`](#taskagent) we previously
|
||||
sent. It's an ack if `p` is null and a nack if `p` is non-null. The `tang`
|
||||
contains an error or traceback if it's a nack.
|
||||
- `%poke-ack`: Another agent has acked (positively acknowledged) or
|
||||
nacked (negatively acknowledged) a `%poke` [`task:agent`](#taskagent)
|
||||
we previously sent. It's an ack if `p` is null and a nack if `p` is
|
||||
non-null. The `tang` contains an error or traceback if it's a nack.
|
||||
- `%watch-ack`: Another agent has acked or nacked a `%watch`
|
||||
[`task:agent`](#taskagent) (subscription request) we previously sent. It's an
|
||||
ack if `p` is null and a nack if `p` is non-null. The `tang` contains an error
|
||||
or traceback if it's a nack. If it's a nack, Gall will automatically remove
|
||||
the subscription from our agent's [`boat`](#boat) (outbound subscription
|
||||
map).
|
||||
- `%fact`: An update from another agent to which we've previously subscribed
|
||||
with a `%watch` [`task:agent`](#taskagent) (subscription request). The `cage`
|
||||
contains the data, and is a `[mark vase]`.
|
||||
- `%kick`: Our subscription to another agent has been ended, and we'll no longer
|
||||
receive updates. A `%kick` may be intentional, but it may also happen due to
|
||||
certain network conditions or other factors. As a result, it's best to try and
|
||||
resubscribe with another `%watch` [`task:agent`](#taskagent), and if they
|
||||
nack the `%watch`, we can conclude it was intentional and give up.
|
||||
[`task:agent`](#taskagent) (subscription request) we previously sent.
|
||||
It's an ack if `p` is null and a nack if `p` is non-null. The `tang`
|
||||
contains an error or traceback if it's a nack. If it's a nack, Gall
|
||||
will automatically remove the subscription from our agent's
|
||||
[`boat`](#boat) (outbound subscription map).
|
||||
- `%fact`: An update from another agent to which we've previously
|
||||
subscribed with a `%watch` [`task:agent`](#taskagent) (subscription
|
||||
request). The `cage` contains the data, and is a `[mark vase]`.
|
||||
- `%kick`: Our subscription to another agent has been ended, and we'll
|
||||
no longer receive updates. A `%kick` may be intentional, but it may
|
||||
also happen due to certain network conditions or other factors. As a
|
||||
result, it's best to try and resubscribe with another `%watch`
|
||||
[`task:agent`](#taskagent), and if they nack the `%watch`, we can
|
||||
conclude it was intentional and give up.
|
||||
|
||||
---
|
||||
|
||||
### `form:agent`
|
||||
|
||||
This defines the structure of the agent itself.
|
||||
|
||||
```hoon
|
||||
++ form
|
||||
$_ ^|
|
||||
@ -406,7 +499,8 @@ possible types are:
|
||||
--
|
||||
```
|
||||
|
||||
This defines the structure of the agent itself. The agent is a door with a [`bowl`](#bowl) as its sample and exactly ten arms. Below we'll describe each arm briefly.
|
||||
The agent is a door with a [`bowl`](#bowl) as its sample and exactly ten
|
||||
arms. Below we'll describe each arm briefly.
|
||||
|
||||
#### `on-init`
|
||||
|
||||
@ -434,16 +528,16 @@ This arm is called when another agent subscribes to our agent.
|
||||
- Accepts: `path`
|
||||
- Produces: [`step:agent`](#stepagent)
|
||||
|
||||
This arm is called when another agent unsubscribes from a subscription path on
|
||||
our agent.
|
||||
This arm is called when another agent unsubscribes from a
|
||||
subscription path on our agent.
|
||||
|
||||
#### `on-peek`
|
||||
|
||||
- Accepts: `path`
|
||||
- Produces: `(unit (unit cage))`
|
||||
|
||||
This arm is called when a [scry](/reference/arvo/concepts/scry) is performed on our
|
||||
agent.
|
||||
This arm is called when a [scry](/reference/arvo/concepts/scry) is
|
||||
performed on our agent.
|
||||
|
||||
#### `on-agent`
|
||||
|
||||
@ -458,7 +552,8 @@ This arm is called when another agent give our agent a
|
||||
- Accepts: `[wire sign-arvo]`
|
||||
- Produces: [`step:agent`](#stepagent)
|
||||
|
||||
This arm is called when a vane gives our agent a `gift`. A `sign-arvo` is:
|
||||
This arm is called when a vane gives our agent a `gift`. A `sign-arvo`
|
||||
is:
|
||||
|
||||
```hoon
|
||||
+$ sign-arvo
|
||||
@ -481,13 +576,15 @@ This arm is called when a vane gives our agent a `gift`. A `sign-arvo` is:
|
||||
==
|
||||
```
|
||||
|
||||
You can refer to the `/sys/lull.hoon` source code, or the API Reference of each
|
||||
vane in the [Arvo documentation](/reference/arvo/overview).
|
||||
You can refer to the `/sys/lull.hoon` source code, or the API Reference
|
||||
of each vane in the [Arvo documentation](/reference/arvo/overview).
|
||||
|
||||
#### `on-fail`
|
||||
|
||||
- Accepts: `[term tang]`
|
||||
- Produces: [`step:agent`](#stepagent)
|
||||
|
||||
This arm is called if certain errors occur in Gall, such as if our agent tries
|
||||
to create a duplicate subscription.
|
||||
This arm is called if certain errors occur in Gall, such as if our agent
|
||||
tries to create a duplicate subscription.
|
||||
|
||||
---
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -59,10 +59,11 @@ definable in a regular recursive data type).
|
||||
|
||||
## Specification
|
||||
|
||||
An agent is defined as a [core](/reference/glossary/core/) with a set of [arms](/reference/glossary/arm/) to handle various
|
||||
events. These handlers usually produce a list of effects and the next
|
||||
state of the agent. The interface definition can be found in
|
||||
`sys/lull.hoon`, which at the time of writing is:
|
||||
An agent is defined as a [core](/reference/glossary/core/) with a set of
|
||||
[arms](/reference/glossary/arm/) to handle various events. These
|
||||
handlers usually produce a list of effects and the next state of the
|
||||
agent. The interface definition can be found in `sys/lull.hoon`, which
|
||||
at the time of writing is:
|
||||
|
||||
```hoon
|
||||
++ agent
|
||||
@ -71,8 +72,13 @@ state of the agent. The interface definition can be found in
|
||||
+$ step (quip card form)
|
||||
+$ card (wind note gift)
|
||||
+$ note
|
||||
$% [%arvo =note-arvo]
|
||||
[%agent [=ship name=term] =task]
|
||||
$% [%agent [=ship name=term] =task]
|
||||
[%arvo note-arvo]
|
||||
[%pyre =tang]
|
||||
::
|
||||
[%grow =spur =page]
|
||||
[%tomb =case =spur]
|
||||
[%cull =case =spur]
|
||||
==
|
||||
+$ task
|
||||
$% [%watch =path]
|
||||
@ -82,8 +88,8 @@ state of the agent. The interface definition can be found in
|
||||
[%poke-as =mark =cage]
|
||||
==
|
||||
+$ gift
|
||||
$% [%fact path=(unit path) =cage]
|
||||
[%kick path=(unit path) ship=(unit ship)]
|
||||
$% [%fact paths=(list path) =cage]
|
||||
[%kick paths=(list path) ship=(unit ship)]
|
||||
[%watch-ack p=(unit tang)]
|
||||
[%poke-ack p=(unit tang)]
|
||||
==
|
||||
@ -134,6 +140,7 @@ state of the agent. The interface definition can be found in
|
||||
|~ [term tang]
|
||||
*(quip card _^|(..on-init))
|
||||
--
|
||||
--
|
||||
```
|
||||
|
||||
Here's a skeleton example of an implementation:
|
||||
|
252
content/reference/arvo/gall/scry.md
Normal file
252
content/reference/arvo/gall/scry.md
Normal file
@ -0,0 +1,252 @@
|
||||
+++
|
||||
title = "Scry Reference"
|
||||
weight = 4
|
||||
+++
|
||||
|
||||
Gall's scry interface is mainly used to scry into the individual agents it's
|
||||
running. The vane itself does have its own interface, however. Both [agent
|
||||
scries](#agent-scries) and [vane scries](#vane-scries) are documented below.
|
||||
|
||||
Note that for all agent scries and most vane scries, `q.beak`, where there'd
|
||||
usually be a `desk`, will be the agent name instead, like:
|
||||
|
||||
```
|
||||
.^(some-type %gx /=agent-name-here=/some/path/noun)
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
|
||||
**Important:** Vane scries are differentiated from agent scries by an extra
|
||||
empty (`%$`) element at the beginning of the `spur` (the path after the
|
||||
`beak`), like: `/=agent-name/$` or `/=agent-name=//some/more/fields`. Without
|
||||
that empty element, Gall will try route the scry to an agent instead.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Agent scries
|
||||
|
||||
In order to hit the `+on-peek` arm of a Gall agent, you need to:
|
||||
|
||||
1. Put the agent in `q.beak` of the scry path (where the `desk` usually goes), like `/=some-agent=`.
|
||||
2. Make sure the beginning of the `spur` is *not* an empty `%$` element, as
|
||||
that will route the scry to the [vane endpoints](#vane-scries) instead. An
|
||||
agent scry must be `/=some-agent=/some/path` not `/=some-agent=//some/path`.
|
||||
|
||||
Any `care` can be used (dependent on what the agent accepts, of course). The
|
||||
most common is `%x`.
|
||||
|
||||
{% callout %}
|
||||
|
||||
Note that `%x` cares alone must include an extra `mark` field at the end of the
|
||||
`spur`. This mark field lets Gall perform any necessary mark conversions before
|
||||
returning your data. For plain unmarked data, you can just use the `%noun`
|
||||
mark. As an example, if an agent specifies an endpoint `/x/some/path` and just
|
||||
returns ordinary data, you'd do `.^(some-type %gx
|
||||
/=some-agent=/some/path/noun)`. If the endpoint returns a `%json` mark or
|
||||
whatever (and that's what you want), you'd put that at the end instead.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
---
|
||||
|
||||
## Vane scries
|
||||
|
||||
Gall itself provides the special vane-level endpoints listed below. They are
|
||||
organized by the `care`. In order to hit the vane-level endpoints, the
|
||||
beginning of the the `spur` (e.g. the `path` after the `beak`) *must* be a `%$`
|
||||
empty element. For example:
|
||||
|
||||
```hoon
|
||||
.^(desk %gd /=acme=/$)
|
||||
.^((set [=dude:gall live=?]) %ge /=base=/$)
|
||||
.^((list path) %gt /=acme=//foo)
|
||||
```
|
||||
|
||||
Note you can use `$` to make the last element empty since it won't allow a
|
||||
trailing `/`. Note how in the third example, the empty element is at the
|
||||
*beginning* of the `spur` and *after* the `beak`. If you fail to include this
|
||||
empty element, Gall will try route the scry to an agent for handling instead.
|
||||
|
||||
### `%d`: get desk of app
|
||||
|
||||
A scry with a `%d` care and an agent in `q.beak` will
|
||||
give you the desk that agent is on.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `desk`
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
> .^(desk %gd /=acme=/$)
|
||||
%base
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `%e`: running apps
|
||||
|
||||
A scry with an `%e` care will give you the agents on the desk given in
|
||||
`q.beak`.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `(set [=dude live=?])`, where `live` is true if running and false if not.
|
||||
|
||||
#### Examples
|
||||
|
||||
```
|
||||
> .^((set [=dude:gall live=?]) %ge /=base=/$)
|
||||
{ [dude=%acme live=%.y]
|
||||
[dude=%hood live=%.y]
|
||||
[dude=%lens live=%.y]
|
||||
[dude=%dbug live=%.y]
|
||||
[dude=%azimuth live=%.y]
|
||||
[dude=%ping live=%.y]
|
||||
[dude=%dojo live=%.y]
|
||||
[dude=%eth-watcher live=%.y]
|
||||
[dude=%spider live=%.y]
|
||||
[dude=%herm live=%.y]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `%f`: nonces of apps
|
||||
|
||||
A scry with a care of `%f` and anything in `q.beak` will produce the
|
||||
subscription nonces of all apps. You are unlikely to use this, it's
|
||||
mostly for kernel debugging.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `(map dude @)` where the `@` is the nonce.
|
||||
|
||||
#### Examples
|
||||
|
||||
```
|
||||
> .^((map dude:gall @) %gf /=//=/$)
|
||||
[ n=[p=%treaty q=2]
|
||||
l
|
||||
[ n=[p=%metadata-hook q=1]
|
||||
l={[p=%contacts q=1] [p=%notify q=2] [p=%groups q=1] [p=%dm-hook q=1] [p=%spider q=1]}
|
||||
r
|
||||
{ [p=%docket q=9]
|
||||
[p=%bait q=1]
|
||||
[p=%hood q=15]
|
||||
[p=%hark-graph-hook q=2]
|
||||
[p=%s3-store q=1]
|
||||
[p=%hark-system-hook q=1]
|
||||
......(truncated for brevity)......
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `%n`: get nonce of subscription
|
||||
|
||||
A scry with a care of `%n`, an agent in `q.beak` and a path of
|
||||
`//[ship]/[agent]/[wire]` will produce the nonce for that subscription.
|
||||
You are unlikely to use this, it's mostly for kernel debugging.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `@`.
|
||||
|
||||
---
|
||||
|
||||
### `%t`: remote scry subpaths
|
||||
|
||||
A scry with a `%t` care, an agent in `q.beak` and a path of `//some/path`
|
||||
will give you the list of remote scry subpaths bound under the given
|
||||
path.
|
||||
|
||||
See the [remote scry guide](/guides/additional/remote-scry) for more
|
||||
details.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `(list path)`
|
||||
|
||||
#### Examples
|
||||
|
||||
```
|
||||
> .^((list path) %gt /=acme=//foo)
|
||||
~
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `%u`: check if installed
|
||||
|
||||
A scry with a `%u` care will check whether the given agent is installed and
|
||||
running.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `?`
|
||||
|
||||
#### Examples
|
||||
|
||||
|
||||
```
|
||||
> .^(? %gu /=acme=/$)
|
||||
%.y
|
||||
```
|
||||
|
||||
```
|
||||
> .^(? %gu /=doesnt-exist=/$)
|
||||
%.n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `%w`: latest revision of path
|
||||
|
||||
A scry with a `%w` care and an agent in `q.beak` will get the latest revision
|
||||
number of the bound remote scry path given in the `spur`.
|
||||
|
||||
See the [remote scry guide](/guides/additional/remote-scry) for more
|
||||
details.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `cass:clay`, specifically the `%ud` kind.
|
||||
|
||||
---
|
||||
|
||||
### `%x`: remote scry file
|
||||
|
||||
A scry with a `%x` care and an agent in `q.beak` will get the value
|
||||
bound at the remote scry path given in the `spur`. The revision of the
|
||||
file must be given in the `beak` portion of the scry path. The general
|
||||
format is therefore:
|
||||
|
||||
```hoon
|
||||
[%gx /[ship]/[agent]/[file revision]//[file path]
|
||||
```
|
||||
|
||||
See the [remote scry guide](/guides/additional/remote-scry) for more
|
||||
details.
|
||||
|
||||
#### Produces
|
||||
|
||||
The type returned is an `(each page @uvI)`. If the file has been
|
||||
tombstoned, it'll be the `@uvI` hash of the file. Otherwise, it will be
|
||||
the data as a `page` (a pair of `mark` and `noun`).
|
||||
|
||||
---
|
||||
|
||||
### `%z`: hash of value at path
|
||||
|
||||
A scry with a `%z` care and an agent in `q.beak` will get the hash identifier
|
||||
of the value bound at the remote scry path given in the `spur`.
|
||||
|
||||
See the [remote scry guide](/guides/additional/remote-scry) for more
|
||||
details.
|
||||
|
||||
#### Produces
|
||||
|
||||
A `@uvI`.
|
||||
|
||||
---
|
@ -1,86 +0,0 @@
|
||||
+++
|
||||
title = "Threads Reference"
|
||||
weight = 50
|
||||
+++
|
||||
|
||||
## Start thread
|
||||
|
||||
Poke `spider` with mark `%spider-start` and a vase containing `start-args`:
|
||||
|
||||
```hoon
|
||||
+$ start-args
|
||||
[parent=(unit tid) use=(unit tid) =beak file=term =vase]
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- `parent` - optional `tid` of parent thread if the thread is a child. If specified, the child thread will be killed with the parent thread ends.
|
||||
- `use` - `tid` (thread ID) to give the new thread. Can be generated with something like `(scot %ta (cat 3 'my-agent_' (scot %uv (sham eny))))`. However you do it, make sure it's unique.
|
||||
- `beak` - A `$beak` is a triple of `[p=ship q=desk r=case]`. `p` is always our ship, `q` is the desk which contains the thread we want to run. `r` is a `case`, which specifies a desk revision and is a tagged union of:
|
||||
```hoon
|
||||
+$ case
|
||||
$% [%da p=@da] :: date
|
||||
[%tas p=@tas] :: label
|
||||
[%ud p=@ud] :: number
|
||||
==
|
||||
```
|
||||
You'll almost always just want the current revision, so you can specify the `case` as `da+now.bowl`. If the thread is on the same desk as the agent you can also just use `byk.bowl(r da+now)` for the `beak`.
|
||||
- `file` - name of the thread file in `/ted`. For example, if the thread you want to start is `/ted/foo/hoon` you'd specify `%foo`.
|
||||
- `vase` - `vase` to be given to the thread when it's started. Can be whatever or just `!>(~)` if it doesn't need any args.
|
||||
|
||||
#### Example
|
||||
|
||||
```hoon
|
||||
[%pass /some-path %agent [our.bowl %spider] %poke %spider-start !>([~ `tid byk.bowl %foo !>(~)])]
|
||||
```
|
||||
|
||||
## Stop thread
|
||||
|
||||
Poke `spider` with mark `%spider-stop` and a vase containing `[tid ?]`, where:
|
||||
|
||||
- `tid` - the `tid` of the thread you want to stop
|
||||
- `?` - whether thread should end nicely. If `%.y` it'll end with mark `%thread-done` and the bunt value of a vase. If `%.n` it'll end with mark `%thread-fail` and a `[term tang]` where `term` is `%cancelled` and `tang` is `~`.
|
||||
|
||||
#### Example
|
||||
|
||||
```hoon
|
||||
[%pass /some-path %agent [our.bowl %spider] %poke %spider-stop !>([tid %.y)]
|
||||
```
|
||||
|
||||
## Subscribe for result
|
||||
|
||||
Spider will send the result on `/thread-result/[tid]` so you can subscribe there for the result. You should subscribe before starting the thread.
|
||||
|
||||
The result will have a mark of either `%thread-fail` or `%thread-done`.
|
||||
|
||||
- `%thread-fail` - has a vase containing a `[term tang]` where `term` is an error message and `tang` is a traceback.
|
||||
- `%thread-done` - has a vase of the result of the thread.
|
||||
|
||||
#### Example
|
||||
|
||||
```hoon
|
||||
[%pass /some-path %agent [our.bowl %spider] %watch /thread-result/[tid]]
|
||||
```
|
||||
|
||||
## Subscribe to thread
|
||||
|
||||
You can subscribe to a thread on `/thread/[tid]/path`. Note this is for facts sent off by the thread while it's running, not the final result. The path depends on the particular thread.
|
||||
|
||||
#### Example
|
||||
|
||||
```hoon
|
||||
[%pass /some-path %agent [our.bowl %spider] %watch /thread/[tid]/thread-path]
|
||||
```
|
||||
|
||||
## Poke thread
|
||||
|
||||
To poke a thread you poke spider with a mark of `%spider-input` and a vase of `[tid cage]`.
|
||||
|
||||
- `tid` is the tid of the thread you want to poke
|
||||
- `cage` is whatever mark and vase you want to poke it with
|
||||
|
||||
#### Example
|
||||
|
||||
```hoon
|
||||
[%pass /some-path %agent [our.bowl %spider] %poke %spider-input !>([tid %foo !>('foooo')])]
|
||||
```
|
@ -27,6 +27,27 @@ A `%code` scry gets the current web login code. It takes your ship name as its `
|
||||
~lidlut-tabwed-pillex-ridrup
|
||||
```
|
||||
|
||||
## `%fake`
|
||||
|
||||
A `%fake` scry checks whether the current ship is fake (a development ship
|
||||
booted with the `-F` option). The type returned is a `?`.
|
||||
|
||||
#### Example
|
||||
|
||||
On a fake `~zod`:
|
||||
|
||||
```
|
||||
> .^(? %j /=fake=)
|
||||
%.y
|
||||
```
|
||||
|
||||
On a real planet:
|
||||
|
||||
```
|
||||
> .^(? %j /=fake=)
|
||||
%.n
|
||||
```
|
||||
|
||||
## `%life`
|
||||
|
||||
A `%life` scry gets the current `life` (key revision number) of a ship if known, otherwise it crashes. It takes a ship as its `path` and the type returned is a `@ud`.
|
||||
|
@ -3,222 +3,451 @@ title = "API Reference"
|
||||
weight = 2
|
||||
+++
|
||||
|
||||
This document details all the `task`s you may wish to send Jael, as well as the `gift`s you'll receive in response.
|
||||
This document details all the tasks you may wish to send Jael, as well
|
||||
as the gifts you'll receive in response.
|
||||
|
||||
You may also wish to reference the [Data Types](/reference/arvo/jael/data-types) document for details of the types referenced here, and the [Examples](/reference/arvo/jael/examples) document for practical examples of using these `task`s.
|
||||
You may also wish to reference the [Data
|
||||
Types](/reference/arvo/jael/data-types) document for details of the
|
||||
types referenced here, and the [Examples](/reference/arvo/jael/examples)
|
||||
document for practical examples of using these tasks.
|
||||
|
||||
## `%dawn`
|
||||
## Tasks
|
||||
|
||||
### `%dawn`
|
||||
|
||||
Boot from keys.
|
||||
|
||||
```hoon
|
||||
[%dawn dawn-event]
|
||||
```
|
||||
|
||||
Boot from keys.
|
||||
This task is called once per ship during the vane initialization phase
|
||||
immediately following the beginning of the [adult
|
||||
stage](/reference/arvo/overview#structural-interface-core). This task
|
||||
is `%pass`ed to Jael by Dill, as Dill is the first vane to be loaded for
|
||||
technical reasons, though we consider Jael to be the true "first" vane.
|
||||
This task is only used for ships that will join the Ames network -
|
||||
fake ships (i.e. made with `./urbit -F zod`) use the [%fake](#fake)
|
||||
task instead.
|
||||
|
||||
This `task` is called once per ship during the vane initialization phase immediately following the beginning of the [adult stage](/reference/arvo/overview#structural-interface-core). This `task` is `%pass`ed to Jael by Dill, as Dill is the first vane to be loaded for technical reasons, though we consider Jael to be the true "first" vane. This `task` is only used for ships that will join the Ames network - fake ships (i.e. made with `./urbit -F zod`) use the [%fake](#fake) `task` instead.
|
||||
|
||||
`%dawn` is used to perform a sequence of initialization tasks related to saving information about Azimuth and the Ames network and booting other vanes for the first time. Upon receipt of a `%dawn` `task`, Jael will:
|
||||
`%dawn` is used to perform a sequence of initialization tasks related to
|
||||
saving information about Azimuth and the Ames network and booting other
|
||||
vanes for the first time. Upon receipt of a `%dawn` task, Jael will:
|
||||
|
||||
- record the Ethereum block the public key is registered to,
|
||||
- record the URL of the Ethereum node used,
|
||||
- save the signature of the parent planet (if the ship is a moon),
|
||||
- load the initial public and private keys for the ship,
|
||||
- set the DNS suffix(es) used by the network (currently just `urbit.org`),
|
||||
- set the DNS suffix(es) used by the network (currently just
|
||||
`urbit.org`),
|
||||
- save the public keys of all galaxies,
|
||||
- set Jael to subscribe to `%azimuth-tracker`,
|
||||
- `%slip` a `%init` `task` to Ames, Clay, Gall, Dill, and Eyre, and `%give` an `%init`
|
||||
`gift` to Arvo, which then informs Unix that the initialization process has concluded.
|
||||
- `%slip` a `%init` task to Ames, Clay, Gall, Dill, and Eyre, and
|
||||
`%give` an `%init` gift to Arvo, which then informs Unix that the
|
||||
initialization process has concluded.
|
||||
|
||||
This `task` takes a [$dawn-event](/reference/arvo/jael/data-types#dawn-event) as its argument.
|
||||
This task takes a
|
||||
[$dawn-event](/reference/arvo/jael/data-types#dawn-event) as its
|
||||
argument.
|
||||
|
||||
You would not use this `task` manually.
|
||||
You would not use this task manually.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael `%give`s an `%init` `gift` to Unix. This occurs after the Dill `%slip` init.
|
||||
Jael `%give`s an `%init` gift to Unix. This occurs after the Dill
|
||||
`%slip` init.
|
||||
|
||||
## `%fake`
|
||||
---
|
||||
|
||||
### `%fake`
|
||||
|
||||
Boot fake ship.
|
||||
|
||||
```hoon
|
||||
[%fake =ship]
|
||||
```
|
||||
|
||||
Boot fake ship.
|
||||
This task is used instead of [%dawn](#dawn) when creating a fake ship
|
||||
via the `-F` flag when calling the Urbit binary. It performs a subset of
|
||||
the actions that `%dawn` performs, modified to accommodate the fake
|
||||
ship.
|
||||
|
||||
This `task` is used instead of [%dawn](#dawn) when creating a fake ship via the `-F` flag when calling the Urbit binary. It performs a subset of the actions that `%dawn` performs, modified to accommodate the fake ship.
|
||||
|
||||
`%fake` endows the ship with a private key and a public key deterministically derived from the ship's `@p`. It sets `fak.own.pki` to `%.y`, which is the bit that determines whether or not a ship is fake. Other parts of the Jael state, such as the sponsorship chain and galaxy public keys are left at their bunted values.
|
||||
`%fake` endows the ship with a private key and a public key
|
||||
deterministically derived from the ship's `@p`. It sets `fak.own.pki` to
|
||||
`%.y`, which is the bit that determines whether or not a ship is fake.
|
||||
Other parts of the Jael state, such as the sponsorship chain and galaxy
|
||||
public keys are left at their bunted values.
|
||||
|
||||
The `ship` field specifies the `@p` of the fake ship being created.
|
||||
|
||||
You would not use this `task` manually.
|
||||
You would not use this task manually.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael `%give`s a `%init` `gift` to Unix.
|
||||
Jael `%give`s a `%init` gift to Unix.
|
||||
|
||||
## `%listen`
|
||||
---
|
||||
|
||||
### `%listen`
|
||||
|
||||
Set Ethereum source.
|
||||
|
||||
```hoon
|
||||
[%listen whos=(set ship) =source]
|
||||
```
|
||||
|
||||
Set Ethereum source.
|
||||
Sets the source that the public keys for a set of `ship`s should be
|
||||
obtained from. This can either be a Gall app that communicates with an
|
||||
Ethereum node such as `%azimuth-tracker`, as in the case of galaxies,
|
||||
stars, and planets, or a ship, as in the case of moons.
|
||||
|
||||
Sets the source that the public keys for a set of `ship`s should be obtained from. This can either be a Gall app that communicates with an Ethereum node such as `%azimuth-tracker`, as in the case of galaxies, stars, and planets, or a ship, as in the case of moons.
|
||||
`whos` is the set of ships whose key data source is to be monitored. The
|
||||
[$source](/reference/arvo/jael/data-types#source) is either a ship or
|
||||
the name of a Gall app to use as a source. A `%listen` task with empty
|
||||
`whos` will set the default source. When the `source` is a ship, Jael
|
||||
will obtain public keys for ships in `(set ship)` from the given ship.
|
||||
By default, the `source` for a moon will be the planet that spawned that
|
||||
moon.
|
||||
|
||||
`whos` is the set of ships whose key data source is to be monitored. The [$source](/reference/arvo/jael/data-types#source) is either a ship or the name of a Gall app to use as a source. A `%listen` `task` with empty `whos` will set the default source. When the `source` is a ship, Jael will obtain public keys for ships in `(set ship)` from the given ship. By default, the `source` for a moon will be the planet that spawned that moon.
|
||||
|
||||
You are unlikely to use this `task` manually.
|
||||
You are unlikely to use this task manually.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael will not return any `gift`s in response to a `%listen` `task`.
|
||||
Jael will not return any gifts in response to a `%listen` task.
|
||||
|
||||
## `%meet`
|
||||
---
|
||||
|
||||
### `%meet`
|
||||
|
||||
This task is deprecated and does not perform any actions.
|
||||
|
||||
```hoon
|
||||
[%meet =ship =life =pass]
|
||||
```
|
||||
|
||||
This `task` is deprecated and does not perform any actions.
|
||||
---
|
||||
|
||||
## `%moon`
|
||||
### `%moon`
|
||||
|
||||
Register moon keys or otherwise administer a moon.
|
||||
|
||||
```hoon
|
||||
[%moon =ship =udiff:point]
|
||||
```
|
||||
|
||||
Register moon keys or otherwise administer a moon.
|
||||
|
||||
This is what is sent to Jael by `%hood` behind the scenes when you run `|moon`, `|moon-breach` or `|moon-cycle-keys`. The `ship` field is the moon's `@p`. The [$udiff:point](/reference/arvo/jael/data-types#udiffpoint) will contain the bunt of an [$id:block](/reference/arvo/jael/data-types#idblock) (since moons aren't registered in Azimuth) and one of the `udiff` actions depending on what you want to do.
|
||||
This is what is sent to Jael by `%hood` behind the scenes when you run
|
||||
`|moon`, `|moon-breach` or `|moon-cycle-keys`. The `ship` field is the
|
||||
moon's `@p`. The
|
||||
[$udiff:point](/reference/arvo/jael/data-types#udiffpoint) will contain
|
||||
the bunt of an [$id:block](/reference/arvo/jael/data-types#idblock)
|
||||
(since moons aren't registered in Azimuth) and one of the `udiff`
|
||||
actions depending on what you want to do.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael does not return any `gift`s in response to a `%moon` `task`.
|
||||
Jael does not return any gifts in response to a `%moon` task.
|
||||
|
||||
## `%nuke`
|
||||
---
|
||||
|
||||
### `%nuke`
|
||||
|
||||
Cancel subscription to public or private key updates.
|
||||
|
||||
```hoon
|
||||
[%nuke whos=(set ship)]
|
||||
```
|
||||
|
||||
Cancel subscription to public or private key updates.
|
||||
If you've subscribed to public or private key updates from Jael with a
|
||||
[%private-keys](#private-keys) or [%public-keys](#public-keys) task,
|
||||
you can unsubscribe and stop receiving updates with a `%nuke` task.
|
||||
The `(set ship)` is the `set` of `ship`s which you want to stop
|
||||
tracking. Jael organises subscriptions based on `duct`s, and will
|
||||
determine which subscription to cancel implicitly based on the `duct`
|
||||
the `%nuke` task came from. This means a `%nuke` task only works
|
||||
from the same thread or agent and on the same `path` as the original
|
||||
subscription request.
|
||||
|
||||
If you've subscribed to public or private key updates from Jael with a [%private-keys](#private-keys) or [%public-keys](#public-keys) `task`, you can unsubscribe and stop receiving updates with a `%nuke` `task`. The `(set ship)` is the `set` of `ship`s which you want to stop tracking. Jael organises subscriptions based on `duct`s, and will determine which subscription to cancel implicitly based on the `duct` the `%nuke` `task` came from. This means a `%nuke` `task` only works from the same thread or agent and on the same `path` as the original subscription request.
|
||||
|
||||
To cancel a subscription to the ship's private keys you must leave `whos` empty like `[%nuke ~]`.
|
||||
To cancel a subscription to the ship's private keys you must leave
|
||||
`whos` empty like `[%nuke ~]`.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael does not return a `gift` in response to a `%nuke` `task`.
|
||||
Jael does not return a gift in response to a `%nuke` task.
|
||||
|
||||
#### Examples
|
||||
|
||||
See the [%public-keys and %nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of the Examples document for an example of using `%nuke` to cancel a `%public-keys` subscription. See the thread in the [%private-keys](/reference/arvo/jael/examples#private-keys) example for cancelling a `%private-keys` subscription.
|
||||
See the [%public-keys and
|
||||
%nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of
|
||||
the Examples document for an example of using `%nuke` to cancel a
|
||||
`%public-keys` subscription. See the thread in the
|
||||
[%private-keys](/reference/arvo/jael/examples#private-keys) example for
|
||||
cancelling a `%private-keys` subscription.
|
||||
|
||||
## `%private-keys`
|
||||
---
|
||||
|
||||
### `%private-keys`
|
||||
|
||||
Subscribe to private key updates.
|
||||
|
||||
```hoon
|
||||
[%private-keys ~]
|
||||
```
|
||||
|
||||
Subscribe to private key updates.
|
||||
|
||||
Subscribe to be notified of private key updates for the local ship. The subscription will continue until Jael receives a [%nuke](#nuke) `task` to cancel it.
|
||||
Subscribe to be notified of private key updates for the local ship. The
|
||||
subscription will continue until Jael receives a [%nuke](#nuke) task
|
||||
to cancel it.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael responds to a `%private-keys` `task` with a `%private-keys` `gift` which looks like:
|
||||
Jael responds to a `%private-keys` task with a [`%private-keys`
|
||||
gift](#private-keys-1).
|
||||
|
||||
```hoon
|
||||
[%private-keys =life vein=(map life ring)]
|
||||
```
|
||||
|
||||
The `life` is the current life of the ship and the `vein` `map` contains the private key for each life up to (and including) the current one. Upon first subscribing, Jael will immediately respond with a `%private-keys` `gift`. Then, whenever the ship's private keys are changed, it'll send a new and updated `%private-keys` `gift`.
|
||||
Jael will immediately respond with a `%private-keys` gift. Then,
|
||||
whenever the ship's private keys are changed, it'll send a new and
|
||||
updated `%private-keys` gift.
|
||||
|
||||
#### Example
|
||||
|
||||
See the [%private-keys](/reference/arvo/jael/examples#private-keys) section of the Examples document for a practical example.
|
||||
See the [%private-keys](/reference/arvo/jael/examples#private-keys)
|
||||
section of the Examples document for a practical example.
|
||||
|
||||
## `%public-keys`
|
||||
---
|
||||
|
||||
### `%public-keys`
|
||||
|
||||
Subscribe to public key (and related) updates from Jael.
|
||||
|
||||
```hoon
|
||||
[%public-keys ships=(set ship)]
|
||||
```
|
||||
|
||||
Subscribe to public key (and related) updates from Jael.
|
||||
|
||||
An agent or thread can subscribe to be notified of public key updates, sponsorship changes and continuity breaches for the `set` of `ship`s specified in the `ships` field. The subscription will continue until Jael receives a [%nuke](#nuke) `task` to cancel it.
|
||||
An agent or thread can subscribe to be notified of public key updates,
|
||||
sponsorship changes and continuity breaches for the `set` of `ship`s
|
||||
specified in the `ships` field. The subscription will continue until
|
||||
Jael receives a [%nuke](#nuke) task to cancel it.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael responds to a `%public-keys` `task` with `%public-keys` `gift`s which look like:
|
||||
Jael responds to a `%public-keys` task with [`%public-keys`
|
||||
gift](#public-keys-1).
|
||||
|
||||
```hoon
|
||||
[%public-keys =public-keys-result]
|
||||
```
|
||||
|
||||
The [$public-keys-result](/reference/arvo/jael/data-types#public-keys-result) contains whatever changes have occurred.
|
||||
|
||||
Upon subscription, Jael will immeditely respond with a `%public-keys` `gift` containing a `%full` `public-keys-result` with the public key for each `life` up until the current one for each `ship` specified in the original `task`. After than, Jael will send a `%public-keys` `gift` with either a `%diff` or `%breach` `public-keys-result` each time a change occurs for any of the `ship`s to which you're subscribed.
|
||||
Upon subscription, Jael will immeditely respond with a `%public-keys`
|
||||
gift containing a `%full` `public-keys-result` with the public key for
|
||||
each `life` up until the current one for each `ship` specified in the
|
||||
original task. After than, Jael will send a `%public-keys` gift with
|
||||
either a `%diff` or `%breach`
|
||||
[`$public-keys-result`](/reference/arvo/jael/data-types#public-keys-result)
|
||||
each time a change occurs for any of the `ship`s to which you're
|
||||
subscribed.
|
||||
|
||||
#### Example
|
||||
|
||||
See the [%public-keys and %nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of the Examples document for a practical example.
|
||||
See the [%public-keys and
|
||||
%nuke](/reference/arvo/jael/examples#public-keys-and-nuke) section of
|
||||
the Examples document for a practical example.
|
||||
|
||||
## `%rekey`
|
||||
---
|
||||
|
||||
### `%rekey`
|
||||
|
||||
Update private keys.
|
||||
|
||||
```hoon
|
||||
[%rekey =life =ring]
|
||||
```
|
||||
|
||||
Update private keys.
|
||||
|
||||
This is what is sent to Jael by `%hood` when you run `|rekey`, as you must after setting new Azimuth keys or running `|cycle-moon-keys` on a moon's parent. It will update your `life` (key revision number) and private keys. The `life` field is the new `life` (typically an increment of the current `life`) and the `ring` is a private key `@`.
|
||||
This is what is sent to Jael by `%hood` when you run `|rekey`, as you
|
||||
must after setting new Azimuth keys or running `|cycle-moon-keys` on a
|
||||
moon's parent. It will update your `life` (key revision number) and
|
||||
private keys. The `life` field is the new `life` (typically an increment
|
||||
of the current `life`) and the `ring` is a private key `@`.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael does not return any `gift` in response to a `%rekey` `task`.
|
||||
Jael does not return any gift in response to a `%rekey` task.
|
||||
|
||||
## `%turf`
|
||||
---
|
||||
|
||||
### `%resend`
|
||||
|
||||
Resend private keys.
|
||||
|
||||
```hoon
|
||||
[%resend ~]
|
||||
```
|
||||
|
||||
This task asks Jael to resend our private keys to subscribers who have
|
||||
subscribed with a [`%private-keys` task](#private-keys).
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael doesn't return any gifts in response to a `%rekey` task, but
|
||||
`%private-keys` subscribers will receive a [`%private-keys`
|
||||
gift](#private-keys-1).
|
||||
|
||||
---
|
||||
|
||||
### `%ruin`
|
||||
|
||||
Pretend breach.
|
||||
|
||||
```hoon
|
||||
[%ruin ships=(set ship)]
|
||||
```
|
||||
|
||||
This simulates a breach locally for the given `set` of `ship`s. Jael
|
||||
will blast out a `%breach` [`%public-keys` gift](#public-keys-1) to all
|
||||
subscribers. Ames will delete all message state for the ships in
|
||||
question in response to the `%breach` gift.
|
||||
|
||||
{% callout %}
|
||||
|
||||
**WARNING**
|
||||
|
||||
This will break communications with the given ships, and is not
|
||||
reversible until they actually breach. **Use with extreme caution.**
|
||||
|
||||
Note it's better to use the [`%snub` Ames
|
||||
task](/reference/arvo/ames/tasks#snub) if you want to block packets from
|
||||
ships.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael doesn't return any gifts in response to a `%ruin` task.
|
||||
|
||||
---
|
||||
|
||||
### `%turf`
|
||||
|
||||
View domains.
|
||||
|
||||
```hoon
|
||||
[%turf ~]
|
||||
```
|
||||
|
||||
View domains.
|
||||
|
||||
The domains returned by a `%turf` `task` are used as the base for individual galaxy domain names (e.g. from `urbit.org` you get `zod.urbit.org`, `bus.urbit.org`, etc). Jael gets these from Azimuth, then Ames gets them from Jael and passes them to the runtime, which will perform the DNS lookups and give Ames back the galaxy IP addresses. A `%turf` task takes no additional arguments. You're unlikely to use this manually - if you want the current `turf`s you'd likely want to do a [turf scry](/reference/arvo/jael/scry#turf) instead.
|
||||
The domains returned by a `%turf` task are used as the base for
|
||||
individual galaxy domain names (e.g. from `urbit.org` you get
|
||||
`zod.urbit.org`, `bus.urbit.org`, etc). Jael gets these from Azimuth,
|
||||
then Ames gets them from Jael and passes them to the runtime, which will
|
||||
perform the DNS lookups and give Ames back the galaxy IP addresses. A
|
||||
`%turf` task takes no additional arguments. You're unlikely to use this
|
||||
manually - if you want the current `turf`s you'd likely want to do a
|
||||
[turf scry](/reference/arvo/jael/scry#turf) instead.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael will respond to a `%turf` `task` with a `%turf` `gift`, which looks like:
|
||||
|
||||
```hoon
|
||||
[%turf turf=(list turf)]
|
||||
```
|
||||
|
||||
The `turf` in the `(list turf)` is a domain as a `(list @t)`, TLD-first. The current default is `[['org' 'urbit' ~] ~]`.
|
||||
Jael will respond to a `%turf` task with a [`%turf` gift](#turf-1).
|
||||
|
||||
#### Example
|
||||
|
||||
See the [%turf section of the Examples document](/reference/arvo/jael/examples#turf) for a practical example.
|
||||
See the [%turf section of the Examples
|
||||
document](/reference/arvo/jael/examples#turf) for a practical example.
|
||||
|
||||
## `%step`
|
||||
---
|
||||
|
||||
### `%step`
|
||||
|
||||
Reset web login code.
|
||||
|
||||
```hoon
|
||||
[%step ~]
|
||||
```
|
||||
|
||||
Reset web login code.
|
||||
|
||||
Jael maintains a `step` value that represents the web login code revision number, and uses it to derive the code itself. It begins at `0` and is incremented each time the code is changed. When Jael updates the web login code, it sends Eyre a `%code-changed` `task:eyre` so that Eyre can throw away all of its current cookies and sessions. A `%step` task takes no additional argument.
|
||||
Jael maintains a `step` value that represents the web login code
|
||||
revision number, and uses it to derive the code itself. It begins at `0`
|
||||
and is incremented each time the code is changed. When Jael updates the
|
||||
web login code, it sends Eyre a `%code-changed` `task:eyre` so that Eyre
|
||||
can throw away all of its current cookies and sessions. A `%step` task
|
||||
takes no additional argument.
|
||||
|
||||
#### Returns
|
||||
|
||||
Jael does not return a `gift` in response to a `%step` `task`.
|
||||
Jael does not return a gift in response to a `%step` task.
|
||||
|
||||
#### Example
|
||||
|
||||
See the [%step](/reference/arvo/jael/examples#step) section of the Examples document for a practical example.
|
||||
See the [%step](/reference/arvo/jael/examples#step) section of the
|
||||
Examples document for a practical example.
|
||||
|
||||
---
|
||||
|
||||
## Gifts
|
||||
|
||||
### `%done`
|
||||
|
||||
Ames message (n)ack.
|
||||
|
||||
```hoon
|
||||
[%done error=(unit error:ames)]
|
||||
```
|
||||
|
||||
This is given in response to a `%plea` from Ames. You would not use this
|
||||
from userspace.
|
||||
|
||||
---
|
||||
|
||||
### `%boon`
|
||||
|
||||
Ames response.
|
||||
|
||||
```hoon
|
||||
[%boon payload=*]
|
||||
```
|
||||
|
||||
This is given in response to a request from Ames. You would not use this
|
||||
from userspace.
|
||||
|
||||
---
|
||||
|
||||
### `%private-keys`
|
||||
|
||||
Private keys.
|
||||
|
||||
```hoon
|
||||
[%private-keys =life vein=(map life ring)]
|
||||
```
|
||||
|
||||
This is given to those who have subscribed with a [`%private-keys`
|
||||
task](#private-keys) whenever our keys change.
|
||||
|
||||
The `life` is our current key revision number, and the `vein` contains a
|
||||
map from current and previous `life`s to private keys as `ring`s.
|
||||
|
||||
---
|
||||
|
||||
### `%public-keys`
|
||||
|
||||
Ethereum changes.
|
||||
|
||||
```hoon
|
||||
[%public-keys =public-keys-result]
|
||||
```
|
||||
|
||||
Public key information, diffs, and breach notifications. This is given
|
||||
to those who have subscribed with a [`%public-keys` task](#public-keys).
|
||||
|
||||
See the
|
||||
[`$public-keys-result`](/reference/arvo/dill/data-types#public-keys-result)
|
||||
entry in the data types reference for details of the data this gift
|
||||
contains.
|
||||
|
||||
---
|
||||
|
||||
### `%turf`
|
||||
|
||||
Domains.
|
||||
|
||||
```hoon
|
||||
[%turf turf=(list turf)]
|
||||
```
|
||||
|
||||
This is given in response to a [`%turf` task](#turf), and contains the
|
||||
list of domains used for galaxies (the `urbit.org` part in
|
||||
`zod.urbit.org`).
|
||||
|
||||
A `$turf` is a `(list @t)` of domain components, TLD-first. For example,
|
||||
`urbit.org` would be `~['org' 'urbit']`.
|
||||
|
||||
---
|
||||
|
@ -12,13 +12,15 @@ not yet proper libraries for other languages that can make use of it. Therefore,
|
||||
these documents will only touch on Khan's internal interface.
|
||||
|
||||
Khan's internal interface lets you run threads via Khan rather than having to
|
||||
poke [Spider](/reference/arvo/threads/reference) and subscribe for the result. This interface is simpler and more
|
||||
ergonomic than Spider's, so is usually preferable.
|
||||
poke [Spider](/reference/arvo/threads/reference) and subscribe for the result.
|
||||
This interface is simpler and more ergonomic than Spider's, so is usually
|
||||
preferable.
|
||||
|
||||
There are currently three `task`s to run a thread: `%fard`, `%fyrd`, and `%lard`.
|
||||
Only `%fard` is currently documented in
|
||||
the [API Reference](/reference/arvo/khan/tasks) section, and a practical example
|
||||
is given in the [Example](/reference/arvo/khan/example) section.
|
||||
There are two `task`s for running threads from userspace:
|
||||
[`%fard`](/reference/arvo/khan/tasks#fard) and
|
||||
[`%lard`](/reference/arvo/khan/tasks#lard). The former is for running a thread
|
||||
from file and the latter is for running an "in-line" thread, where you pass
|
||||
Khan the thread directly.
|
||||
|
||||
## Sections
|
||||
|
||||
|
@ -3,31 +3,101 @@ title = "API Reference"
|
||||
weight = 2
|
||||
+++
|
||||
|
||||
Khan's external interface is still experimental, so there's only one `task` that
|
||||
is currently useful:
|
||||
These are the `task`s Khan can be passed and the `gift`s it can give.
|
||||
|
||||
## `%fard`
|
||||
## Tasks
|
||||
|
||||
Here are the `task`s you can pass Khan. You'd either use
|
||||
[`%fard`](#fard) to run a thread from a file or [`%lard`](#lard) to run
|
||||
an in-line thread.
|
||||
|
||||
### `%fard`
|
||||
|
||||
Run a thread from within Arvo.
|
||||
|
||||
```hoon
|
||||
[%fard p=(fyrd cage)]
|
||||
```
|
||||
|
||||
Run a thread from within Arvo
|
||||
|
||||
`p` contains the thread location, name, and start arguments. See the
|
||||
[`fyrd`](/reference/arvo/khan/types#fyrd) data type reference entry for details.
|
||||
|
||||
#### Returns
|
||||
|
||||
When the thread finishes, either by succeeding or failing, Khan will return an
|
||||
`%arow` `gift`, which looks like :
|
||||
[`%arow`](#arow) gift.
|
||||
|
||||
---
|
||||
|
||||
### `%fyrd`
|
||||
|
||||
External thread.
|
||||
|
||||
```hoon
|
||||
[%fyrd p=(fyrd cast)]
|
||||
```
|
||||
|
||||
This is passed to Khan by the runtime when a thread is run externally.
|
||||
You would not use this from userspace.
|
||||
|
||||
---
|
||||
|
||||
### `%lard`
|
||||
|
||||
In-line thread.
|
||||
|
||||
```hoon
|
||||
[%lard =bear =shed]
|
||||
```
|
||||
|
||||
The [`bear`](/reference/arvo/khan/types#bear) is either a `desk` or
|
||||
`beak`. The [`shed`](/reference/arvo/khan/types#shed) is the thread
|
||||
itself. Since Spider doesn't need to read out the thread from Clay, the
|
||||
`bear` doesn't do much apart from be included in the thread name that
|
||||
Spider generates. Khan will have Spider run the given thread, and
|
||||
eventually give an [`%arow`](#arow) gift back with the result.
|
||||
|
||||
#### Returns
|
||||
|
||||
When the thread eventually finishes (or if it fails), Khan with give an
|
||||
[`%arow`](#arow) gift back with the result.
|
||||
|
||||
---
|
||||
|
||||
## Gifts
|
||||
|
||||
These are the two `gift`s Khan can give. In userspace, you'd only
|
||||
receive an [`%arow`](#arow).
|
||||
|
||||
### `%arow`
|
||||
|
||||
In-arvo result.
|
||||
|
||||
```hoon
|
||||
[%arow p=(avow cage)]
|
||||
```
|
||||
|
||||
`p` either contains the result in a `cage`, or an error and stack trace if it
|
||||
failed. See the [`avow`](/reference/arvo/khan/types#avow) data type reference
|
||||
entry for details.
|
||||
This gift contains the result of a finished thread if successful, or an
|
||||
error and stack trace if it failed. It's given for threads run from
|
||||
within Arvo. See the [`avow`](/reference/arvo/khan/types#avow) entry in
|
||||
the types reference for more details.
|
||||
|
||||
---
|
||||
|
||||
### `%avow`
|
||||
|
||||
External result.
|
||||
|
||||
```hoon
|
||||
[%avow p=(avow page)]
|
||||
```
|
||||
|
||||
This gift contains the result of running a thread externally. You would
|
||||
not receive this in userspace.
|
||||
|
||||
|
||||
A `page` is a pair of `mark` and `noun`. See the
|
||||
[`avow`](/reference/arvo/khan/types#avow) entry in the types reference
|
||||
for more details of that mold builder.
|
||||
|
||||
---
|
||||
|
@ -276,7 +276,7 @@ every step in the path the request takes onto the chain until we get to the
|
||||
terminal cause of the computation. Then we use this causal stack to route
|
||||
results back to the caller.
|
||||
|
||||
The Arvo causal stack is called a `duct`. This is represented simply as a list of paths, where each path represents a step in the causal chain. The first element in the path is the first letter of whichever vane handled that step in the computation, or the empty span for Unix.
|
||||
The Arvo causal stack is called a `duct`. This is represented simply as a list of paths, where each path represents a step in the causal chain. The first element in the path is the first letter of whichever vane handled that step in the computation, or the empty path element for Unix.
|
||||
|
||||
Here's a `duct` that was recently observed in the wild upon entering `-time ~s1`
|
||||
into the dojo and pressing Enter, which sets a timer for one second that will
|
||||
@ -301,7 +301,7 @@ time app. This app returns a `@d` which denotes the current time, which falls do
|
||||
which drops it through to the terminal. Terminal drops this down to Dill, which
|
||||
converts it into an effect that Unix will recognize as a request to print the
|
||||
current time to the screen. When Dill produces this, the last path in the `duct` has an
|
||||
initial element of the empty span, so this is routed to Unix, which applies the effects.
|
||||
initial empty element, so this is routed to Unix, which applies the effects.
|
||||
|
||||
This is a call stack, with a crucial feature: the stack is a first-class citizen. You can respond over a `duct` zero, one, or many times. You can save `duct`s for later use. There are definitely parallels to Scheme-style continuations, but simpler and with more structure.
|
||||
|
||||
|
@ -9,9 +9,17 @@ insert_anchor_links = "right"
|
||||
|
||||
Overview of threads.
|
||||
|
||||
## [Threads Guide](/guides/additional/threads/)
|
||||
## [HTTP API](/reference/arvo/threads/http-api)
|
||||
|
||||
Tutorial and explanation of the basics of writing threads.
|
||||
Documenation of Spider's HTTP API.
|
||||
|
||||
## [Reference](/reference/arvo/threads/reference)
|
||||
|
||||
Documentation of Spider's API.
|
||||
|
||||
## [Strandio](/reference/arvo/threads/strandio)
|
||||
|
||||
Reference documentation of the Strandio helper library.
|
||||
|
||||
## [Gall](/reference/arvo/threads/gall/)
|
||||
|
||||
|
@ -15,12 +15,12 @@ The `desk` is the desk in which the thread resides. The `inputMark` is the `mark
|
||||
|
||||
When Spider receives an HTTP request, the following steps happen:
|
||||
|
||||
1. It converts the raw body of the message to `json` using `de-json:html`
|
||||
1. It converts the raw body of the message to `json` using `de:json:html`
|
||||
2. It creates a `tube:clay` (`mark` conversion gate) from `json` to whatever input `mark` you've specified and does the conversion.
|
||||
3. It runs the specified thread and provides a `vase` of `(unit inputMark)` as the argument.
|
||||
4. The thread does its thing and finally produces its result as a `vase` of `outputMark`.
|
||||
5. Spider creates another `tube:clay` from the output `mark` to `json` and converts it.
|
||||
6. It converts the `json` back into raw data suitable for the HTTP response body using `en-json:html`.
|
||||
6. It converts the `json` back into raw data suitable for the HTTP response body using `en:json:html`.
|
||||
7. Finally, it composes the HTTP response and passes it back to Eyre which passes it on to the client.
|
||||
|
||||
Thus, it's important to understand that the original HTTP request and final HTTP response must contain JSON data, and therefore the input & output `mark`s you specify must each have a `mark` file in `/mar` that includes a conversion method for `json -> inputMark` and `outputMark -> json` respectively.
|
||||
|
@ -79,3 +79,7 @@ These docs walk through the basics of interacting with threads from gall agents.
|
||||
## [Reference](/reference/arvo/threads/reference)
|
||||
|
||||
Basic reference information. For usage of particular `strandio` functions just refer directly to `/lib/strandio/hoon` since they're largely self-explanatory.
|
||||
|
||||
## [Strandio](/reference/arvo/threads/strandio)
|
||||
|
||||
Reference documentation of the Strandio helper library.
|
||||
|
2654
content/reference/arvo/threads/strandio.md
Normal file
2654
content/reference/arvo/threads/strandio.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -127,10 +127,11 @@ Here is the line of code in `arvo.hoon`, found in the [section
|
||||
First we note that this line is executed only if the laconic bit is set to true,
|
||||
as we did when we input `|verb`. Here, `ovo` is the input `ovum`. Knowing that an `ovum` is a `[p=wire q=curd]`,
|
||||
we can then say that this is a `%unix` `move` tagged with `%belt` whose cause is a `wire` given by `//term/1`,
|
||||
where the empty span `//` represents Unix and `term/1` represents the terminal
|
||||
in Unix. Here we have a `wire` instead of a `duct` (i.e. a list of `wire`s)
|
||||
since Unix I/O events are always the beginning and end of the Arvo event loop,
|
||||
thus only a single `wire` is ever required at this initial stage.
|
||||
where the empty path element `//` represents Unix and `term/1`
|
||||
represents the terminal in Unix. Here we have a `wire` instead of a
|
||||
`duct` (i.e. a list of `wire`s) since Unix I/O events are always the
|
||||
beginning and end of the Arvo event loop, thus only a single `wire` is
|
||||
ever required at this initial stage.
|
||||
|
||||
The `""` here is a metadatum that keeps track of how many steps deep in the
|
||||
causal chain the event is. An event
|
||||
|
@ -100,7 +100,7 @@ Corresponds to the layer 2 `%spawn` action.
|
||||
```
|
||||
|
||||
Transfer `_point` to `_target`, clearing all permissions data and keys if
|
||||
`_reset` is true. `_reset` set to makes this transaction a
|
||||
`_reset` is true. `_reset` set to true makes this transaction a
|
||||
[breach](https://urbit.org/using/id/guide-to-resets), and thus this action increments the
|
||||
[`continuityNumber`](/reference/azimuth/azimuth-eth#points) of `_point`, and usually
|
||||
the `keyRevisionNumber` as well (see [Life and
|
||||
|
@ -35,9 +35,9 @@ The Gall agents involved with Azimuth are summarized as follows:
|
||||
|
||||
The transaction processing library is [`/lib/naive.hoon`](#naive).
|
||||
|
||||
### Gall agents
|
||||
## Gall agents
|
||||
|
||||
#### `%azimuth` {% #azimuth %}
|
||||
### `%azimuth` {% #azimuth %}
|
||||
|
||||
`%azimuth`, located at `/app/azimuth.hoon`, is a Gall agent and thread handler
|
||||
responsible for finding Azimuth transactions gathered by `%eth-watcher`,
|
||||
@ -79,12 +79,12 @@ Scries can be inferred from the `+on-peek` arm:
|
||||
==
|
||||
```
|
||||
|
||||
#### `%azimuth-rpc` {% #azimuth-rpc %}
|
||||
### `%azimuth-rpc` {% #azimuth-rpc %}
|
||||
|
||||
`%azimuth-rpc`, located at `app/azimuth-rpc.hoon`, is a JSON RPC-API for getting
|
||||
`point` and `dns` data from the Azimuth PKI state kept by `%azimuth`.
|
||||
|
||||
#### `%eth-watcher` {% #eth-watcher %}
|
||||
### `%eth-watcher` {% #eth-watcher %}
|
||||
|
||||
`%eth-watcher`, located at `/app/eth-watcher.hoon`, is responsible for listening
|
||||
to an Ethereum node and collecting event logs from it. It is general-purpose and
|
||||
@ -94,7 +94,7 @@ not particular to Azimuth. It sends collected transactions to `+on-agent` in
|
||||
|
||||
[![Eth-watcher](https://media.urbit.org/docs/layer2/roller-agents.png)](https://media.urbit.org/docs/layer2/roller-agents.png)
|
||||
|
||||
#### `%roller` {% #roller %}
|
||||
### `%roller` {% #roller %}
|
||||
|
||||
`%roller`, stored at `/app/roller.hoon`, is a Gall agent responsible for
|
||||
collecting and submitting batches of layer 2 transactions to the Ethereum
|
||||
@ -142,7 +142,7 @@ This app is not responsible for communicating with Bridge via HTTP. Instead, tha
|
||||
handled by `%roller-rpc`. The scries are also communicated to Bridge via
|
||||
`%roller-rpc`.
|
||||
|
||||
#### `%roller-rpc`
|
||||
### `%roller-rpc`
|
||||
|
||||
`%roller-rpc`, stored at `/app/roller-rpc.hoon`, is a very simple Gall app responsible for receiving HTTP RPC-API
|
||||
calls, typically sent from other Urbit ID users via Bridge. It then translates
|
||||
@ -152,7 +152,7 @@ does not keep any state - its only purpose is to act as an intermediary between
|
||||
Bridge and `%roller`. See [here](/reference/azimuth/l2/layer2-api) for more
|
||||
information on the JSON RPC-API.
|
||||
|
||||
### `naive.hoon` {% #naive %}
|
||||
## `naive.hoon` {% #naive %}
|
||||
|
||||
`/lib/naive.hoon` consists of a gate whose sample is a `verifier`, `chain-id=@ud`,
|
||||
`state`, and `input`, which outputs a cell of `[effects state]`. This is the
|
||||
|
@ -23,7 +23,7 @@ HD wallet [below](#hardware-hd-wallet).
|
||||
Urbit HD wallets are composed of the following items, which are each assigned to
|
||||
their own individual Ethereum key-pairs.
|
||||
|
||||
### Master Ticket
|
||||
## Master Ticket
|
||||
|
||||
Think of your master ticket like a very high-value password. The master ticket
|
||||
is the secret code from which all of your other keys are derived. Technically,
|
||||
@ -31,14 +31,14 @@ your master ticket is a cryptographic seed. You should never share it with anyon
|
||||
store it very securely. This ticket can derive all of your other keys: your
|
||||
ownership key and all of the related proxies.
|
||||
|
||||
### Ownership Address
|
||||
## Ownership Address
|
||||
|
||||
An ownership address has all rights over the assets deeded to it. These rights
|
||||
are on-chain actions described and implemented in
|
||||
[Ecliptic](/reference/glossary/ecliptic), Azimuth's suite of governing
|
||||
smart-contracts.
|
||||
|
||||
### Proxies
|
||||
## Proxies
|
||||
|
||||
Each permanent Urbit ID can designate one or more
|
||||
[proxies](https://urbit.org/using/id/proxies), which are Ethereum addresses capable of a
|
||||
@ -46,7 +46,7 @@ limited subset of Urbit ID transactions, such as spawning planets or rotating
|
||||
keys. The HD wallet automatically generates additional addresses utilized as
|
||||
proxies according to what is appropriate for your Urbit ID.
|
||||
|
||||
### HD wallet generation
|
||||
## HD wallet generation
|
||||
|
||||
Your Urbit HD wallet is generated from a `@q` seed called `T`, which looks
|
||||
something like `~sampel-ticket-bucbel-sipnem`. This is the string known as your
|
||||
@ -68,7 +68,7 @@ which will be known as your ownership address. Bridge then automatically uses
|
||||
your ownership address to assign the other proxies to the other wallets
|
||||
generated.
|
||||
|
||||
### ERC-721
|
||||
## ERC-721
|
||||
|
||||
Most Ethereum tokens use the ERC-20 standard for smart contracts. Urbit
|
||||
identities are, however, essentially different from most Ethereum tokens, due to
|
||||
|
@ -17,14 +17,14 @@ while otherwise remaining on layer 1, but it is not possible to transfer only
|
||||
the management proxy to layer 2; it may only happen as a side-effect of
|
||||
transferring ownership to layer 2.
|
||||
|
||||
### Moving a pre-existing ship to L2
|
||||
## Moving a pre-existing ship to L2
|
||||
|
||||
In order to move your ship from layer 1 to layer 2, transfer ownership of your
|
||||
ship to the address `0x1111111111111111111111111111111111111111`. The easiest
|
||||
way to accomplish this is using [Bridge](/reference/glossary/bridge). The Azimuth
|
||||
smart contracts interpret any ship at this address as being on layer 2.
|
||||
|
||||
### Dominion
|
||||
## Dominion
|
||||
|
||||
Layer 2 Azimuth data for a given ship includes which layer that ship is on. We
|
||||
call this the ship's _dominion_. There are three dominions: `%l1`, `l2`, and
|
||||
@ -32,11 +32,11 @@ call this the ship's _dominion_. There are three dominions: `%l1`, `l2`, and
|
||||
of the three dominions, and galaxies may exist in dominion `%l1` or `%spawn`. We
|
||||
detail what this means in each case in the following.
|
||||
|
||||
### Planets
|
||||
## Planets
|
||||
|
||||
*Permitted dominions:* `%l1`, `%l2`.
|
||||
|
||||
#### `%l1` planets
|
||||
### `%l1` planets
|
||||
|
||||
*Permitted layer 2 actions:*
|
||||
- owner: `%escape`, `%cancel-escape`
|
||||
@ -54,7 +54,7 @@ sponsorship actions.
|
||||
Layer 1 planets may also move to dominion `%l2` by depositing their ownership to
|
||||
the layer 2 deposit address.
|
||||
|
||||
#### `%l2` planets
|
||||
### `%l2` planets
|
||||
|
||||
*Permitted layer 2 actions:*
|
||||
- owner: `%transfer-point`, `%configure-keys`,
|
||||
@ -71,11 +71,11 @@ will always be on layer 2.
|
||||
A layer 2 planet is no longer capable of performing any layer 1 actions, and
|
||||
cannot move to layer 1.
|
||||
|
||||
### Stars
|
||||
## Stars
|
||||
|
||||
*Permitted dominions:* `%l1`, `%spawn`, `%l2`.
|
||||
|
||||
#### `%l1` stars
|
||||
### `%l1` stars
|
||||
|
||||
*Permitted layer 2 actions:*
|
||||
- owner: `%escape`, `%cancel-escape`, `%adopt`,
|
||||
@ -94,7 +94,7 @@ A `%l1` dominion star may move to dominion `%spawn` by depositing its spawn prox
|
||||
layer 2 deposit address, or may move to dominion `%l2` by depositing its ownership
|
||||
to the layer 2 deposit address. Both actions are irreversible.
|
||||
|
||||
#### `%spawn` stars
|
||||
### `%spawn` stars
|
||||
|
||||
*Permitted layer 2 actions:*
|
||||
- owner: `%escape`, `%cancel-escape`, `%adopt`,
|
||||
@ -116,7 +116,7 @@ A star moving from `%l1` to `%spawn` has no effect on sponsorship status of any
|
||||
of its sponsored planets. Moving to `%spawn` from `%l1` is currently
|
||||
irreversible - the only further change to dominion permitted is moving to `%l2`.
|
||||
|
||||
#### `%l2` stars
|
||||
### `%l2` stars
|
||||
|
||||
*Permitted layer 2 actions:*
|
||||
- owner: `%transfer-point`, `%spawn`, `%configure-keys`, `%escape`,
|
||||
@ -134,11 +134,11 @@ spawned by a `%spawn` dominion galaxy.
|
||||
|
||||
A star in dominion `%l2` cannot perform any layer 1 actions.
|
||||
|
||||
### Galaxies
|
||||
## Galaxies
|
||||
|
||||
*Permitted dominions:* `%l1`, `%spawn`.
|
||||
|
||||
#### `%l1` galaxies
|
||||
### `%l1` galaxies
|
||||
|
||||
*Permitted layer 2 actions:*
|
||||
- owner: `%adopt`, `%reject`, `%detach`
|
||||
@ -162,7 +162,7 @@ Layer 2 has no interactions with this contract - all stars released in this
|
||||
manner are `%l1` dominion stars. Moving to the `%spawn` dominion has no effect
|
||||
on sponsorship status.
|
||||
|
||||
#### `%spawn` galaxies
|
||||
### `%spawn` galaxies
|
||||
|
||||
*Permitted layer 2 actions:*
|
||||
- owner: `%adopt`, `%reject`, `%detach`, `%spawn`,
|
||||
|
@ -32,7 +32,7 @@ There are three main steps involved with setting up a roller:
|
||||
- starting and configuring `%roller`,
|
||||
- aiming your front-end at the roller
|
||||
|
||||
### 1. Make sure `%azimuth` state is up to date
|
||||
## 1. Make sure `%azimuth` state is up to date
|
||||
|
||||
If you are using an ordinary live ship on the network as the roller, you should
|
||||
already have the latest `%azimuth` state and this step should not be necessary
|
||||
@ -52,7 +52,7 @@ found under the Setting page for the node on infura.io listed under `ENDPOINTS`.
|
||||
If you do not perform this step, you'll later see an error "roller not ready"
|
||||
when the first roller batch is about to be submitted.
|
||||
|
||||
### 2. Starting and configuring `%roller` {% #step2 %}
|
||||
## 2. Starting and configuring `%roller` {% #step2 %}
|
||||
|
||||
This step must be performed whether you're using a fakezod or a live ship.
|
||||
|
||||
@ -125,7 +125,7 @@ work with a front-end if you want to use it on livenet.
|
||||
|
||||
We cover the additional settings for `%roller` at the end.
|
||||
|
||||
### 3. Aiming Bridge at the roller
|
||||
## 3. Aiming Bridge at the roller
|
||||
|
||||
The last step is to set up the web interface by which users can submit
|
||||
transactions to be batched by the roller, which we refer to as the front-end. We
|
||||
@ -153,7 +153,7 @@ REACT_APP_ROLLER_HOST=https://myroller.sampel-pal.net/v1/roller npm run pilot-ma
|
||||
This will launch a server running Bridge that utilizes the mainnet roller you
|
||||
set up at `https://myroller.sampel-pal.net/v1/roller`.
|
||||
|
||||
### Additional `%roller` commmands
|
||||
## Additional `%roller` commmands
|
||||
|
||||
`%roller` has a few other settings and commands for managing things like the
|
||||
rate at which transactions are submitted and manually submitting batches. These
|
||||
|
@ -1,5 +1,5 @@
|
||||
+++
|
||||
title = "Kelvin versioning"
|
||||
title = "Kernel"
|
||||
|
||||
[extra]
|
||||
category = "arvo"
|
||||
|
32
content/reference/glossary/remote-scry.md
Normal file
32
content/reference/glossary/remote-scry.md
Normal file
@ -0,0 +1,32 @@
|
||||
+++
|
||||
title = "Scry"
|
||||
|
||||
[extra]
|
||||
category = "hoon-nock"
|
||||
|
||||
[glossaryEntry."Remote scry"]
|
||||
name = "remote scry"
|
||||
symbol = ""
|
||||
usage = "hoon-nock"
|
||||
desc = "A remote scry is a read-only request to the namespace of a remote ship."
|
||||
|
||||
+++
|
||||
|
||||
A **remote scry** is a read-only request to the namespace of a remote
|
||||
[vane](/reference/glossary/vane) or [agent](/reference/glossary/agent). These
|
||||
differ from ordinary [local scries](/reference/glossary/scry) and are not
|
||||
performed with the `.^` [rune](/reference/glossary/rune).
|
||||
|
||||
Remote scries reduce event log bloat on the publishing ship, allow the
|
||||
publisher's runtime to cache data, and especially improve performance when
|
||||
publishing the same data for many ships to retrieve.
|
||||
|
||||
At the the time of writing, Gall allows agents to bind data to remote scry
|
||||
paths and perform remote scries with `task`s to
|
||||
[Ames](/reference/glossary/ames). Additionally, Clay uses remote scries
|
||||
internally to sync remote desks.
|
||||
|
||||
#### Further Reading
|
||||
|
||||
- [Remote scry guide](/guides/additional/remote-scry): developer documentation
|
||||
of how remote scries work and how to use them.
|
@ -51,7 +51,7 @@ changed two parts: `%core` and `%face`. We added polymorphism to
|
||||
|
||||
If cores never changed, we wouldn't need polymorphism. Of
|
||||
course, nouns are immutable and never change, but we use them as
|
||||
but we use them as templates to construct new nouns around.
|
||||
templates to construct new nouns around.
|
||||
|
||||
Suppose we take a core, a cell `[battery payload]`, and replace
|
||||
the payload with a different noun. Then, we invoke an arm from
|
||||
|
@ -9,7 +9,7 @@ This is used for type checking as well as pretty printing.
|
||||
|
||||
You can learn more about auras in [Hoon school](/guides/core/hoon-school/B-syntax#nouns).
|
||||
|
||||
### Table of Auras
|
||||
## Table of Auras
|
||||
|
||||
```
|
||||
Aura Meaning Example Literal Syntax
|
||||
@ -48,7 +48,7 @@ Aura Meaning Example Literal Syntax
|
||||
@ux unsigned hexadecimal 0x5f5.e138
|
||||
```
|
||||
|
||||
### Bitwidth
|
||||
## Bitwidth
|
||||
|
||||
Capital letters at the end of auras indicate the bitwidth in binary powers of
|
||||
two, starting from A.
|
||||
@ -61,7 +61,7 @@ two, starting from A.
|
||||
@uvJ unsigned, 512-bit integer (frequently used for entropy)
|
||||
```
|
||||
|
||||
### Nesting
|
||||
## Nesting
|
||||
|
||||
A given aura nests under any aura whose name is a substring or extension of the
|
||||
given aura:
|
||||
@ -94,7 +94,7 @@ This is implicitly done by the irregular form of `^-`.
|
||||
7.303.014
|
||||
```
|
||||
|
||||
### Bunting
|
||||
## Bunting
|
||||
|
||||
The bunt value for all auras is 0 except for `@da`.
|
||||
|
||||
|
@ -3,6 +3,17 @@ title = "Irregular forms"
|
||||
weight = 20
|
||||
+++
|
||||
|
||||
While Hoon has a large amount of sugar syntax, some forms that may look irregular are
|
||||
actually regular wing syntax or another language feature, such as `,`.
|
||||
|
||||
When in doubt, you can use the [`!,` zapcom](/reference/hoon/rune/zap#-zapcom) rune to
|
||||
determine the AST to which Hoon parses an expression.
|
||||
|
||||
```
|
||||
> !,(*hoon c.b.a)
|
||||
[%wing p=~[%c %b %a]]
|
||||
```
|
||||
|
||||
## Quick Lookup of Irregular Forms
|
||||
|
||||
| Form | Regular Form |
|
||||
@ -366,6 +377,82 @@ See [%sand](/reference/hoon/rune/constants#warm) for other irregular definitions
|
||||
"~[1 2 3]"
|
||||
```
|
||||
|
||||
### `,` com
|
||||
|
||||
`,` can serve in several capacities in Hoon programs:
|
||||
|
||||
1. As sugar for the `^:` ketcol or `$;` bucmic runes, toggling structure and value mode.
|
||||
(Toggling out of structure mode is uncommon.)
|
||||
|
||||
```
|
||||
> !,(*hoon ,[@t @t])
|
||||
[ %ktcl
|
||||
p=[%bccl p=[i=[%base p=[%atom p=~.t]] t=[i=[%base p=[%atom p=~.t]] t=~]]]
|
||||
]
|
||||
|
||||
> !,(*hoon |=(a=,[@t @t] b))
|
||||
[ %brts
|
||||
p
|
||||
[ %bcts
|
||||
p=term=%a
|
||||
q
|
||||
[ %bcmc
|
||||
p=[%cltr p=[i=[%base p=[%atom p=~.t]] t=[i=[%base p=[%atom p=~.t]] t=~]]]
|
||||
]
|
||||
]
|
||||
q=[%cnts p=~[[%.y p=2] %a] q=~]
|
||||
]
|
||||
|
||||
> !,(*hoon ,,[@t @t])
|
||||
[ %ktcl
|
||||
p
|
||||
[ %bcmc
|
||||
p=[%cltr p=[i=[%base p=[%atom p=~.t]] t=[i=[%base p=[%atom p=~.t]] t=~]]]
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
(`$;` bucmic, or manual value mode, allows the use of value mode syntax to
|
||||
construct a mold. Concretely, it lets you build a mold out of `hoon` instead
|
||||
of out of `spec`. It is not commonly used.)
|
||||
|
||||
From value mode to structure mode:
|
||||
|
||||
```hoon
|
||||
[%ktcl p=spec]
|
||||
```
|
||||
|
||||
From structure mode to value mode:
|
||||
|
||||
```hoon
|
||||
[%bcmc p=hoon]
|
||||
```
|
||||
|
||||
2. As wing syntax for stripping a face.
|
||||
|
||||
For instance, a line similar to the following is present in many Gall agents
|
||||
receiving HTTP requests via Eyre:
|
||||
|
||||
```
|
||||
=/ ,request-line:server (parse-request-line:server url.request.inbound-request)
|
||||
```
|
||||
|
||||
This `,` lets you avoid using an outer face when handling the result.
|
||||
|
||||
```
|
||||
> =/ ,@ud 1
|
||||
-
|
||||
1
|
||||
> !,(*hoon =/(,@ud 1 -))
|
||||
[ %tsfs
|
||||
p=[%spec spec=[%bcmc p=[%base p=[%atom p=~.ud]]] skin=[%base base=%noun]]
|
||||
q=[%sand p=%ud q=1]
|
||||
r=[%cnts p=~[[%.y p=2]] q=~]
|
||||
]
|
||||
```
|
||||
|
||||
3. As a separator, e.g. between pairs in an inline `%=` centis expression, `$(i +(i), j (dec j))`.
|
||||
|
||||
## Commentary
|
||||
|
||||
In our in-house examples throughout our documentation, we use irregular forms instead of regular for the sake of verbosity. But remember with irregular forms: everything is just runes! Like magic. In general, irregular forms (usually) read better, but of course regular forms provide more information about what you're doing by showing you the full rune. Of course, it's up to you, the Hoon programmer, as to whether or not you want to use these.
|
||||
|
@ -4,13 +4,13 @@ weight = 1
|
||||
+++
|
||||
A limb is an attribute of subject.
|
||||
|
||||
### Produces
|
||||
## Produces
|
||||
|
||||
There are two kinds of limbs: arms and legs. An **arm** is a computation of some core. A **leg** is a piece of data in the subject.
|
||||
|
||||
If a limb expression resolves to a leg, the leg is produced. If a limb expression resolves to an arm -- in particular, by way of an arm name -- then the arm is computed with its parent core as the subject. The result of that computation is produced.
|
||||
|
||||
### Syntax
|
||||
## Syntax
|
||||
|
||||
Irregular: `+15` is slot `15`. The value at address `15` of the subject is produced.
|
||||
|
||||
@ -26,7 +26,7 @@ Irregular: `^^^abc` is the name `abc`, but which will skip the first three name
|
||||
|
||||
Irregular: `+<-` is "take the tail, then take the head of that, then the head of that." `+` and `>` mean "tail" while `-` and `<` mean "head." This limb syntax starts on `+` or `-` and alternates with `>` or `<` for readability.
|
||||
|
||||
### Traverse
|
||||
## Traverse
|
||||
|
||||
Name resolution happens by way of a search through the subject. The search traverse takes a name `q` and a **skip count** `p`.
|
||||
|
||||
@ -47,7 +47,7 @@ If the skip count `p` is nonzero, we pretend our first `p`
|
||||
matches are actually mismatches. This lets the programmer "look
|
||||
through" an overriding label.
|
||||
|
||||
### Examples
|
||||
## Examples
|
||||
|
||||
The Dojo prompt gives you a subject with a decent namespace.
|
||||
Try:
|
||||
|
@ -6,15 +6,15 @@ weight = 2
|
||||
|
||||
A wing is a limb search path into the subject.
|
||||
|
||||
### Produces
|
||||
## Produces
|
||||
|
||||
A wing is a list of limbs (including a trivial list of one limb). The limbs are resolved in succession. The result of the last limb resolution is the value produced by the wing expression.
|
||||
|
||||
### Syntax
|
||||
## Syntax
|
||||
|
||||
Irregular: `a.b.c`. Read this as '`a` in `b` in `c`'. Finds limb `a` within limb `b` within limb `c` of the subject.
|
||||
|
||||
### Discussion
|
||||
## Discussion
|
||||
|
||||
Intuitively, Hoon wings are written in the opposite order
|
||||
from attribute dot-paths in most languages. Hoon `a.b.c` is Java's
|
||||
@ -29,7 +29,7 @@ The mysterious idiom `..b` produces the leg `b` if `b`
|
||||
is a leg; the core exporting `b` if `b` is an arm. Since `.`
|
||||
is the same limb as `+`, `..b` is the same wing as `+1.foo`.
|
||||
|
||||
### Examples
|
||||
## Examples
|
||||
|
||||
```
|
||||
~zod:dojo> =a [fod=3 bat=[baz=1 moo=2]]
|
||||
@ -40,3 +40,26 @@ is the same limb as `+`, `..b` is the same wing as `+1.foo`.
|
||||
~zod:dojo> moo.bat.a
|
||||
2
|
||||
```
|
||||
|
||||
## Wing Resolution
|
||||
|
||||
There are two common syntaxes used to resolve a wing path into the
|
||||
current subject: `.` dot and `:` col.
|
||||
|
||||
- `.` dot syntax, as `c.b.a`, resolves the wing path into the subject
|
||||
at the right hand using Nock 0 (or possibly Nock 9 or Nock 10
|
||||
depending on the expression).
|
||||
|
||||
```hoon
|
||||
> !,(*hoon c.b.a)
|
||||
[%wing p=~[%c %b %a]]
|
||||
```
|
||||
|
||||
- The `:` col operator expands to a `=>` tisgar to resolve the wing path
|
||||
against its right-hand side as the subject. This can be a Nock 7
|
||||
or possibly optimized by the compiler to a Nock 0.
|
||||
|
||||
```hoon
|
||||
> !,(*hoon c:b:a)
|
||||
[%tsgl p=[%wing p=~[%c]] q=[%tsgl p=[%wing p=~[%b]] q=[%wing p=~[%a]]]]
|
||||
```
|
||||
|
@ -11,7 +11,7 @@ language that set it apart from others. If you're looking to learn Hoon, check
|
||||
out our tutorial series called [Hoon School](/guides/core/hoon-school/)
|
||||
below.
|
||||
|
||||
### What can Hoon do that other languages can't?
|
||||
## What can Hoon do that other languages can't?
|
||||
|
||||
The short answer is: implement a purely functional operating system.
|
||||
Try to do this in a principled way in Haskell, and the problems you'll
|
||||
@ -25,7 +25,7 @@ functional languages are:
|
||||
- Typesafe metaprogramming, and
|
||||
- Hot code reload and online data migration.
|
||||
|
||||
### What is Hoon good at?
|
||||
## What is Hoon good at?
|
||||
|
||||
Hoon is mostly good at compiling and running other Hoon code. Urbit
|
||||
consists of many layers of bootstrapping. Several of these layers lean
|
||||
@ -34,7 +34,7 @@ build system, the Dojo shell, and the Arvo kernel itself. Even Urbit's
|
||||
chat application lets you run Hoon expressions and share the results
|
||||
with your friends.
|
||||
|
||||
### Why did we write the OS in Hoon?
|
||||
## Why did we write the OS in Hoon?
|
||||
|
||||
The chain of reasoning goes something like this:
|
||||
|
||||
@ -78,13 +78,13 @@ Urbit's solution to these design constraints. Some Lisps come close to
|
||||
meeting these criteria — and Nock is very Lisp-like — but no practical
|
||||
Lisp dialects are nearly as pure or axiomatic as Nock.
|
||||
|
||||
### What is special about Hoon?
|
||||
## What is special about Hoon?
|
||||
|
||||
It's a purely functional systems language. Calling it a functional
|
||||
analog of C is not too far off in several ways. Almost all code
|
||||
throughout Urbit's kernelspace and userspace is written in Hoon.
|
||||
|
||||
### What properties does Hoon have? What type of language is it?
|
||||
## What properties does Hoon have? What type of language is it?
|
||||
|
||||
Hoon is a statically typed, purely functional, strictly evaluated
|
||||
programming language.
|
||||
@ -215,7 +215,7 @@ Hoon and Nock have several unusual properties:
|
||||
calmness of working with such inert building blocks is addictive, as
|
||||
many Hoon programmers will attest.
|
||||
|
||||
### Why is Hoon the way it is?
|
||||
## Why is Hoon the way it is?
|
||||
|
||||
Minimalism, mostly.
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user