mirror of
https://github.com/ilyakooo0/airlock.git
synced 2024-10-03 21:37:36 +03:00
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:
parent
5391624c2a
commit
1aa86b4da7
101
docs/sink.md
Normal file
101
docs/sink.md
Normal 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.
|
@ -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 )
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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 } =
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user