mirror of
https://github.com/urbit/shrub.git
synced 2024-08-17 08:00:39 +03:00
Implement changes to Counter tutorial (#74)
* update counter example to ensure it compiles * fix formatting * make shrub at a meaningful path * update success case, add todo * modify to use cull instead of tomb, per existing diary.hoon * Add comment on counter's +kids arm * Delete old counter files that snuck back in * Improve pith and card section * Implement TODO about verifying poke worked --------- Co-authored-by: Josh Lehman <josh@urbit.org>
This commit is contained in:
parent
1b6e964700
commit
2fa9eff190
@ -1,21 +0,0 @@
|
||||
/@ node :: manx
|
||||
/@ counter-diff :: [%inc ~]
|
||||
:: import /lib/manx-utils, which helps us work with XML
|
||||
/- manx-utils
|
||||
:: declare this is a conversion from node to counter-diff
|
||||
:- [%node %$ %counter-diff]
|
||||
|= =node
|
||||
^- counter-diff
|
||||
:: initiate the manx-utils door with node
|
||||
=/ mu ~(. manx-utils node)
|
||||
::
|
||||
:: got:mu gets an attribute from the manx by its name
|
||||
:: in this case, the =head specified in /con/number-htmx
|
||||
:: we expect the head from the manx to be %inc,
|
||||
:: but we could add more terms to that type union...
|
||||
=/ head (?(%inc) (got:mu %head))
|
||||
::
|
||||
:: return the [%inc ~] poke
|
||||
:: if we wanted to handle multiple pokes,
|
||||
:: we'd switch on the type of head here
|
||||
[head ~]
|
@ -1,62 +0,0 @@
|
||||
/@ number :: @ud
|
||||
:: import /lib/feather-icons
|
||||
/- feather-icons
|
||||
:: declare that this is a conversion from number to HTMX
|
||||
:- [%number %$ %htmx]
|
||||
::
|
||||
:: this gate accepts a number and a bowl:neo;
|
||||
:: we'll access bowl:neo in the UI to access the
|
||||
:: here.bowl of the shrub that's using this /con file
|
||||
|= =number
|
||||
|= =bowl:neo
|
||||
::
|
||||
:: this gate returns a manx, which is what Hoon uses
|
||||
:: to store dynamic XML nodes; in this case we'll use
|
||||
:: Sail to specify a manx that expects the HTMX library
|
||||
:: to be available on the frontend
|
||||
^- manx
|
||||
::
|
||||
:: open a <div class="p3 fc g2 ac br2">
|
||||
:: these utility classes are specified in feather.css,
|
||||
:: which this /con file expects on the frontend
|
||||
;div.p3.fc.g2.ac.br2
|
||||
:: <h1>Counter</h1>
|
||||
;h1: Counter
|
||||
:: <p>{number}</p>
|
||||
;p: {<number>}
|
||||
:: open a <form> with HTMX attributes
|
||||
;form
|
||||
::
|
||||
:: hx-post will issue a POST request to the provided
|
||||
:: url and swap response into the DOM
|
||||
=hx-post "/neo/hawk{(en-tape:pith:neo here.bowl)}?stud=counter-diff"
|
||||
::
|
||||
:: hx-target specifies the target for hx-post's DOM
|
||||
:: swap: the element with class "loading"
|
||||
=hx-target "find .loading"
|
||||
::
|
||||
:: hx-swap specifies how the response to hx-post's
|
||||
:: request will be swapped in relative to the target
|
||||
=hx-swap "outerHTML"
|
||||
::
|
||||
:: here, the head attribute specifies the poke that
|
||||
:: hx-post will send to the target shrub; look at
|
||||
:: /con/node-counter-diff.hoon for more on =head
|
||||
=head "inc"
|
||||
::
|
||||
:: below, the classes "loaded", "loader", and
|
||||
:: "loading" provide loading spinner behavior on
|
||||
:: sending and receiving this form's POST request
|
||||
::
|
||||
:: <button class="bd1 br1 pr b1 hover loader">
|
||||
;button.bd1.br1.p2.b1.hover.loader
|
||||
:: <span class="loaded">Increment</span>
|
||||
;span.loaded: Increment
|
||||
:: <span class="loading">
|
||||
;span.loading
|
||||
:: import +loading sail from /lib/feather-icons
|
||||
;+ loading.feather-icons
|
||||
== :: </span>
|
||||
== :: </button>
|
||||
== :: </form>
|
||||
== :: </div>
|
@ -92,6 +92,9 @@ Here's the same thing in Shrubery.
|
||||
++ poke
|
||||
^- (set stud:neo)
|
||||
(sy %counter-diff ~)
|
||||
++ kids
|
||||
^- kids:neo
|
||||
*kids:neo
|
||||
++ deps
|
||||
^- deps:neo
|
||||
*deps:neo
|
||||
@ -101,11 +104,12 @@ Here's the same thing in Shrubery.
|
||||
+* state !<(number state-vase)
|
||||
++ init
|
||||
|= old=(unit pail:neo)
|
||||
^- ((list card:neo) pail:neo)
|
||||
^- (quip card:neo pail:neo)
|
||||
[~ (need old)]
|
||||
++ poke
|
||||
|= [=stud:neo vaz=vase]
|
||||
^- ((list card:neo) pail:neo)
|
||||
^- (quip card:neo pail:neo)
|
||||
?> =(%counter-diff stud)
|
||||
=/ act
|
||||
!<(counter-diff vaz)
|
||||
?> =(-.act %inc)
|
||||
@ -170,13 +174,13 @@ The `form` is where the Gall agent-like application logic lives. We only need tw
|
||||
:: unlike +on-init, potentially accept some injected initial state
|
||||
++ init
|
||||
|= old=(unit pail:neo)
|
||||
^- ((list card:neo) pail:neo)
|
||||
^- (quip card:neo pail:neo)
|
||||
!!
|
||||
::
|
||||
:: like +on-poke, run some logic when this shrub is poked
|
||||
++ poke
|
||||
|= [=stud:neo vaz=vase]
|
||||
^- ((list card:neo) pail:neo)
|
||||
^- (quip card:neo pail:neo)
|
||||
!!
|
||||
--
|
||||
```
|
||||
@ -220,6 +224,12 @@ There are lots of new types here which are flagged with the `:neo` suffix in cod
|
||||
^- (set stud:neo)
|
||||
(sy %counter-diff ~)
|
||||
::
|
||||
:: counter does not constrain the state/pokes of shrubs
|
||||
:: underneath it in the namespace
|
||||
++ kids
|
||||
^- kids:neo
|
||||
*kids:neo
|
||||
::
|
||||
:: counter has no dependencies
|
||||
++ deps
|
||||
^- deps:neo
|
||||
@ -229,8 +239,8 @@ There are lots of new types here which are flagged with the `:neo` suffix in cod
|
||||
++ form
|
||||
^- form:neo
|
||||
::
|
||||
:: the sample is populated with context like bowl, version number, and
|
||||
:: counter's current state
|
||||
:: the sample is populated with context like bowl,
|
||||
:: version number, and counter's current state
|
||||
|_ [=bowl:neo =aeon:neo =stud:neo state-vase=vase]
|
||||
::
|
||||
:: de-vase counter's state
|
||||
@ -243,13 +253,16 @@ There are lots of new types here which are flagged with the `:neo` suffix in cod
|
||||
:: pail:neo is a (pair stud:neo vase),
|
||||
:: like a cell of a mark and data
|
||||
|= old=(unit pail:neo)
|
||||
^- ((list card:neo) pail:neo)
|
||||
^- (quip card:neo pail:neo)
|
||||
[~ (need old)]
|
||||
::
|
||||
:: +poke, like +on-poke
|
||||
++ poke
|
||||
|= [=stud:neo vaz=vase]
|
||||
^- ((list card:neo) pail:neo)
|
||||
^- (quip card:neo pail:neo)
|
||||
::
|
||||
:: assert that the poke's stud is %counter-diff
|
||||
?> =(%counter-diff stud)
|
||||
::
|
||||
:: de-vase the poke
|
||||
=/ act
|
||||
@ -269,16 +282,17 @@ Once you've saved `/imp/counter.hoon` and the `/pro` files, run `|commit %base`
|
||||
|
||||
## Poking the shrub
|
||||
|
||||
A `card:neo` is a `(pair pith note)`.
|
||||
A `card:neo` is a `(pair pith note:neo)`. Data in Shrubbery is stored at its `pith`.
|
||||
|
||||
A `pith` is a `(list iota)`, and an `iota` is either a `term` or a head-tagged noun. For instance:
|
||||
* `/examples/counter/one` would be represented as `~[%examples %counter %one]`.
|
||||
* `/~sampel/examples/counter/one` would be represented as `~[[%p ~sampel] %examples %counter %one]`.
|
||||
* `/~sampel/examples/counter/1` would be represented as `~[[%p ~sampel] %examples %counter [%ud 1]]`.
|
||||
A `pith` is a `(list iota)`, and an `iota` is either a `term` or a head-tagged noun.
|
||||
|
||||
(You might also see a `pith` written in this irregular form `#/[p/our.bowl]/examples/counter/one`.)
|
||||
For instance:
|
||||
|
||||
Data in Shrubbery is stored by `pith`.
|
||||
- `/path/to/counter/one` would be represented as `~[%path %to %counter %one]`.
|
||||
- `/~sampel/path/to/counter/one` would be represented as `~[[%p ~sampel] %path %to %counter %one]`.
|
||||
- `/~sampel/path/to/counter/1` would be represented as `~[[%p ~sampel] %path %to %counter [%ud 1]]`.
|
||||
|
||||
(You might also see a `pith` written in this irregular form `#/[p/our.bowl]/path/to/counter/one`.)
|
||||
|
||||
A `note` is one of the four types of command any shrub will accept.
|
||||
|
||||
@ -291,20 +305,22 @@ A `note` is one of the four types of command any shrub will accept.
|
||||
==
|
||||
```
|
||||
|
||||
Let’s `%make` a shrub at path `/foo/bar` from the Dojo, giving it an initial state of `0`. We’ll explain the structure of the `%make` note in more detail in the Diary tutorial.
|
||||
Let’s `%make` a shrub at an arbitrary path `/path/to/counter` from the Dojo, giving it an initial state of `0`. We’ll explain the structure of the `%make` note in more detail in the Diary tutorial.
|
||||
|
||||
```
|
||||
:neo &neo-card [~[[%p our] %foo %bar] [%make %counter `[%number !>(0)] ~]]
|
||||
:neo &neo-card [~[[%p our] %path %to %counter] [%make %counter `[%number !>(0)] ~]]
|
||||
```
|
||||
|
||||
You should see `>> %make /foo/bar` in the Dojo if successful.
|
||||
You should see `>> /~zod/~/sys -> /~zod/path/to/counter: make` in the Dojo if successful.
|
||||
|
||||
Now we can now send a `%poke` to the counter shrub at this path.
|
||||
|
||||
```
|
||||
:neo &neo-card [~[[%p our] %foo %bar] [%poke [%counter-diff !>([%inc ~])]]]
|
||||
:neo &neo-card [~[[%p our] %path %to %counter] [%poke [%counter-diff !>([%inc ~])]]]
|
||||
```
|
||||
|
||||
You'll know the poke worked if you see `>> /~zod/~/sys -> /~zod/counter: %poke %counter-diff` in the Dojo. In the next section, we'll build a simple frontend that will enable us to view and modify the state of the shrub.
|
||||
|
||||
## Counter frontend in Sky
|
||||
|
||||
Shrubbery aims to be interface-agnostic. One part of that vision is `/con` files, which make it possible to convert data from one type to another. Here are Counter’s `/con` files.
|
||||
|
@ -39,13 +39,13 @@ Diary is an app that lets you write timestamped diary entries that the frontend
|
||||
~[[(welp here.bowl ~[da/id.act]) [%make %txt `txt/!>(txt.act) ~]]]
|
||||
%del-entry
|
||||
:_ diary/!>(state)
|
||||
~[[(welp here.bowl ~[da/id.act]) [%tomb ~]]]
|
||||
~[[(welp here.bowl ~[da/id.act]) [%cull ~]]]
|
||||
==
|
||||
--
|
||||
--
|
||||
```
|
||||
|
||||
Most of this should be legible after the first tutorial. The only new ideas are the `+kids` arm, the use of `bowl`, and `%tomb`.
|
||||
Most of this should be legible after the first tutorial. The only new ideas are the `+kids` arm, the use of `bowl`, and `%cull`.
|
||||
|
||||
## +kids
|
||||
|
||||
@ -115,9 +115,9 @@ Notice that the `src` in `bowl:neo` differs from `bowl:gall`. Here’s the new t
|
||||
==
|
||||
```
|
||||
|
||||
## Generating cards, tombstoning shrubs
|
||||
## Generating cards, culling shrubs
|
||||
|
||||
We covered `card:neo` in the Counter tutorial, but this is the first time we’re seeing one generated within a shrub. Diary takes two pokes: `%put-entry`, to create a new diary entry, and `%del-entry` to tombstone one.
|
||||
We covered `card:neo` in the Counter tutorial, but this is the first time we’re seeing one generated within a shrub. Diary takes two pokes: `%put-entry`, to create a new diary entry, and `%del-entry` to cull one.
|
||||
|
||||
Here’s the `+poke` arm of the Diary shrub, expanded with comments.
|
||||
|
||||
@ -177,14 +177,14 @@ Here’s the `+poke` arm of the Diary shrub, expanded with comments.
|
||||
:: return unchanged state
|
||||
:_ [%diary !>(state)]
|
||||
::
|
||||
:: send a %tomb note to /path/to/diary/<id>
|
||||
:: this will tombstone the diary entry,
|
||||
:: send a %cull note to /path/to/diary/<id>
|
||||
:: this will cull the diary entry,
|
||||
:: effectively deleting it from the namespace
|
||||
:~ :- %+ welp
|
||||
here.bowl
|
||||
~[[%da id.act]]
|
||||
^- note:neo
|
||||
[%tomb ~]
|
||||
[%cull ~]
|
||||
==
|
||||
==
|
||||
```
|
||||
@ -264,7 +264,7 @@ Like Counter, the Diary shrub just has two `/con` files to convert to and from a
|
||||
==
|
||||
==
|
||||
;button.p2.br1.fr.g2.b1.hover.fc.ac.jc.loader
|
||||
=onclick "alert('not yet implemented. no tombstoning?')"
|
||||
=onclick "alert('not yet implemented. no culling?')"
|
||||
;span.loaded
|
||||
;+ close.feather-icons
|
||||
==
|
||||
|
@ -1,80 +0,0 @@
|
||||
/@ number :: @ud
|
||||
/@ counter-diff :: [%inc ~]
|
||||
::
|
||||
:: outer core of a shrub: define state, pokes,
|
||||
:: dependencies, and kids
|
||||
^- kook:neo
|
||||
|%
|
||||
::
|
||||
:: the state of the counter is a %number, just a @ud
|
||||
:: a curb:neo is a constraint imposed on a shrub
|
||||
++ state
|
||||
^- curb:neo
|
||||
[%pro %number]
|
||||
::
|
||||
:: the set of pokes that counter
|
||||
:: takes only contains %counter-diff
|
||||
++ poke
|
||||
^- (set stud:neo)
|
||||
(sy %counter-diff ~)
|
||||
::
|
||||
::
|
||||
:: counter does not constrain the type and behaviour of
|
||||
:: its children; any shrub can be made below this shrub
|
||||
:: in the tree, they can have any state, kids, or pokes
|
||||
++ kids
|
||||
^- kids:neo
|
||||
*kids:neo
|
||||
::
|
||||
:: counter has no other shrubs as dependencies
|
||||
++ deps
|
||||
^- deps:neo
|
||||
*deps:neo
|
||||
::
|
||||
:: inner core of a shrub: business logic
|
||||
++ form
|
||||
^- form:neo
|
||||
:: treat this door's sample as boilerplate
|
||||
|_ [=bowl:neo =aeon:neo =pail:neo]
|
||||
::
|
||||
:: +init, like +on-init
|
||||
++ init
|
||||
::
|
||||
:: minimal +init, just returns the
|
||||
:: initial state passed in on %make
|
||||
|= old=(unit pail:neo)
|
||||
^- (quip card:neo pail:neo)
|
||||
[~ (need old)]
|
||||
::
|
||||
:: +poke, like +on-poke
|
||||
++ poke
|
||||
::
|
||||
:: a stud (e.g. %number or %counter-diff) is
|
||||
:: like a mark
|
||||
|= [=stud:neo vaz=vase]
|
||||
::
|
||||
:: return a (list card:neo) and a
|
||||
:: pail, which is a (pair stud vase)
|
||||
^- (quip card:neo pail:neo)
|
||||
::
|
||||
:: assert the stud of the pail (pair stud vase),
|
||||
:: which is the shrub's state given in the sample
|
||||
:: (technically unnecessary in this case, but good
|
||||
:: hygiene)
|
||||
?> =(p.pail %number)
|
||||
::
|
||||
:: de-vase the payload's vase through the number
|
||||
:: type; note the difference between the number
|
||||
:: type and the %number stud
|
||||
=/ state !<(number q.pail)
|
||||
::
|
||||
:: assert that the poke's stud is %counter-diff
|
||||
?> =(%counter-diff stud)
|
||||
=/ act
|
||||
!<(counter-diff vaz)
|
||||
?> =(-.act %inc)
|
||||
::
|
||||
:: return no cards and a pail
|
||||
[~ [%number !>(+(state))]]
|
||||
--
|
||||
--
|
@ -1 +0,0 @@
|
||||
,[%inc ~]
|
@ -1 +0,0 @@
|
||||
,@ud
|
Loading…
Reference in New Issue
Block a user