@@ -4,6 +4,8 @@ sort: 6
4title: Web Apps
4title: Web Apps
7We've done purely functional web pages as hook files, and we've
9We've done purely functional web pages as hook files, and we've
8done command line apps, so now let's interact with an app over
10done command line apps, so now let's interact with an app over
9the web. The fundamental concepts are no different than from the
11the web. The fundamental concepts are no different than from the
@@ -14,26 +16,26 @@ javascript.
14Let's create a small web app that counts the number of times you
16Let's create a small web app that counts the number of times you
15poke it. There'll be a button to poke the app, and line of text
17poke it. There'll be a button to poke the app, and line of text
16below it saying how many times the app has been poked. We can
18below it saying how many times the app has been poked. We can
17−update this by subscribing to that information from the app.
19+update this by subscribing our page to the relevant path on its app.
19−Let's first write the app, `/ape/click.hoon`:
21+Let's first write the app, `/app/click.hoon`:
25−++ move ,[bone %diff %mark *]
27+++ move {bone $diff $mark *}
28−|_ [hid=bowl clicks=@]
30+|_ {hid/bowl clicks/@}
29++ poke-click
31++ poke-click
30− |= click=%click
32+ |= click/$click
31 ~& [%poked +(clicks)]
33 ~& [%poked +(clicks)]
32 :_ +>.$(clicks +(clicks))
34 :_ +>.$(clicks +(clicks))
33 %+ turn (prey /the-path hid)
35 %+ turn (prey /the-path hid)
34− |=([o=bone *] [o %diff %clicks +(clicks)])
36+ |=({o/bone *} [o %diff %clicks +(clicks)])
35++ peer-the-path
37++ peer-the-path
36− |= pax=path
38+ |= pax/path
37 [[[ost.hid %diff %clicks clicks] ~] +>.$]
39 [[[ost.hid %diff %clicks clicks] ~] +>.$]
@@ -48,12 +50,12 @@ number of clicks.
48Let's take a look at the new marks. Here's `/mar/click.hoon`:
50Let's take a look at the new marks. Here's `/mar/click.hoon`:
51−|_ click=%click
53+|_ click/$click
54 ++ noun |=(* %click)
56 ++ noun |=(* %click)
56− |= jon=^json
58+ |= jon/^json
57 ?> =('click' (need (so:jo jon)))
59 ?> =('click' (need (so:jo jon)))
@@ -69,11 +71,10 @@ asserting the parsing succeeded with `need`, and asserting the
69result was 'click' with `?>`, which asserts that its first child
71result was 'click' with `?>`, which asserts that its first child
72−> Note the argument to `++json`is `jon=^json`. Why `^json`?
74+> Note the argument to `++json`is `jon/^json`. Why `^json`?
73> `++json` shadows the type definition, so if we want to refer to
75> `++json` shadows the type definition, so if we want to refer to
74> the type, we have to prepend a `^`. This extends to multiple
76> the type, we have to prepend a `^`. This extends to multiple
75−> levels: `^^^foo` means "not the innermost `foo`, nor the next
77+> levels: `^^^foo` means the fourth most innermost instance of `foo`.
76−> innermost, nor the next innermost, but the next innermost".
78> `++jo` in `zuse` is a useful library for parsing complex json
79> `++jo` in `zuse` is a useful library for parsing complex json
79> into hoon structures. In this case, the `:` between `so` and
80> into hoon structures. In this case, the `:` between `so` and
@@ -81,7 +82,7 @@ is true.
81> the core `jo`. Our case is actually simple enough that the
82> the core `jo`. Our case is actually simple enough that the
82> `?>` line could have been `?> =([%s 'click'] jon)`.
83> `?>` line could have been `?> =([%s 'click'] jon)`.
84−We can test this mark from the command line:
85+We can test this mark from the command line (don't forget to start your app with `|start %click`)
87~fintud-macrep:dojo> &click %click
88~fintud-macrep:dojo> &click %click
@@ -99,7 +100,7 @@ ford: cast %click
99And `/mar/clicks.hoon`:
100And `/mar/clicks.hoon`:
102−|_ clicks=@
103+|_ clicks/@
@@ -117,7 +118,7 @@ object with a single field "clicks" with our value.
119~fintud-macrep:dojo> &json &clicks 6
120~fintud-macrep:dojo> &json &clicks 6
120−[%o p={[p='clicks' q=[%n p=~.^]]}]
121+[%o p={[p='clicks' q=[%n p=~.6]]}]
121~fintud-macrep:dojo> &mime &json &clicks 6
122~fintud-macrep:dojo> &mime &json &clicks 6
122[[%text %json ~] p=12 q='{"clicks":6}']
123[[%text %json ~] p=12 q='{"clicks":6}']
@@ -143,14 +144,16 @@ the app from the command line.
143 `:click` and print out the subscription updates on the command
144 `:click` and print out the subscription updates on the command
146That's all that's needed for the back end. The front end is just
149That's all that's needed for the back end. The front end is just
147−some "sail" html and javascript. Here's `/pub/click/hymn.hook`:
150+some "sail" html (Hoon markup for XML) and javascript. Here's `/web/click.hoon`:
152 ;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js");
155 ;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js");
153− ;script(type "text/javascript", src "/~~/~/at/home/lib/urb.js");
156+ ;script(type "text/javascript", src "/~/at/lib/js/urb.js");
154 ;title: Clickety!
157 ;title: Clickety!
@@ -159,7 +162,7 @@ some "sail" html and javascript. Here's `/pub/click/hymn.hook`:
159 ;div#err(class "disabled");
162 ;div#err(class "disabled");
160 ;div#clicks;
163 ;div#clicks;
162− ;script(type "text/javascript", src "click/main.js");
165+ ;script(type "text/javascript", src "/click/main.js");
@@ -168,10 +171,12 @@ You should recognize the sail syntax from an earlier chapter.
168Aside from jquery, we also include `urb.js`, which is a framework
171Aside from jquery, we also include `urb.js`, which is a framework
169for interacting with urbit.
172for interacting with urbit.
174+To view the frontend, point your browser at `ship-name.urbit.org/~~/click` if you're on the live network. If you're running a fake galaxy, navigate to whatever port it's running on, which is usually: `https://localhost:8443/~~/click`.
171We have a button labeled "Poke!" and a div with id `clicks` where
176We have a button labeled "Poke!" and a div with id `clicks` where
172we'll put the number of clicks. We also include a small
177we'll put the number of clicks. We also include a small
173javascript file where the client-side application logic can be
178javascript file where the client-side application logic can be
174−found. It's in `/pub/click/main.js`:
179+found. It's in `/web/click/main.js`:
177$(function() {
182$(function() {
@@ -208,14 +213,10 @@ $(function() {
211−We first set the default application to "click". A page can, of
212−course, talk to multiple apps by specifying on each message which
215We set up two event handlers. When we click the button, we run
216We set up two event handlers. When we click the button, we run
216−`window.urb.send`, which sends a poke to an app. The arguments
217+`window.urb.send`, which sends a poke to the app specified as the first argument, in this case our :click app. The arguments
217−are data, parameters, and callback. The data is the specific
218+are data, parameters, and the callback. The data is the specific
218−data to send. The parameters are all optional, but here's a list
219+data we want to send. The parameters are all optional, but here's a list
219of the available ones:
220of the available ones:
221- `ship`: target urbit. Defaults to `window.urb.ship`, which
222- `ship`: target urbit. Defaults to `window.urb.ship`, which
@@ -231,15 +232,10 @@ In our case, we specify only that the mark is "click".
231The callback funciton is called when we receive an
232The callback funciton is called when we receive an
232acknowledgment. If there was an error, we put it in the `err`
233acknowledgment. If there was an error, we put it in the `err`
233div that we defined above. Otherwise, we printf to the console a
234div that we defined above. Otherwise, we printf to the console a
234−message saying the poke succeeded. A common pattern is to gray
235+message saying the poke succeeded. As is common, we gray
235−out a button when you click it and only reenable it when you get
236+out the button when it is clicked. It is only reenabled when positive acknowledgement is received.
236−the positive acknowledgment.
238−We also call `window.urb.bind`, which subscribes to a data stream
238+Our second event handler is `window.urb.bind`, which is called immediately when the page is loaded, subscribing the page to the specified data stream on the app set with `urb.appl` (which is, in this case, :click). It also takes three arguments: path, parameters, and callback. The path is the path on the app to subscribe to. The parameters are all optional (indeed, we omit them entirely here), but are similar to those for `send`:
239−on an app. It also takes three arguments: path, parameters, and
240−callback. The path is the path on the app to subscribe to. The
241−parameters are all optional (inded, we omit them entirely here),
242−but are similar to those for `send`:
244- `ship`: target urbit. Defaults to `window.urb.ship`, which
240- `ship`: target urbit. Defaults to `window.urb.ship`, which
245 defaults to the urbit which served the page.
241 defaults to the urbit which served the page.
@@ -255,10 +251,6 @@ The callback function here just updates the data in the `clicks`
255div. A truly robust app would intelligently handle errors, of
251div. A truly robust app would intelligently handle errors, of
258−This app can be tested from the web by going to
259−`/~~/home/pub/click` on your server. Usually, this is
260−`ship-name.urbit.org/~~/home/pub/click`.
262Note that the app doesn't have anything web-specific in itself.
254Note that the app doesn't have anything web-specific in itself.
263As far as it knows, it's just receiving pokes and subscriptions.
255As far as it knows, it's just receiving pokes and subscriptions.
264The javascript is fairly pure as well, sending and receiving json
256The javascript is fairly pure as well, sending and receiving json