on master help sign in
Add reads & subs doc
commit5d6ae085661ab02303a1f8d00a12f14a070c4fea authortinnus-napbus <admin@tinnus-napbus.xyz> dateFri 7 May 2021, 21:34 (5 years ago) parentf1848d4
3 files changed+522
arvo/clay/_index.md+4 arvo/clay/clay.md+4 arvo/clay/read.md+514
arvo/clay/_index.md+4
@@ -26,3 +26,7 @@ Explanations of the many data types found throughout Clay.
2626[Scry Reference](@/docs/arvo/clay/scry.md)
2727
2828Reference for Clay's various scry endpoints.
29+
30+[Read and Subscribe](@/docs/arvo/clay/read.md)
31+
32+How to read files and subscribe for changes.
arvo/clay/clay.md+4
@@ -55,3 +55,7 @@ Explanations of the many data types found throughout Clay. (Up to date as of Apr
5555[Scry Reference](@/docs/arvo/clay/scry.md)
5656
5757Reference for Clay's various scry endpoints.
58+
59+[Read and Subscribe](@/docs/arvo/clay/read.md)
60+
61+How to read files and subscribe for changes.
arvo/clay/read.mdnew file+514
@@ -0,0 +1,514 @@
1++++
2+title = "Read and Subscribe"
3+weight = 6
4+template = "doc.html"
5++++
6+
7+Here we'll look at reading files and subscribing to changes. We'll do this by sending clay a `card:agent:gall` containing a `%warp` tagged `task:clay`.
8+
9+## Contents
10+
11+- [Introduction](#introduction)
12+- [%sing](#sing) - Read a single file or directory.
13+- [%next](#next) - Subscribe for the next change to a file or directory.
14+- [%mult](#mult) - Subscribe for the next change to a set of files and/or directories.
15+- [%many](#many) - Track changes to a desk for the specified range of revisions.
16+- [Cancel Subsciption](#cancel-subscription)
17+
18+## Introduction
19+
20+A `%warp` task looks like:
21+
22+```hoon
23+[%warp wer=ship rif=riff] :: internal file req
24+```
25+
26+The `riff` part is the following structure:
27+
28+```hoon
29++$ riff [p=desk q=(unit rave)] :: request+desist
30+```
31+
32+The `desk` is the desk in question. The `(unit rave)` is null to cancel a subscription, otherwise the `rave` is:
33+
34+```hoon
35++$ rave :: general request
36+ $% [%sing =mood] :: single request
37+ [%next =mood] :: await next version
38+ [%mult =mool] :: next version of any
39+ [%many track=? =moat] :: track range
40+ == ::
41+```
42+
43+- `%sing` - Read a single file or directory.
44+- `%next` - Subscribe for the next change to a file or directory.
45+- `%mult` - Subscribe for the next change to a set of files and/or directories.
46+- `%many` - Track changes to a desk for the specified range of revisions.
47+
48+We'll look at each of these in more detail later.
49+
50+Clay responds to a `%mult` request with a sign containing a `%wris` tagged `gift:clay`, and the rest with a `%writ`.
51+
52+A `%wris` gift looks like:
53+
54+```hoon
55+[%wris p=[%da p=@da] q=(set (pair care path))] :: many changes
56+```
57+
58+A `%writ` gift looks like:
59+
60+```hoon
61+[%writ p=riot] :: response
62+```
63+
64+The `riot` is just:
65+
66+```hoon
67++$ riot (unit rant) :: response+complete
68+```
69+
70+And the `rant` has the following structure:
71+
72+```hoon
73++$ rant :: response to request
74+ $: p=[p=care q=case r=desk] :: clade release book
75+ q=path :: spur
76+ r=cage :: data
77+ == ::
78+```
79+
80+The `cage` will contain the data you requested and its contents will vary depending on the kind of request and `care`.
81+
82+Now we'll look at each of the `rave` request types in turn.
83+
84+## %sing
85+
86+```hoon
87+[%sing =mood]
88+```
89+
90+This is for reading a single file or directory immediately. The `mood` structure is:
91+
92+```hoon
93++$ mood [=care =case =path] :: request in desk
94+```
95+
96+The `care` is `%x`, `%y` or whatever else. This will determine what you can read and what type of data will be returned. See the type documentation and scry documentation for details on the various cares.
97+
98+The `case` specifies the desk revision and looks like:
99+
100+```hoon
101++$ case :: ship desk case spur
102+ $% [%da p=@da] :: date
103+ [%tas p=@tas] :: label
104+ [%ud p=@ud] :: number
105+ == ::
106+```
107+
108+You can use whichever you prefer. The `path` will usually be a path to a file or directory like `/gen/hood/hi/hoon` but may be something else depending on the `care`.
109+
110+Example:
111+
112+**`read-file.hoon`**
113+
114+```hoon
115+/- spider
116+/+ strandio
117+=, strand=strand:spider
118+^- thread:spider
119+|= arg=vase
120+=/ m (strand ,vase)
121+^- form:m
122+=/ uarg !< (unit path) arg
123+?~ uarg
124+ (strand-fail:strand %no-arg ~)
125+=/ =path u.uarg
126+;< =bowl:strand bind:m get-bowl:strandio
127+=/ =task:clay [%warp our.bowl %home ~ %sing %x da+now.bowl path]
128+=/ =card:agent:gall [%pass /sing %arvo %c task]
129+;< ~ bind:m (send-raw-card:strandio card)
130+;< =riot:clay bind:m (take-writ:strandio /sing)
131+(pure:m !>(riot))
132+```
133+
134+Save this thread to `/ted/read-file.hoon`, `|commit %home` and run it like:
135+
136+```hoon
137+> -read-file /gen/hood/hi/hoon
138+```
139+
140+You should see something like this as the output:
141+
142+```hoon
143+[ ~
144+ [ p=[p=%x q=[%da p=~2021.4.17..10.55.30..ed01] r=%home]
145+ q=/gen/hood/hi/hoon
146+ r
147+ [ p=%hoon
148+ q
149+ [ #t/@
150+ q
151+3.548.750.706.400.251.607.252.023.288.575.526.190.856.734.474.077.821.289.791.377.301.707.878.697.553.411.219.689.905.949.957.893.633.811.025.757.107.990.477.902.858.170.125.439.223.250.551.937.540.468.638.902.955.378.837.954.792.031.592.462.617.422.136.386.332.469.076.584.061.249.923.938.374.214.925.312.954.606.277.212.923.859.309.330.556.730.410.200.952.056.760.727.611.447.500.996.168.035.027.753.417.869.213.425.113.257.514.474.700.810.203.348.784.547.006.707.150.406.298.809.062.567.217.447.347.357.039.994.339.342.906
152+ ]
153+ ]
154+ ]
155+]
156+```
157+
158+The `cage` in the `riot` contains the file's data due to our use of an `%x` care. It needn't be an `%x` though. If we change it to `%u`, for example, we'll get a `?` cage instead:
159+
160+```hoon
161+> -read-file /gen/hood/hi/hoon
162+[ ~
163+ [ p=[p=%u q=[%da p=~2021.4.26..06.51.32..56a7] r=%home]
164+ q=/gen/hood/hi/hoon
165+ r=[p=%flag q=[#t/?(%.y %.n) q=0]]
166+ ]
167+]
168+```
169+
170+Here's a breakdown of the task we sent:
171+
172+![read file diagram](https://pub.m.tinnus-napbus.xyz/read-file.png "read file diagram")
173+
174+## %next
175+
176+```hoon
177+[%next =mood] :: await next version
178+```
179+
180+This subscribes to the next version of the specified file. The `mood` structure is the same as described in the `%sing` example.
181+
182+If you subscribe to the current case of the desk, clay will not respond until the file changes. If you subscribe to a previous case of the desk and the file has changed in between then and now, it will immediately return the first change it comes across in that range. For example, if you're currently at case `100`, subscribe to case `50` and the file in question has been modified at both `60` and `80`, clay will immediately return the version of the file at case `60`.
183+
184+**`sub-next.hoon`**
185+
186+```hoon
187+/- spider
188+/+ strandio
189+=, strand=strand:spider
190+^- thread:spider
191+|= arg=vase
192+=/ m (strand ,vase)
193+^- form:m
194+=/ uarg !< (unit path) arg
195+?~ uarg
196+ (strand-fail:strand %no-arg ~)
197+=/ =path u.uarg
198+;< =bowl:strand bind:m get-bowl:strandio
199+=/ =task:clay [%warp our.bowl %home ~ %next %x da+now.bowl path]
200+=/ =card:agent:gall [%pass /next %arvo %c task]
201+;< ~ bind:m (send-raw-card:strandio card)
202+;< =riot:clay bind:m (take-writ:strandio /next)
203+~& riot
204+?~ riot
205+ %- (slog leaf+"{(spud path)} deleted!" ~)
206+ (pure:m !>(~))
207+%- (slog leaf+"{(spud q.u.riot)} changed!" ~)
208+(pure:m !>(~))
209+```
210+
211+This thread will subscribe to the next version of the file given as an argument. It will print the `riot` it gets back from clay and will also print a message saying whether the file's been deleted or changed.
212+
213+Save this in `ted/sub-next.hoon`, `|commit %home` and run like:
214+
215+```hoon
216+> -sub-next /foo/txt
217+```
218+
219+Now, in unix, create a file called `foo.txt` in the `ted` directory of your ship. In the dojo, hit backspace to disconnect the thread from the dojo prompt and run `|commit %home`. You should see something like:
220+
221+```hoon
222+> |commit %home
223+>=
224+[ ~
225+ [ p=[p=%x q=[%ud p=106] r=%home]
226+ q=/foo/txt
227+ r=[p=%txt q=[#t/*'' q=[7.303.014 0]]]
228+ ]
229+]
230+/foo/txt changed!
231++ /~zod/home/106/foo/txt
232+```
233+
234+As you can see, the `riot` includes a `cage` with the data of `/foo/txt` due to our use of an `%x` care.
235+
236+Now run the thread again, and this time delete the file in unix and again `|commit %home` in the dojo. You should see:
237+
238+```hoon
239+> |commit %home
240+>=
241+~
242+/foo/txt deleted!
243+- /~zod/home/107/foo/txt
244+```
245+
246+You can see the `riot` is just `~` due to the file being deleted.
247+
248+Here's a breakdown of the task we sent:
249+
250+![subscribe next diagram](https://pub.m.tinnus-napbus.xyz/sub-next.png "subscribe next diagram")
251+
252+## %mult
253+
254+```hoon
255+[%mult =mool] :: next version of any
256+```
257+
258+This subscribes to the next version of a set of files or directories. Clay will only send a single response, and it will send it when *any* of the specified files change. For example, if you subscribe to both `/foo/txt` and `/bar/txt`, and only `/foo/txt` changes, clay will send a response indicating a change to `/foo/txt`. If `/bar/txt` changes subsequently, it will not tell you. If more than one file changes at once, it will tell you about each of the changes in the one response.
259+
260+The behaviour with respect to requesting old cases is the same as explained in the [`%next`](#next) section above.
261+
262+The `mool` specified in the request is this structure:
263+
264+```hoon
265++$ mool [=case paths=(set (pair care path))] :: requests in desk
266+```
267+
268+You can use a different `care` for each of the files specified by the `path` if you like. Significantly, the `care` will determine whether clay sends a response for a given change. For example, if you subscribe to an existing `/foo/txt` with a `%u` care and `/foo/txt` is modified but isn't deleted, clay will *not* tell you. However, if you subscribe with an `%x` care, it *will* tell you.
269+
270+Example:
271+
272+This thread will subscribe to `/foo/txt` with an `%x` care and `/bar/txt` with a `%u` care. It will print out the `%wris` it gets back from clay.
273+
274+**`sub-mult.hoon`**
275+
276+```hoon
277+/- spider
278+/+ strandio
279+=, strand=strand:spider
280+^- thread:spider
281+|= arg=vase
282+=/ m (strand ,vase)
283+^- form:m
284+=/ files=(set (pair care:clay path))
285+ %- sy :~
286+ [%x /foo/txt]
287+ [%u /bar/txt]
288+ ==
289+;< =bowl:strand bind:m get-bowl:strandio
290+=/ =task:clay [%warp our.bowl %home ~ %mult da+now.bowl files]
291+=/ =card:agent:gall [%pass /mult %arvo %c task]
292+;< ~ bind:m (send-raw-card:strandio card)
293+;< response=(pair wire sign-arvo) bind:m take-sign-arvo:strandio
294+~& +.q.response
295+(pure:m !>(~))
296+```
297+
298+Save the above to `ted/sub-mult.hoon`, `|commit %home` and run with `-sub-mult`. Now, create `foo.txt` and `bar.txt` in your home directory, hit backspace in the dojo to disconnect the thread and run `|commit %home`. You should see something like:
299+
300+```hoon
301+> |commit %home
302+>=
303+[%wris p=[%da p=~2021.4.27..06.07.08..5ec4] q={[p=%u q=/bar/txt] [p=%x q=/foo/txt]}]
304++ /~zod/home/151/foo/txt
305++ /~zod/home/151/bar/txt
306+```
307+
308+You'll notice that, unlike a `%writ`, the `%wris` doesn't give you the data. It merely tells gives you the cares and paths of the files that changed. If you need to actually get the data, you can just scry or send a request for the files in question.
309+
310+Now, run the thread again, open `bar.txt` in an editor, modify its contents, save it and `|commit %home`. You'll notice you didn't receive a `%wris`. This is because we subscribed to `/bar/txt` with `%u` care and its existence didn't change.
311+
312+Lastly, delete `foo.txt` and `|commit %home`. You should see something like:
313+
314+```hoon
315+> |commit %home
316+>=
317+[%wris p=[%da p=~2021.4.27..06.15.03..0da4] q={[p=%x q=/foo/txt]}]
318+- /~zod/home/153/foo/txt
319+```
320+
321+As you can see, a relevant change to any of the subscribed files will trigger a response, not just when all of them change.
322+
323+Here's a breakdown of the task we sent:
324+
325+![subscribe mult diagram](https://pub.m.tinnus-napbus.xyz/sub-mult.png "subscribe mult diagram")
326+
327+## %many
328+
329+```hoon
330+[%many track=? =moat] :: track range
331+```
332+
333+This subscribes to all changes to a desk for the specified range of cases. Note that you're unlikely to use this directly, it's mostly used implicitly if you make a `%sing` or `%next` request with a `%v` care to a foreign desk. It's basically what the system uses to keep in sync with a foreign desk. Regardless, we'll have a look at it for completeness.
334+
335+If the `track` is `%.y` it will just return a `%writ` like:
336+
337+```hoon
338+[%writ p=[~ [p=[p=%w q=[%ud p=256] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]
339+```
340+
341+...that merely informs you of a change. If you want the actual data you'll have to request it separately.
342+
343+If the `track` is `%.n`, the `cage` of the `%writ` will contain a `nako`, which looks like:
344+
345+```hoon
346+:: New desk data.
347+::
348+:: Sent to other ships to update them about a particular desk. Includes a map
349+:: of all new aeons to hashes of their commits, the most recent aeon, and sets
350+:: of all new commits and data.
351+::
352++$ nako :: subscription state
353+ $: gar=(map aeon tako) :: new ids
354+ let=aeon :: next id
355+ lar=(set yaki) :: new commits
356+ bar=(set plop) :: new content
357+ == ::
358+```
359+
360+As the comment explains, it contains all relevant data for a changes to a desk between what you have and the case requested. It is very large and fairly complicated. The `nako` structure is defined in the `clay.hoon` source file itself rather than in `lull.hoon` or elsewhere since you're unlikely to work with it yourself.
361+
362+The `moat` in the `%many` is the following structure:
363+
364+```hoon
365+ +$ moat [from=case to=case =path] :: change range
366+```
367+
368+The `from` and `to` fields specify the range of cases for which to subscribe. The range is *inclusive*. It can be specified by date or by revision number, whichever you prefer.
369+
370+The `path` is a path to a file or directory. If it's `~` it refers to the root of the desk in question. This lets you say "only inform me of changes to the desk if the specified file or directory exists". If it doesn't exist, clay will send you nothing.
371+
372+When you reach the end of the subscribed range of cases, clay will send you a `%writ` with a null `riot` to inform you the subscription has ended like:
373+
374+```hoon
375+[%writ p=~]
376+```
377+
378+Example:
379+
380+This thread will subscribe to changes to your `%home` desk for the next three minutes. The `track` is `%.y` so it will only inform you of changes, not send the full `nako`. It will only get updates if the specified file exists. It contains a `main-loop` that will take an arbitrary number of signs and print them out in the dojo. Since it never ends, you'll need to stop it with the `:spider|kill` command in the dojo.
381+
382+**`sub-many.hoon`**
383+
384+```hoon
385+/- spider
386+/+ strandio
387+=, strand=strand:spider
388+|%
389+++ take-sign-loop
390+ =/ m (strand ,~)
391+ ^- form:m
392+ %- (main-loop:strandio ,~)
393+ :~ |= ~
394+ ^- form:m
395+ ;< res=(pair wire sign-arvo)
396+ bind:m
397+ ((handle:strandio ,(pair wire sign-arvo)) take-sign-arvo:strandio)
398+ ~& res
399+ (pure:m ~)
400+ ==
401+--
402+^- thread:spider
403+|= arg=vase
404+=/ m (strand ,vase)
405+^- form:m
406+=/ uarg !< (unit path) arg
407+?~ uarg
408+ (strand-fail:strand %no-arg ~)
409+=/ =path u.uarg
410+;< =bowl:strand bind:m get-bowl:strandio
411+=/ =task:clay [%warp our.bowl %home ~ %many %.y da+now.bowl da+(add ~m3 now.bowl) path]
412+=/ =card:agent:gall [%pass /many %arvo %c task]
413+;< ~ bind:m (send-raw-card:strandio card)
414+;< ~ bind:m take-sign-loop
415+(pure:m !>(~))
416+```
417+
418+Make sure `foo.txt` doesn't exist in the root of your `%home` desk. Save this to `ted/sub-many.hoon`, `|commit %home`, run it like `-sub-many /foo/txt`, and hit backspace in the dojo to free up the dojo prompt. Now, add a file called `bar.txt` to your desk and `|commit %home`. You should see something like:
419+
420+```hoon
421+> |commit %home
422+>=
423++ /~zod/home/260/bar/txt
424+```
425+
426+Notice you've received no `%writ` from clay. This is because `/foo/txt` doesn't exist. Now, create `foo.txt` and `|commit %home` again. You should see:
427+
428+```hoon
429+> |commit %home
430+>=
431+[ p=/many
432+ q=[%clay [%writ p=[~ [p=[p=%w q=[%ud p=261] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]]
433+]
434++ /~zod/home/261/foo/txt
435+```
436+
437+Now that `/foo/txt` exists it will inform you of updates. Note that if you delete `/foo/txt` again it will again stop sending updates.
438+
439+Now try adding `baz.txt`:
440+
441+```hoon
442+> |commit %home
443+>=
444+[ p=/many
445+ q=[%clay [%writ p=[~ [p=[p=%w q=[%ud p=262] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]]
446+]
447++ /~zod/home/262/baz/txt
448+```
449+
450+Now wait until the three minutes is up and try making a change, for example deleting `baz.txt`:
451+
452+```hoon
453+> |commit %home
454+>=
455+[ p=/many
456+ q=[%clay [%writ p=[~ [p=[p=%w q=[%ud p=264] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]]
457+]
458+[p=/many q=[%clay [%writ p=~]]]
459+- /~zod/home/263/baz/txt
460+```
461+
462+You can see that along with the normal `%writ` it's also sent a second `%writ` with a null `riot` to indicate the subscription has ended. This is because it has now passed the end of the range of cases to which you subscribed.
463+
464+Run `:spider|kill` to stop the thread.
465+
466+Here's a breakdown of the task we sent:
467+
468+![subscribe many diagram](https://pub.m.tinnus-napbus.xyz/sub-many.png "subscribe many diagram")
469+
470+## Cancel Subscription
471+
472+Here we'll look at cancelling a subscription. It's very simple, you just send a `%warp` with a null `(unit rave)` in the `riff`. Clay will cancel the subscription based on the `wire`. The request is exactly the same regardless of which type of `rave` you subscribed with originally.
473+
474+Example:
475+
476+This thread will subscribe to the `%next` version of `/foo/txt`, then immediately cancel the subscription and wait for a response to print (which it will never receive).
477+
478+**`stop-sub.hoon`**
479+
480+```hoon
481+/- spider
482+/+ strandio
483+=, strand=strand:spider
484+^- thread:spider
485+|= arg=vase
486+=/ m (strand ,vase)
487+^- form:m
488+;< =bowl:strand bind:m get-bowl:strandio
489+=/ =task:clay [%warp our.bowl %home ~ %next %x da+now.bowl /foo/txt]
490+=/ =card:agent:gall [%pass /next %arvo %c task]
491+;< ~ bind:m (send-raw-card:strandio card)
492+=. task [%warp our.bowl %home ~]
493+=. card [%pass /next %arvo %c task]
494+;< ~ bind:m (send-raw-card:strandio card)
495+;< =riot:clay bind:m (take-writ:strandio /next)
496+~& riot
497+(pure:m !>(~))
498+```
499+
500+Save the above to `ted/stop-sub.hoon`, `|commit %home`, run it with `-stop-sub` and hit backspace to detach it from the dojo prompt. Now, add `foo.txt` to the root of your `%home` desk and `|commit %home`. You should see:
501+
502+```hoon
503+> |commit %home
504+>=
505++ /~zod/home/266/foo/txt
506+```
507+
508+As you can see we've received no `%writ`. We can thus conclude the subscription has successfully been cancelled.
509+
510+Run `:spider|kill` to stop the thread.
511+
512+Here's a breakdown of the task we sent:
513+
514+![cancel subscription diagram](https://pub.m.tinnus-napbus.xyz/stop-sub.png "cancel subscription diagram")