This guide is focussed on storing application state using the `%gall` vane. To show off how we store and distribute data in Urbit we're going to examine a simple webapp. Some of the material here expects that you have looked over the [`%ford` guide](). If you haven't, it's a good idea to start there. There's also more information in the [`%gall` overview]() and [`%gall` commentary]() but it's not necesarry that you read those before going forward. One important thing to keep in mind is that `%gall` services aren't 'started' or 'stopped' as in a unix system. When your files are copied in they are compiled and begin running immediately and permanently. `%gall` services simply wake up when certain events happen. If you need to make updates to the structure of your stored data, you write connector functions or (when developing) just throw your existing data away. We'll see examples of how this works, just keep in mind that when we talk about a `%gall` 'service' it has no concept of 'running'. Going forward we'll refer to the directory you cloned the repo in as `/$URB_DIR` and assume that your pier is listening for HTTP connections on port `8080`. 1. Get the code. Clone the GitHub repository and move the files into your `/main` desk, under the corresponding paths. You will need four files: - /main/app/lead/core.hook - /main/pub/lead/hymn.hook - /main/pub/lead/src/main.css - /main/pub/lead/src/main.js When everything is in place, try it: http://localhost:8080/gen/main/pub/lead/ That URL should render a page and be self explanatory. Try adding names to the leaderboard and incrementing their scores. It's also fun to open a few tabs and watch how the state gets updated simultaneously. 2. How is the code structured? In our `%ford` guide we generated pages by defining all of their possible states, but we didn't exactly store any data. When building applications on top of Urbit we think of them as existing in two natural parts: page resources and state services. Effectively, we think of any Urbit app talking to the web as a single page app whose resources are generated by `%ford` which talks to a `%gall` service if it needs to persist any state. Let's look more closely at the specifics in this simple app. When we load our page, we render the contents of our `/main/pub/lead/hymn.hook`. This file should look familiar as [`++sail`](). Our `hymn.hook` file writes the basic HTML elements to the page, and pulls in our supporting CSS and JavaScript resources. Our application-specific resources are stored in `/main/pub/lead/src/`. `/main/pub/lead/src/main.css` simply produces the page layout, while `/main/pub/lead/src/main.js` updates the page and sends data. We also use two utility scripts: `/gop/hart.js` and `/gen/main/lib/urb.js`. These are the standard libraries for handling data transfer from a browser to Urbit, and are very frequently used. `hart.js` handles the page heartbeat, making regular AJAX requests so we can keep track of subscribers, and `urb.js` offers a more complete set of helper functions. `urb.js` depends on `hart.js`, so that's why `hart.js` always goes in the ``. For complete documentation, check out the [`urb.js` reference](). Our application state is stored and distributed to connected clients by `/main/app/lead/core.hook`. Let's take a closer look at how that works. At the top of our `core.hook` we have: /? 314 :: need urbit 314 This should be familiar from the `%ford` guide. Here we're requiring that this code run on an Urbit ship where `(lte zuse 314)` is `yes`. In this `core.hook` we only use one `%ford` rune, but this is where we would also pull in any dependencies we might have or use other [`/` runes](). Below our `/?` you can see that our code is divided into two sections: a [`|%`]() where we define our models, and a [`|_`]() where we define the body of our program. We'll look at these more closely one at a time. 3. How is our state stored? In `/main/app/lead/core.hook`: ++ axle $% [%0 p=(map ,@t ,@ud)] == is the first arm inside our leading `|%` that's important to notice. `++axle` defines the tile for our state. By convention we store our state as a [`$%`](), or labelled cases. We assume that our state can be versioned, so we want its model to be one of many tagged cases. This makes it possible to migrate our state to a new version of the service. Since this is the first version of our app, we tag our state with `%0`. In this simple application we're keeping track of pairs of names to scores, and we define that here as `(map ,@t ,@ud)`. You can think of this kind of like an associative array of strings to numbers, or an object with string keys and numeric values. When we use `++axle` to define the type of our state it's kind of like declaring a schema definition. There's no secondary data storage layer. Since `%gall` services run permanently your data persists as normal application state. We use tiles the way we normally would to declare the type of data that we're passing around. Looking ahead, you can see that our main `|_` takes a `++axle` as part of its sample. Let's look at how that core actually works, to get a sense of what our application is doing. 4. Where do requests go? In `/main/app/lead/core.hook`: ++ peer |= [ost=bone you=ship pax=path] ^- [(list move) _+>] ?~ pax [[ost %give %rust %json vat-json]~ +>.$] :_ +>.$ :_ ~ ?+ -.pax =- [ost %give %mean -] `[%not-found [%leaf "you need to specify a path"]~] %data =- [ost %give %rush %json -] (joba %conn %b &) == is the most important arm to look at first. `++peer` is one of the predefined arms that `%gall` calls when certain events happen. You can find them all in the [`%gall` overview](). We 'get a `++peer`' when we get either a HTTP request, or a subscription request. Each time this happens our main `|_` is populated with a `++hide` and our current `++axle` in its sample and `++peer` gets passed three things: `[ost=bone you=ship pax=path]`. The sample in the `|_` that contains `++peer` is our application state and all of the contained arms have access to that sample as part of their context. To change the state we simply produce a new context with changed values. Let's look at each of these parts of our context and the sample in `++peer`. `++hide`, labelled `hid` in peer's context, gives us some information about the `++request` being passed in. You can look at the specifics in the [`%arvo` `++models`](), but for our purposes we can think of it simply as request metadata. `++axle`, labelled as `vat` in peer's context, should be familiar from the discussion in the previous step. `ost` is a `++bone`, or an identifier for an `%arvo` duct. 'Duct' is actually a pretty good word for what a ++duct does. Informally, when an event is processed in `%arvo` we patch together our requisite computations with `++ducts`. For example, when we get a network packet, parse it, pass it to the webserver, and then pass it to the application framework we use a `++duct` to make all those connections. In `++peer` our ost just identifies the incoming request by number. We don't have access to the connecting `++duct`, but we use `ost` in the effects we produce so our responses are correctly coupled to the incoming request. `you` is a `++ship`, which is just a [`@p`]() or a phonemic string like `~tasfyn- partyv`. `%eyre` does some work to figure out who this is, or uses a submarine name if it can't be determined. You can read more about how we parse identities in `%eyre` in the [`%eyre` reference](). `pax` is a `++path`, or a list of `@ta`. In Hoon we most often write paths as you would expect, `/something/like/this`. In `%gall` services requests come in on a specific path, like `/data` or `/`. `++peer`, as with any arm that handles events, must produce a pair of a `(list ++move)` and our context, with any intended changes. In this peer we handle two cases, when `pax` is empty, or `~`, when our `pax` is `/data`. We throw an error if `pax` is anything else. 5. What exactly is a list of moves? Try pointing your browser at: http://localhost:8082/lead/ to see our response when `pax` in `++peer` is `~`. In our case we use this URL to load the initial state of the application as JSON. This is produced by the line `[[ost %give %rust %json vat-json]~ +>.$]` which produces a single `++move`, and our local context. Let's look more closely at our `++move`. ++ move ,[p=bone q=[%give gift]] :: output operation From our prior discussion we're familiar with a `++bone`, and `++gift` is defined right above in `core.hook`: ++ gift :: output action $% [%rust gilt] :: total update [%rush gilt] :: partial update [%mean (unit (pair term (list tank)))] :: Error, maybe w/ msg [%nice ~] :: Response message == :: Which clearly depends on `++gilt`: ++ gilt :: subscription frame $% [%hymn p=manx] :: html tree [%json p=json] :: json == :: `++gift` defines the possible actions we can take in the moves that we produce. We can send either partial or total updates with `%rush` or `%rust` respectively. We can also send either an error, `%mean` or default acknowledgement, `%nice`. Returning to our original `++move`, `[ost %give %rust %json vat-json]` we can now read it as 'send a total update with `++vat-json` as `++json`'. `++vat-json` simply takes our `(map @t @ud)` and turns it in to JSON. Looking at the remainer of `++peer` we can see that it is mostly control-flow that produces a `%mean` if our `pax` is not matched, and a `%rush` if our `pax` is `%data`. We'll revisit this `%data` path later on. 5. How do we change our state? All of our state changes happen in `++poke-json`. Incoming messages are handled by `++poke` arms in `%gall` services. If an incoming message has a `%logo` it is appeneded after a `-`. Messages from the web are often sent as JSON, so `++poke- json` is common for services that face the web. Let's walk through this part: =. p.vat (~(put by p.vat) newl) :_ +>.$ :* [ost %give %nice ~] (deliver %upd-lead (joba -.newl [%n (scot %ud +.newl)])) == Using [`=.`]() we update the value of `p.vat` in our context using [`put:by`](), one of our map container functions. Then, we produce `+>.$` as our context. Since we have changed the value of `p.vat` within our immediate context, `$`, this is equivalient to updating the state of our service. Changing a value in your context and producing it is all you need to do to update your permanent state. That's one of the main goals of `%gall`, to be a single-level store. So, how did we get to this point in `++poke-json`? =+ ^= jop ^- kiss %- need %. jon => jo %- of :~ [%new-lead so] [%add-lead so] == 6. `++deliver` 7. main.js