Added some docs (#7)

* Added some docs

* Simplified %sink (#8)

* Simplified %sink

* Formatting

* Added docs for new sink

* fixed padding

* fixed padding

* fixed padding

* fixed link
This commit is contained in:
iko 2023-07-09 20:11:38 +03:00 committed by GitHub
parent 5391624c2a
commit 1aa86b4da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 13 deletions

101
docs/sink.md Normal file
View File

@ -0,0 +1,101 @@
# %sink
%sink is a state replication system between your %gall agent and your Elm airlock.
%sink allows you to avoid having to manually write subscriptions which send semantic diffs between your %gall agent and the frontend.
## How it works
%sink consists of two parts:
1. The %sink library you import into your %gall agent and call appropriate functions at the right points in your agent lifecycle. More on this in a bit.
2. The `Ur.Sub.sink` urbit subscription which reconciles the state and hands you the latest version of %gall agent state that you can then `Deconstruct` into Elm data structures.
The whole system works by saving the previous version your %gall agent state and diffing the raw nouns that compose your old state and your new state.
On first initialization of the %sink system the whole state you supplied is sent to the frontend. Any subsequent changes are sent as a diff between your old state and your new state.
## How to use it
### The %gall agent part
#### Copying library files
In order to use %sink you have to copy the following two files into your agent desk into the `lib` directory:
1. [example/urbit/lib/noun-diff.hoon](../example/urbit/lib/noun-diff.hoon) -- contains the algorithm for diffing raw nouns.
2. [example/urbit/lib/sink.hoon](../example/urbit/lib/sink.hoon) -- contains the logic of syncing the state between your agent and your frontend.
#### Adding sync points into your agent
First you need to import the %sink library into your agent:
```hoon
/+ *sink
```
Next you need to initialize the sink. Put the following declaration somewhere before your agent door:
```hoon
::
:: A deferred expression of the state you want to sync to the frontend.
=* entries (tap:j-orm journal.stat)
::
:: Replace /sync with whatever path you want to use for syncing your state.
=/ snik (sink ~[/sync])
::
:: Next you initialize your sink with your initial agent state.
=/ sink (snik entries)
```
You can have multiple sinks in the same application to sync different parts of your state on different paths.
Don't forget to reinitialize your `sink` when your restore your agent state in the `++on-load` arm:
```hoon
++ on-load
|= old-vase=vase
^- (quip card _this)
=/ state !<(versioned-state old-vase)
::
:: the `sink (snik entries)` is the important bit.
`this(state state, sink (snik entries))
```
Lastly, you need to send sink updates whenever you change your state. Most likely this will be in your `++on-poke` arm:
```hoon
::
:: This line generates a `card` that you need to pass to arvo and updates
:: the `sink` to reference the latest state of your agent.
=^ card sink (sync:sink entries)
```
You can look at the [journal.hoon](../example/urbit/app/journal.hoon) for a full example.
#### Adding sink the the frontend
For your frontend to recieve %sink updates you need to pass the result of calling `Ur.Sub.sink` to the `urbitSubscriptions` field of `Ur.Run.application` or similar function from `Ur.Run`:
```elm
main : Ur.Run.Program Model Msg
main =
Ur.Run.application
{
-- ...
urbitSubscriptions =
-- ...
Ur.Sub.sink
{ ship = ship
, app = "journal"
, path = [ "sync" ]
, deconstructor =
D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b )))
|> D.map GotListings
}
-- ...
}
```
In the `deconstructor` field you specify a `Deconstructor` the deconstructs _the whole_ state that is being synced form the %gall agent.
You do not have to deal with diffs. It is handled automatically
You can look at [example/src/Sink.elm](../example/src/Sink.elm) for a full example.

View File

@ -80,7 +80,6 @@ type alias Model =
type Msg
= Noop
| GotSink Noun
| Error String
| GotListings (List ( BigInt, String ))
| UpdateNewEntry String
@ -96,9 +95,6 @@ update msg model =
Noop ->
( model, Ur.Cmd.none )
GotSink _ ->
( model, Ur.Cmd.none )
Error err ->
( { model | error = err }, Ur.Cmd.none )

View File

@ -89,7 +89,7 @@ signedInt i =
)
{-| Constructs an `Atom` from `Bytes`
{-| Constructs an `Atom` from `Bytes`.
-}
bytes : Bytes -> Noun
bytes =
@ -103,7 +103,7 @@ cord s =
Atom (BE.encode (BE.string s))
{-| Constructs a [`tape`](https://developers.urbit.org/reference/glossary/tape) from a `String`
{-| Constructs a [`tape`](https://developers.urbit.org/reference/glossary/tape) from a `String`.
-}
tape : String -> Noun
tape s =

View File

@ -102,7 +102,7 @@ runBytes (Deconstructor f) bs =
{-| Asserts that the value at the current position should be exactly equal to the second argument.
The first argument is a `Deconstructor` for the gven tyoe.
The first argument is a `Deconstructor` for the given type.
The second argument is the value to compare with.
@ -370,7 +370,9 @@ cell (Deconstructor l) (Deconstructor r) =
)
{-| -}
{-| "Lazily" applies a deconstructor.
This is useful when you are defining a recursive `Deconstructor` which needs to call itself.
-}
lazy : (() -> Deconstructor a b) -> Deconstructor a b
lazy f =
Deconstructor

View File

@ -13,7 +13,7 @@ import Urbit.Encoding.Atom exposing (toBigInt)
import Urbit.Encoding.Phonemic exposing (..)
{-| Converts a ship name like `~zod` into an Atom.
{-| Converts a ship name like `~zod` into an `Atom`.
-}
fromString : Ship -> Atom
fromString s =

View File

@ -3,7 +3,7 @@ module Ur.Sub exposing
, sink
)
{-| This module is conceptually similar to `Platform.Sub`, but also you to subscribe to Urbit channels.
{-| This module is conceptually similar to `Platform.Sub`, but allows you to subscribe to Urbit channels.
@docs Sub, subscribe, none, batch
@ -14,7 +14,7 @@ import Ur.Deconstructor as D
import Ur.Sub.Internal
{-| Like `Sub` from `Platform.Sub`, but for Urbit subscriptions.
{-| Like `Sub` from `Platform.Sub` but for Urbit subscriptions.
-}
type alias Sub msg =
Ur.Sub.Internal.Sub msg
@ -39,7 +39,7 @@ subscribe { ship, app, path, deconstructor } =
|> Ur.Sub.Internal.Sub
{-| Create a Sink subscription.
{-| Creates a %sink subscription.
-}
sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
sink { ship, app, path, deconstructor } =

View File

@ -1,6 +1,6 @@
module Ur.Uw exposing (decode, encode)
{-| This module works with Urbit base-64 encoded strings.
{-| This module works with Urbit base-64 encoded strings aka `@uw`.
@docs decode, encode