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:
bonbud-macryg 2024-06-27 16:42:49 +01:00 committed by GitHub
parent 1b6e964700
commit 2fa9eff190
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 43 additions and 192 deletions

View File

@ -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 ~]

View File

@ -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>

View File

@ -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.
==
```
Lets `%make` a shrub at path `/foo/bar` from the Dojo, giving it an initial state of `0`. Well explain the structure of the `%make` note in more detail in the Diary tutorial.
Lets `%make` a shrub at an arbitrary path `/path/to/counter` from the Dojo, giving it an initial state of `0`. Well 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 Counters `/con` files.

View File

@ -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`. Heres 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 were 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 were seeing one generated within a shrub. Diary takes two pokes: `%put-entry`, to create a new diary entry, and `%del-entry` to cull one.
Heres the `+poke` arm of the Diary shrub, expanded with comments.
@ -177,14 +177,14 @@ Heres 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
==

View File

@ -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))]]
--
--

View File

@ -1 +0,0 @@
,[%inc ~]

View File

@ -1 +0,0 @@
,@ud