Skip to content

Commit

Permalink
Added some docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyakooo0 committed Jul 7, 2023
1 parent 8168841 commit c24d711
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 10 deletions.
105 changes: 105 additions & 0 deletions docs/sink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# %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 sink 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
=/ snik
::
:: replace /sync with whatever path you want to use for syncing your state.
%+ sink ~[/sync]
::
:: This part is a gate that extracts the state you want to sync from your agent state.
:: This allows you to sync only the part of your state that you need, saving resources.
:: If you want to sync the whole state then just pass identity: `|=(x=versioned-state x)`
::
:: versioned-state should be replaced with whatever the type of your application state is.
|=(stat=versioned-state !!)
::
:: Next you initialize your sink with your initial agent state.
=/ sink (snik state)
```

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 state)` is the important bit.
`this(state state, sink (snik state))
```

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 state)
```

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 = "~zod"
, 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](../exmaple/src/Sink.elm) for a full example.
1 change: 0 additions & 1 deletion example/urbit/lib/sink.hoon
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
=/ dif
%+ diff:noun-diff (extract ^stat) (extract stat)
:-
~& [%give %fact pats %noun !>(^-((clog) [%drain dif]))]
[%give %fact pats %noun !>(^-((clog) [%drain dif]))]
..sync(stat stat)
++ paths pats
Expand Down
4 changes: 2 additions & 2 deletions src/Ur/Constructor.elm
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ signedInt i =
)


{-| Constructs an `Atom` from `Bytes`
{-| Constructs an `Atom` from `Bytes`.
-}
bytes : Bytes -> Noun
bytes =
Expand All @@ -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 =
Expand Down
6 changes: 4 additions & 2 deletions src/Ur/Deconstructor.elm
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/Ur/Phonemic.elm
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
6 changes: 3 additions & 3 deletions src/Ur/Sub.elm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 } =
Expand Down
2 changes: 1 addition & 1 deletion src/Ur/Uw.elm
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit c24d711

Please sign in to comment.