@@ -0,0 +1,514 @@
2+title = "Read and Subscribe"
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`.
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)
20+A `%warp` task looks like:
23+[%warp wer=ship rif=riff] :: internal file req
26+The `riff` part is the following structure:
29++$ riff [p=desk q=(unit rave)] :: request+desist
32+The `desk` is the desk in question. The `(unit rave)` is null to cancel a subscription, otherwise the `rave` is:
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
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.
48+We'll look at each of these in more detail later.
50+Clay responds to a `%mult` request with a sign containing a `%wris` tagged `gift:clay`, and the rest with a `%writ`.
52+A `%wris` gift looks like:
55+[%wris p=[%da p=@da] q=(set (pair care path))] :: many changes
58+A `%writ` gift looks like:
61+[%writ p=riot] :: response
67++$ riot (unit rant) :: response+complete
70+And the `rant` has the following structure:
73++$ rant :: response to request
74+ $: p=[p=care q=case r=desk] :: clade release book
80+The `cage` will contain the data you requested and its contents will vary depending on the kind of request and `care`.
82+Now we'll look at each of the `rave` request types in turn.
90+This is for reading a single file or directory immediately. The `mood` structure is:
93++$ mood [=care =case =path] :: request in desk
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.
98+The `case` specifies the desk revision and looks like:
101++$ case :: ship desk case spur
102+ $% [%da p=@da] :: date
103+ [%tas p=@tas] :: label
104+ [%ud p=@ud] :: number
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`.
117+=, strand=strand:spider
122+=/ uarg !< (unit path) arg
124+ (strand-fail:strand %no-arg ~)
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)
134+Save this thread to `/ted/read-file.hoon`, `|commit %home` and run it like:
137+> -read-file /gen/hood/hi/hoon
140+You should see something like this as the output:
144+ [ p=[p=%x q=[%da p=~2021.4.17..10.55.30..ed01] r=%home]
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
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:
161+> -read-file /gen/hood/hi/hoon
163+ [ p=[p=%u q=[%da p=~2021.4.26..06.51.32..56a7] r=%home]
165+ r=[p=%flag q=[#t/?(%.y %.n) q=0]]
170+Here's a breakdown of the task we sent:
172+
177+[%next =mood] :: await next version
180+This subscribes to the next version of the specified file. The `mood` structure is the same as described in the `%sing` example.
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`.
189+=, strand=strand:spider
194+=/ uarg !< (unit path) arg
196+ (strand-fail:strand %no-arg ~)
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)
205+ %- (slog leaf+"{(spud path)} deleted!" ~)
207+%- (slog leaf+"{(spud q.u.riot)} changed!" ~)
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.
213+Save this in `ted/sub-next.hoon`, `|commit %home` and run like:
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:
225+ [ p=[p=%x q=[%ud p=106] r=%home]
227+ r=[p=%txt q=[#t/*'' q=[7.303.014 0]]]
231++ /~zod/home/106/foo/txt
234+As you can see, the `riot` includes a `cage` with the data of `/foo/txt` due to our use of an `%x` care.
236+Now run the thread again, and this time delete the file in unix and again `|commit %home` in the dojo. You should see:
243+- /~zod/home/107/foo/txt
246+You can see the `riot` is just `~` due to the file being deleted.
248+Here's a breakdown of the task we sent:
250+
255+[%mult =mool] :: next version of any
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.
260+The behaviour with respect to requesting old cases is the same as explained in the [`%next`](#next) section above.
262+The `mool` specified in the request is this structure:
265++$ mool [=case paths=(set (pair care path))] :: requests in desk
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.
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.
279+=, strand=strand:spider
284+=/ files=(set (pair care:clay path))
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
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:
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
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.
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.
312+Lastly, delete `foo.txt` and `|commit %home`. You should see something like:
317+[%wris p=[%da p=~2021.4.27..06.15.03..0da4] q={[p=%x q=/foo/txt]}]
318+- /~zod/home/153/foo/txt
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.
323+Here's a breakdown of the task we sent:
325+
330+[%many track=? =moat] :: track range
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.
335+If the `track` is `%.y` it will just return a `%writ` like:
338+[%writ p=[~ [p=[p=%w q=[%ud p=256] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]
341+...that merely informs you of a change. If you want the actual data you'll have to request it separately.
343+If the `track` is `%.n`, the `cage` of the `%writ` will contain a `nako`, which looks like:
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.
352++$ nako :: subscription state
353+ $: gar=(map aeon tako) :: new ids
355+ lar=(set yaki) :: new commits
356+ bar=(set plop) :: new content
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.
362+The `moat` in the `%many` is the following structure:
365+ +$ moat [from=case to=case =path] :: change range
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.
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.
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:
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.
387+=, strand=strand:spider
392+ %- (main-loop:strandio ,~)
395+ ;< res=(pair wire sign-arvo)
397+ ((handle:strandio ,(pair wire sign-arvo)) take-sign-arvo:strandio)
406+=/ uarg !< (unit path) arg
408+ (strand-fail:strand %no-arg ~)
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
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:
423++ /~zod/home/260/bar/txt
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:
432+ q=[%clay [%writ p=[~ [p=[p=%w q=[%ud p=261] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]]
434++ /~zod/home/261/foo/txt
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.
439+Now try adding `baz.txt`:
445+ q=[%clay [%writ p=[~ [p=[p=%w q=[%ud p=262] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]]
447++ /~zod/home/262/baz/txt
450+Now wait until the three minutes is up and try making a change, for example deleting `baz.txt`:
456+ q=[%clay [%writ p=[~ [p=[p=%w q=[%ud p=264] r=%home] q=/ r=[p=%null q=[#t/@n q=0]]]]]]
458+[p=/many q=[%clay [%writ p=~]]]
459+- /~zod/home/263/baz/txt
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.
464+Run `:spider|kill` to stop the thread.
466+Here's a breakdown of the task we sent:
468+
470+## Cancel Subscription
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.
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).
483+=, strand=strand:spider
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)
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:
505++ /~zod/home/266/foo/txt
508+As you can see we've received no `%writ`. We can thus conclude the subscription has successfully been cancelled.
510+Run `:spider|kill` to stop the thread.
512+Here's a breakdown of the task we sent:
514+