mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-09-22 16:09:13 +03:00
restore old guides
This commit is contained in:
parent
e5ff969867
commit
7b0275efec
15
pub/doc/guide.md
Normal file
15
pub/doc/guide.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<div class="short">
|
||||||
|
|
||||||
|
Guides
|
||||||
|
======
|
||||||
|
|
||||||
|
These guides are designed to get you oriented in urbit.
|
||||||
|
|
||||||
|
Each one covers a specific topic. Although it's not necessary to follow
|
||||||
|
them in order, they do get increasingly complex.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
<list></list>
|
813
pub/doc/guide/a-ford.md
Normal file
813
pub/doc/guide/a-ford.md
Normal file
@ -0,0 +1,813 @@
|
|||||||
|
`%ford` Guide
|
||||||
|
=============
|
||||||
|
|
||||||
|
#### basic `hoon` and talking to the web
|
||||||
|
|
||||||
|
<div class="short">
|
||||||
|
|
||||||
|
`%ford` is the arvo vane that handles asset management and publishing.
|
||||||
|
We use `%ford` to compose our files when programming, and also to bundle
|
||||||
|
files together for the web.
|
||||||
|
|
||||||
|
This guide assumes that you have installed and started your urbit.
|
||||||
|
Assuming you cloned the repo into `/$URB_DIR` you should be able to find
|
||||||
|
the unix mirror of your ship's filesystem in
|
||||||
|
`/$URB_DIR/$PIER/$SHIP-NAME`. All of the filesystem paths in this guide
|
||||||
|
are relative to that Unix directory.
|
||||||
|
|
||||||
|
Also, we refer to `http://ship-name.urbit.org/` in our URLs, but you can
|
||||||
|
also use `http://localhost:8080/` if you prefer to just talk to your
|
||||||
|
machine directly.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
1. Let's publish a webpage
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/main/pub/fab/guide/exercise/1/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 1
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 1 — Simple HTML
|
||||||
|
;p: As you may notice, urbit has no problem talking to the web.
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/1/
|
||||||
|
|
||||||
|
#### What did you just do?
|
||||||
|
|
||||||
|
The code you just wrote is urbit's native programming langauge, hoon.
|
||||||
|
Generating HTML with hoon is similar to writing [jade]() or other
|
||||||
|
similar HTML shorthand. In hoon, this shorthand is called [`++sail`]()
|
||||||
|
and it's a native part of the hoon language.
|
||||||
|
|
||||||
|
In `++sail` node-names are prefixed with a `;` and closed with a `==`.
|
||||||
|
Nodes that have text content and are only on one line use a `:`, such as
|
||||||
|
our `;h1:` above, and are closed implicitly with a new line. Nodes with
|
||||||
|
no content are closed with another `;`, such as `;br;`.
|
||||||
|
|
||||||
|
You can find more information about `++sail` in our [rune library
|
||||||
|
documentation]().
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
2. Let's do some programming on the page.
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/2/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 2
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 2 — Call a function
|
||||||
|
;p: Although it may be obvious, 2+2={<(add 2 2)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/2/
|
||||||
|
|
||||||
|
### What's going on there?
|
||||||
|
|
||||||
|
Clearly, the code `(add 2 2)` is generating `4`, and it's not too hard
|
||||||
|
to see why. `add` is one of the library functions that are a part of
|
||||||
|
`hoon.hoon`. Try replacing `(add 2 2)` with `(sub 2 (add 2 2))`. You can
|
||||||
|
find documentation for the full hoon library in the [library
|
||||||
|
reference]().
|
||||||
|
|
||||||
|
Since the product of `(add 2 2)` is a number, we need a few extra things
|
||||||
|
to have it print properly. In `++sail` we use `{` and `}` to do string
|
||||||
|
interpolation. Everything after the `:` is expected to be a string, so
|
||||||
|
we need to tell the parser when we start writing code. The `<` and `>`
|
||||||
|
inside our `{` `}` are converting our result `4`, a number, to the
|
||||||
|
string `"4"`. hoon is a strongly typed language kind of like haskell, so
|
||||||
|
we need to explicitly convert our types.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
3. Let's assign some variables.
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/3/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
=+ ^= a 1
|
||||||
|
=+ b=2
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 3
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 3 — Assignment
|
||||||
|
;p: a={<a>}
|
||||||
|
;p: b={<b>}
|
||||||
|
;p: a+b={<(add a b)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/3/
|
||||||
|
|
||||||
|
#### How does that work?
|
||||||
|
|
||||||
|
The first thing you should notice in this example is the `=+` at the top
|
||||||
|
of our file. `=+` is a rune. hoon is a programming with no reserved
|
||||||
|
words. We don't use `if` `this` or `function` at all. Instead, runes
|
||||||
|
have their own pronunciation. `=+` is pronounced 'tislus'. You can find
|
||||||
|
the table of pronunciation [here](). In hoon you construct your
|
||||||
|
programs using runes, which are two character ascii pairs. You can see
|
||||||
|
the whole set of runes in the [rune index]().
|
||||||
|
|
||||||
|
`=+` pushes an expression on to our subject. The subject in hoon is
|
||||||
|
similar to `this` in other languages. hoon being a functional language
|
||||||
|
if we want something to be available further on in our computation we
|
||||||
|
need to attach it to the subject first.
|
||||||
|
|
||||||
|
The second thing you should notice is the `^- manx`. Here the rune
|
||||||
|
`[^-]()` is used to cast our product to a [++manx](), which you can
|
||||||
|
think of like the hoon MIME type for XML. Using a `^-` is not required,
|
||||||
|
but helps us produce more informative error messages when we generate a
|
||||||
|
type error or mismatch.
|
||||||
|
|
||||||
|
Looking at the rendered page it's clear that we're assigning `a` to be
|
||||||
|
`1` and `b` to be `2`. Looking at the code, however, you can see that
|
||||||
|
we're doing this in two different ways. Runes in hoon can have irregular
|
||||||
|
forms, and `^=` is one of them. The first two lines of our example are
|
||||||
|
doing the same thing, where `a=2` is simply the irregular form of
|
||||||
|
`^= a 2`. You can see the full list of irregular forms [here]().
|
||||||
|
|
||||||
|
Looking at the simple computation on the page, you can try changing the
|
||||||
|
values of `a` and `b` to any integers to produce similar results.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
4. Let's build our own computation
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/4/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
|%
|
||||||
|
++ start 1
|
||||||
|
++ end 10
|
||||||
|
++ length
|
||||||
|
|= [s=@ud e=@ud]
|
||||||
|
(sub e s)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 4
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 4 — Cores
|
||||||
|
;p: We'll be starting at {<start>}
|
||||||
|
;p: And ending at {<end>}
|
||||||
|
;p: Looks like a length of {<(length start end)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
(and be sure to put two spaces between `++` and arm names)
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/4/
|
||||||
|
|
||||||
|
### What's happening?
|
||||||
|
|
||||||
|
As you can see from the output, we have written a little function that
|
||||||
|
takes two numbers, `s` and `e` and returns their difference. The first
|
||||||
|
thing to notice about our code is the first rune. `|%` is a `core` rune.
|
||||||
|
You can think of cores like functions or objects in other languages.
|
||||||
|
`|%` runes contain an arbitrary number of arms, denoted with either `++`
|
||||||
|
or `+-` and closed with `--`.
|
||||||
|
|
||||||
|
Each arm has a value, either static data (in the case of `++start` and
|
||||||
|
`++end`) or a gate (in the case of `++length`). A gate is a kind of
|
||||||
|
core. Gates only have one arm and are quite similar to a function in
|
||||||
|
other languages. We use `|=` to construct our gate. Runes in hoon are
|
||||||
|
generally categorized by their first character. `|` indicates a rune
|
||||||
|
having to do with cores. You can find all of the `|` runes in the [rune
|
||||||
|
library]().
|
||||||
|
|
||||||
|
Our `++length` [gate]() takes two arguments, `s` and `e`. In hoon we
|
||||||
|
call the data passed in to a gate the 'sample'. Every `|=` has two
|
||||||
|
parts: the sample type and the computation, also known as a `tile` and a
|
||||||
|
`twig`. Casually, `[s=@ud e=@ud]` means that the gate takes two
|
||||||
|
arguments, labelled going forward as `s` and `e`, and required to both
|
||||||
|
be `@ud` or unsigned decimal. Our computation, `(sub e s)` simply
|
||||||
|
computes the difference between `e` and `s`.
|
||||||
|
|
||||||
|
`@ud` is an odor. Odors aren't quite types, but they're similar. You'll
|
||||||
|
learn the difference by example as we progress, and you can always refer
|
||||||
|
to the [odor index]().
|
||||||
|
|
||||||
|
You probably also noticed our indentation. In general hoon has both tall
|
||||||
|
and wide forms. In tall form, hoon uses two spaces for indentation and
|
||||||
|
is back-stepped so nested code doesn't drift away toward the right
|
||||||
|
margin. In wide form we use parenthesis just like almost everyone else.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
5.
|
||||||
|
--
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/5/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
|%
|
||||||
|
++ dist ,[start=@ud end=@ud]
|
||||||
|
++ length
|
||||||
|
|= [d=dist]
|
||||||
|
(sub end.d start.d)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 5
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 5 — Cores
|
||||||
|
;p: How long does it take to get from 2 to 20? {<(length 2 20)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/5/
|
||||||
|
|
||||||
|
#### What's the difference?
|
||||||
|
|
||||||
|
Clearly we're producing the same result as before, but we're doing it in
|
||||||
|
a different way.
|
||||||
|
|
||||||
|
The first line in our gate, `++length` always specifies the sample tile.
|
||||||
|
As you can see here, our sample tile is actually a `++dist`, the body of
|
||||||
|
which, `,[start=@ud end=@ud]` looks very similar to our previous example
|
||||||
|
in that it accepts two `@ud`. The important difference is that `++dist`
|
||||||
|
is now defined in a way that can be re-used. hoon is a strongly typed
|
||||||
|
language, but it encourages the creation of your own types using what we
|
||||||
|
call tiles.
|
||||||
|
|
||||||
|
At a high level you can think of hoon as being composed of two things,
|
||||||
|
tiles and twigs. Twigs are the actual AST structures that get consumed
|
||||||
|
by the compiler. Tiles reduce to twigs and provide major affordances for
|
||||||
|
the programmer. If you're interested in learning about tiles more deeply
|
||||||
|
you can find an in-depth explanation in the [tile overview]().
|
||||||
|
|
||||||
|
It should suffice, for now, to say that we create tiles in the same way
|
||||||
|
that you would think of creating type definitions in another language.
|
||||||
|
The primary way we do this is with `$` runes you can find more about
|
||||||
|
them in the [`$` section]() of the rune library.
|
||||||
|
|
||||||
|
In this specific example we are using the `$,` tile rune in its
|
||||||
|
irregular form, `,`. `,` generates a validator from the given
|
||||||
|
expression. In effect, `++dist` uses the type system to only produce
|
||||||
|
cells that appear in the form `[start=@ud end=@ud]`. When we use it in
|
||||||
|
our `++length` gate we assert that our input must be validated by
|
||||||
|
`++dist`. To test that out, you can try passing something that isn't an
|
||||||
|
unsigned decimal (or `@ud`) to `++length`. Try replacing `(length 2 20)`
|
||||||
|
with `(length 2 'a')`. As we continue you'll see how this pattern can be
|
||||||
|
quite useful.
|
||||||
|
|
||||||
|
One other thing to point out which may be immediately confusing coming
|
||||||
|
from other languages is the order of addressing `start` and `end`. We
|
||||||
|
call these labels faces, and we address them in the opposite order than
|
||||||
|
you're usually familiar with. We still separate our addressing with `.`,
|
||||||
|
but do it from the inside out. Given a tuple such as
|
||||||
|
`[a=1 b=[c=[d=2 e=3]]]` we can address the value of `e` with `e.c.b`.
|
||||||
|
You can read more about how faces work in the commentary on `++type`
|
||||||
|
[here]().
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
6.
|
||||||
|
--
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/6/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
|%
|
||||||
|
++ fib
|
||||||
|
|= x=@
|
||||||
|
?: (lte x 2)
|
||||||
|
1
|
||||||
|
(add $(x (dec x)) $(x (sub x 2)))
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 6
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 6 — Loops
|
||||||
|
;p: {<(fib 1)>}, {<(fib 2)>}, {<(fib 3)>}, {<(fib 4)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/6/
|
||||||
|
|
||||||
|
#### What is that doing?
|
||||||
|
|
||||||
|
We're printing a few members of the [fibonacci sequence]() by
|
||||||
|
calling our arm `++fib` with a few values. The fibonacci sequence is a
|
||||||
|
fairly straight forward algorithm: `F(n-1) + F(n-2)` where `F(1) = 1`
|
||||||
|
and `F(2) = 1`. As is obvious from the formula, generating the fibonacci
|
||||||
|
value at any number requires us to recurse, or loop. Let's walk through
|
||||||
|
the code.
|
||||||
|
|
||||||
|
Our example here should look similar to the previous one. We build a
|
||||||
|
core with `|%` and add the arm `++fib`. `++fib` is a gate which takes
|
||||||
|
one argument `x`, an atom. Using [`?:`]() we test if `x` is less
|
||||||
|
than `2` with the library function [`lte`]() to handle our seed
|
||||||
|
values of `F(1) = 1` and `F(2) = 1`. `?:` is a member of the [`?`
|
||||||
|
runes](), related to true / false values, or loobeans. In hoon true
|
||||||
|
and false are `0` and `1` respectively and take the odor `@f`. We tend
|
||||||
|
to say 'yes' and 'no' instead of 'true' and 'false' to keep track of the
|
||||||
|
switch. Our built-in function `lte` produces a loobean, so we evaluate
|
||||||
|
our first line if true, and second if false.
|
||||||
|
|
||||||
|
If `x` is not less than `2` we compute `F(n-1) + F(n-2)` by using `$`.
|
||||||
|
We mentioned previously that a gate is a special kind of core with only
|
||||||
|
one arm, called `$`. Here we're using `$` to mimic the behavior of a
|
||||||
|
loop. You can read the expression `$(x (dec x))` as 'call the gate again
|
||||||
|
with `x` replaced by `(dec x)`. For more on how this works, check out
|
||||||
|
the documentation of [`%=`]() and [`%-`](). With that in mind it
|
||||||
|
should be clear how the last line of `++fib` produces the member of the
|
||||||
|
sequence at a given value `x`.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
7.
|
||||||
|
--
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/7/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/7/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 1
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: %ford Example 1 — Page Variables
|
||||||
|
;div.who: {<(~(get ju aut.ced.gas) 0)>}
|
||||||
|
;div.where: {(spud s.bem.gas)} rev {(scow %ud p.r.bem.gas)}
|
||||||
|
;code
|
||||||
|
;pre: {<gas>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/7/
|
||||||
|
http://ship-name.urbit.org/gin/del/main/pub/fab/guide/exercise/7/
|
||||||
|
|
||||||
|
Here we're putting our publishing framework, `%ford` to work a little
|
||||||
|
bit. We're printing out some of the parameters our page is passed: who
|
||||||
|
is looking at it, where it is and what revision our desk is on. We have
|
||||||
|
also thrown in all our FCGI parameters in a codeblock for reference.
|
||||||
|
|
||||||
|
To do this we have introduced some new runes, `/?`, `/=` and `/$`. We
|
||||||
|
tend to call runes with a leading `/` "`%ford` runes" since they are
|
||||||
|
used by the `%ford` vane for resource loading and composition. By
|
||||||
|
convention, we indent four spaces after them to make them more obvious.
|
||||||
|
They belong at the top of the file.
|
||||||
|
|
||||||
|
`/?` simply checks for compatibility. In this case the line means 'need
|
||||||
|
urbit 314 or below', or in hoon: `(lte zuse 314)`. `314` is the number
|
||||||
|
in the kelvin versioning system, which you can read about [here]().
|
||||||
|
|
||||||
|
`/=` is similar to the combination of `=+ =^`, or assignment. `/$`
|
||||||
|
calls a parsing function, which we specify as [`++fuel`]() with the
|
||||||
|
[`++beam`]() and [`++path`]() of our current file.
|
||||||
|
`/= gas /$ fuel` is a common way to open your page, since the
|
||||||
|
product of `++fuel` is useful when writing pages to the web. The use of
|
||||||
|
`++fuel` is not enforced — you can also write your own parser.
|
||||||
|
|
||||||
|
Our page is made up of two generated parts: who requested the page, the
|
||||||
|
location of the page and its revision. Both are parsed out of the `gas`
|
||||||
|
variable using some straightforward library functions, [`++ju`](),
|
||||||
|
[`++spud`]() and [`++scow`](). You can follow those links to the
|
||||||
|
library reference to learn more about them. You'll also notice our
|
||||||
|
addressing moving in the opposite direction as you may be used to.
|
||||||
|
`aut.ced.gas` pulls `aut` from inside `ced` from inside `gas`.
|
||||||
|
|
||||||
|
Inside of the `;code` tag we also print (for our own reference) the
|
||||||
|
entire `gas`, so you can take a look at the contents. This can be a
|
||||||
|
helpful trick when debugging. To fully understand what gets put in
|
||||||
|
`gas`, we can take a look at `++fuel` and note that it produces a
|
||||||
|
[`++epic`](), which also contains a [`++cred`](). You can follow
|
||||||
|
those links to learn more about them.
|
||||||
|
|
||||||
|
When we try changing the url from `main` to `gin/del/main` we're
|
||||||
|
using some of the access methods from `%eyre` (the urbit webserver) to
|
||||||
|
pretend to be the urbit `~del`. You can find documentation on those
|
||||||
|
access methods in the `%eyre` commentary, [here]().
|
||||||
|
|
||||||
|
Path and identity are useful, but there are some other parameters worth
|
||||||
|
checking out as well.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
8.
|
||||||
|
--
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/8/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/8/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;title: %ford Example 2
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div: Do you have a code?
|
||||||
|
;div: ?code={<(fall (~(get by qix.gas) %code) '')>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/8/
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/8/?code=yes-i-do
|
||||||
|
|
||||||
|
This is a simple example, showing off another use of
|
||||||
|
`/= gas /$ fuel`. In this case we're just pulling out the value of
|
||||||
|
the `code` query string parameter. You should be able to change that
|
||||||
|
value to any url-safe string and see it appear on the page.
|
||||||
|
|
||||||
|
We're using a few simple library functions to actually pull the value
|
||||||
|
out, [`++fall`]() and [`get:by`](). Query string parameters are
|
||||||
|
stored in `qix.gas` as a `++map`, one of the main container constructs
|
||||||
|
used in hoon. We'll encounter a lot of maps along the way, and you can
|
||||||
|
learn more about them in the [map section]() of the library doc.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
9.
|
||||||
|
--
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/9/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/exercise/9/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
::
|
||||||
|
// /%%/lib
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;title: %ford Example 3
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;h1: %ford Example 3
|
||||||
|
;p: {<(fib 1)>}, {<(fib 2)>}, {<(fib 3)>}, {<(fib 4)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### And in
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/9/lib.hoon
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
|%
|
||||||
|
++ fib
|
||||||
|
|= x=@
|
||||||
|
?: (lte x 2)
|
||||||
|
1
|
||||||
|
(add $(x (dec x)) $(x (sub x 2)))
|
||||||
|
--
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/9/
|
||||||
|
|
||||||
|
#### How are they getting combined?
|
||||||
|
|
||||||
|
Clearly this looks a lot like our previous example using `++fib`, only
|
||||||
|
now we're using two separate files. The majority of this code should be
|
||||||
|
familiar to you from example 6, so let's focus on a single line,
|
||||||
|
`// /%%/lib`.
|
||||||
|
|
||||||
|
`//` is a `%ford` rune that loads a resource from a given path. `//` is
|
||||||
|
used as a way to organize project code into separate files, not for
|
||||||
|
loading libraries and structures. We'll show some examples of how urbit
|
||||||
|
handles those things shortly. You can think of `//` as a kind of
|
||||||
|
`include` or `require`. `//` takes a `beam`, an absolute global path in
|
||||||
|
`%clay` — the global urbit filesystem.
|
||||||
|
|
||||||
|
In `%clay` we use `%` to navigate relative paths. `%` is sort of like
|
||||||
|
`.` when moving around the unix file system. Although we put our code in
|
||||||
|
the unix path `/pub/fab/guide/exercise/9/hymn.hook` the file extension
|
||||||
|
is just a hint to the system. `/%/` (the equivalent of a unix `./`)
|
||||||
|
actually resolves to `/pub/fab/guide/exercise/9/hymn`.
|
||||||
|
|
||||||
|
We commonly use two kinds of extensions, `.hoon` for source files and
|
||||||
|
`.hook` for files that generate something else. Since our hymn file is
|
||||||
|
generating html, it's a `.hook`, and our source file is just a `.hoon`.
|
||||||
|
In order to find our file one level up we need two `%%` to get to
|
||||||
|
`/pub/fab/guide/exercise/9/`. Adding `lib` resolve to our neighboring
|
||||||
|
file. You can read more about how `%clay` paths are parsed in the
|
||||||
|
[`%clay` overview](link). It's also pretty easy to try them out using
|
||||||
|
`/=main=`, `/=try`, `/try/a/b/c/d`, etc.
|
||||||
|
and `:ls` in your `%arvo` terminal.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
10.
|
||||||
|
---
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/10/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/10/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
// /%%/lib
|
||||||
|
::
|
||||||
|
=+ ^= arg
|
||||||
|
%+ slav
|
||||||
|
%ud
|
||||||
|
%+ fall
|
||||||
|
%- ~(get by qix.gas) %number
|
||||||
|
'0'
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;title: %ford Example 4
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div: {<(fib arg)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### And in
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/10/lib.hoon
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
|%
|
||||||
|
++ fib
|
||||||
|
|= x=@
|
||||||
|
?: (lte x 2)
|
||||||
|
1
|
||||||
|
(add $(x (dec x)) $(x (sub x 2)))
|
||||||
|
--
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/10/
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/10/?number=7
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/10/?number=12
|
||||||
|
|
||||||
|
As you can see by changing the URL, we're now passing our query string
|
||||||
|
parameter to our `++fib` script and printing the output. It's common to
|
||||||
|
pull some data out of the URL and do something with it, but we don't
|
||||||
|
have any type information about our query string parameters and hoon is
|
||||||
|
a strongly typed languge. As you can see, we're calling `++fib` with a
|
||||||
|
parameter `arg`. Let's look closely at how we generate `arg`.
|
||||||
|
|
||||||
|
The first part of our assignment should look familiar from previous
|
||||||
|
example, `=+ ^= arg` puts the face `arg` on the product of our
|
||||||
|
remaining computation. In short, you can read the code that produces
|
||||||
|
`arg` as `(slav %ud (fall (~(get by qix.gas) %number) '0'))`. We use `%`
|
||||||
|
runes to write this in tall form. `%` runes are used for calling gates
|
||||||
|
or evaluating changes. `%+` 'slams' or calls a gate with two arguments,
|
||||||
|
and `%-` 'slams' or calls a gate with one argument. As usual, you can
|
||||||
|
find more about the `%` runes in the [`%` section]() of the rune
|
||||||
|
library.
|
||||||
|
|
||||||
|
To get a value out of our map of query string parameters `qix.gas` we
|
||||||
|
use a method from the [maps library]() that produces a `++unit`.
|
||||||
|
`++unit`s are a common type in hoon used for optional values. A
|
||||||
|
[`++unit`]() is either `~` or `[~ p=value]`. Since we need to
|
||||||
|
specify a value for `(fib arg)` even when someone doesn't enter the
|
||||||
|
query string we use [`++fall`](), which produces either the value of
|
||||||
|
the unit, or the second argument if the `++unit` is null. Since our
|
||||||
|
`qix.gas` has string values in it we specify a string in our second
|
||||||
|
argument, `'0'`. As an aside, `'0'` is different from `"0"` in hoon. You
|
||||||
|
can read about the difference in [`++cord`]() and [`++tape`]().
|
||||||
|
|
||||||
|
Our outermost call, to [`++slav`](), casts our string to a `@ud` —
|
||||||
|
which is the type expected by `++fib`. `++slav` takes the name of an
|
||||||
|
odor and a value, and tries to cast the value to that odor.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
11.
|
||||||
|
---
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/11/hymn.hook
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
/= posts /: /%%/lib
|
||||||
|
/; |= a=(list (pair ,@ ,manx))
|
||||||
|
%+ turn
|
||||||
|
a
|
||||||
|
|= [* b=manx]
|
||||||
|
b
|
||||||
|
/@
|
||||||
|
/psal/
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 11
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;h1: Ford example — Loading Resources by Number
|
||||||
|
;* posts
|
||||||
|
==
|
||||||
|
==
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/11/lib/1.md
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
#1
|
||||||
|
|
||||||
|
This is my first post.
|
||||||
|
|
||||||
|
#### In
|
||||||
|
|
||||||
|
/pub/fab/guide/exercise/11/lib/2.md
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
|
||||||
|
#2
|
||||||
|
|
||||||
|
This is my second post.
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/11/
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/11/lib/1/
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/exercise/11/lib/2/
|
||||||
|
|
||||||
|
#### Experiment with it
|
||||||
|
|
||||||
|
Try adding other `.md` files with numeric file names (such as `3.md`) to
|
||||||
|
the `11/lib/` directory to get a feel for what's going on.
|
||||||
|
|
||||||
|
What's happening?
|
||||||
|
|
||||||
|
As you can see, we're loading the markdown files in `11/lib` and putting
|
||||||
|
them in to our page. Let's dive into the code.
|
||||||
|
|
||||||
|
We're using `/=` to assign the `posts` face. `/:` sets the `++beam` for
|
||||||
|
the computation below it. You can think of it sort of like setting an
|
||||||
|
environment variable. Everything below uses our `++beam` `/%%/lib`.
|
||||||
|
|
||||||
|
If we take the next few lines and write them as pseudo code in wide form
|
||||||
|
they might look something like this, `(/; [gate] (/@ /psal/))`. That
|
||||||
|
being the case, let's start at the bottom and move upwards since that's
|
||||||
|
how our data flows. In depth documentation on individual `++horn` runes
|
||||||
|
can be found in the [horn section of the rune library]().
|
||||||
|
|
||||||
|
`/psal/` loads our `psal` mark. Marks are like content types, and we
|
||||||
|
keep them in `/main/mar/`. You can open `/main/mar/psal/door.hook` to
|
||||||
|
see that we specify the ways in which a particular mark can be converted
|
||||||
|
to produced well typed output. The general form of this is [`/mark/`]()
|
||||||
|
where `mark` exists in the `/main/mar/` directory. A `psal` is a partial
|
||||||
|
`hymn`, where `hymn` is the hoon structure for `html`.
|
||||||
|
|
||||||
|
`/@` loads a list of files in numerical order from the previously
|
||||||
|
specified `++beam` using our mark, `psal`. `/@` has a few close
|
||||||
|
relatives. `/&`, for example, reads files by `@da` or absolute date. You
|
||||||
|
can see the rest in the [horn section of the library]().
|
||||||
|
|
||||||
|
`/;` takes the output from `/@` and `/psal/` and passes it to a twig. In
|
||||||
|
this case, a gate. Our `/@` actually produces a [`++list`]() of
|
||||||
|
pairs of `[@ manx]` where the `@` is the filename, and the `manx` is the
|
||||||
|
converted contents. We use [`++turn`](), one of our `++list`
|
||||||
|
operator functions, to iterate through the list and produce only a list
|
||||||
|
of `++manx`. This is the output assigned to `posts`.
|
||||||
|
|
||||||
|
Then, further down, we use [`;*`]() to write the list to the page.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
</hr>
|
||||||
|
12.
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Look in
|
||||||
|
|
||||||
|
/pub/fab/guide/hymn.hook
|
||||||
|
/pub/fab/guide/main.css
|
||||||
|
/pub/fab/guide/main.js
|
||||||
|
|
||||||
|
#### Try it
|
||||||
|
|
||||||
|
http://ship-name.urbit.org/main/pub/fab/guide/
|
||||||
|
|
||||||
|
#### Bring it all together
|
||||||
|
|
||||||
|
It turns out it's pretty easy to pull our examples together into a
|
||||||
|
single blog-like page using what we just covered. We include some css to
|
||||||
|
make things a bit prettier, and this should give you a good jumping off
|
||||||
|
point for experimenting with your own publishing code.
|
||||||
|
|
||||||
|
#### Have fun!
|
273
pub/doc/guide/b-cli.md
Normal file
273
pub/doc/guide/b-cli.md
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
XX The CLI is under heavy development with, with pieces being folded
|
||||||
|
into the "window manager" `sole` and the new cli `dojo`. Don't expect
|
||||||
|
any of the following to work as described.
|
||||||
|
|
||||||
|
This guide is intended to get you oriented in the Arvo command prompt
|
||||||
|
and give you a tour of some basic utilities. The command prompt comes in
|
||||||
|
two flavors, in a web browser and in a terminal. For the most part
|
||||||
|
they're the same, except that in a browser you can evaluate tall-form
|
||||||
|
Hoon expressions but you can't run readline apps, such as `:talk`.
|
||||||
|
|
||||||
|
Every Arvo command prompt is also a Hoon REPL. The command line is a
|
||||||
|
great place to test out your hoon knowledge. In this guide we're just
|
||||||
|
going to talk about some basic system utilities and get comfortable
|
||||||
|
moving around in `%clay`. If you'd just like to see a list of
|
||||||
|
command-line utilities, you can find the Arvo man pages
|
||||||
|
[here](../arvo/util).
|
||||||
|
|
||||||
|
This rudimentary tour should work well in both places.
|
||||||
|
|
||||||
|
1
|
||||||
|
|
||||||
|
Move around `%clay`
|
||||||
|
|
||||||
|
After finishing the [setup instructions]() you should have an Arvo
|
||||||
|
prompt that looks like this:
|
||||||
|
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
The path at the beginning of your prompt is actually a path in the
|
||||||
|
global filesystem of Urbit, called `%clay`. Since `%clay` is universal
|
||||||
|
across all of Urbit, each full path starts with a ship name. `%clay` is
|
||||||
|
also versioned on a per-desk basis. Desks are the top-level directories
|
||||||
|
in your pier.
|
||||||
|
|
||||||
|
Moving around `%clay` is simple. There is no equivalent of `cd`.
|
||||||
|
Instead, just type a valid path name at the prompt to move to that
|
||||||
|
directory. Here we'll move to our starting root path in the try desk,
|
||||||
|
`/try=` to the `main` desk:
|
||||||
|
|
||||||
|
~talsur-todres/try=> /=main=
|
||||||
|
=% /~talsur-todres/main/0
|
||||||
|
~talsur-todres/main=>
|
||||||
|
|
||||||
|
We have two shortcuts in `%clay` that are worth noting, `=` and `%`.
|
||||||
|
|
||||||
|
`=` copies in some corresponding part of our current path. In the second
|
||||||
|
line above you can see how the `=` in `/=main=` pull in the
|
||||||
|
`~talsur-todres` and `0` in from our starting directory,
|
||||||
|
`/~talsur-todres/try/0`. It's important to note that our full prompt to
|
||||||
|
start is `/~talsur-todres/try=`, where the trailing `=` indicates the
|
||||||
|
current revision. In the shell, revision `0` never exists — it's used as
|
||||||
|
a pointer to the head.
|
||||||
|
|
||||||
|
`%` is similar to `.` in unix:
|
||||||
|
|
||||||
|
~talsur-todres/main=> %
|
||||||
|
=% /~talsur-todres/main/0
|
||||||
|
~talsur-todres/main=> %%
|
||||||
|
[~.~talsur-todres ~.main ~]
|
||||||
|
~talsur-todres/main=> %%%
|
||||||
|
[~.~talsur-todres ~]
|
||||||
|
~talsur-todres/main=> %%%%
|
||||||
|
~
|
||||||
|
|
||||||
|
When using `%` to move around in `%clay` you need to make sure to use
|
||||||
|
leading and trailing `/` to ensure your path is interpolted correctly:
|
||||||
|
|
||||||
|
~talsur-todres/main=> /%%%/try=
|
||||||
|
=% /~talsur-todres/try/0
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
2
|
||||||
|
|
||||||
|
Create some revisions
|
||||||
|
|
||||||
|
Let's use `:into`, our simple utility for writing text to a file, to
|
||||||
|
create a new file:
|
||||||
|
|
||||||
|
~talsur-todres/try=> :into %/helo/txt 'helo mars'
|
||||||
|
written
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
To confirm that our file was written, we can use `:ls`. `:ls` prints a
|
||||||
|
list of directory contents, but requires that you specify a path. `%`
|
||||||
|
will suffice for the current path:
|
||||||
|
|
||||||
|
~talsur-todres/try=> :ls %
|
||||||
|
readme helo
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
Let's quickly switch back to a unix command prompt to see a few things
|
||||||
|
about both how files are synced between `%clay` and unix, and where your
|
||||||
|
apps live.
|
||||||
|
|
||||||
|
my-pier/talsur-todres/$ ls try
|
||||||
|
helo.txt readme.md
|
||||||
|
my-pier/talsur-todres/$ cat try/helo.txt
|
||||||
|
helo mars
|
||||||
|
|
||||||
|
Here you can see that our files are synced back to unix as they are
|
||||||
|
changed in urbit, and vice-versa. As you change files in unix you'll see
|
||||||
|
those changes appear in `%clay`.
|
||||||
|
|
||||||
|
my-pier/talsur-todres/$ ls base/app/
|
||||||
|
bang grep peek solid tweet
|
||||||
|
began helm poke sync twit
|
||||||
|
begin hi pope talk twitter-auth
|
||||||
|
cat into reboot tease twitter-feed
|
||||||
|
code label reload terminal type
|
||||||
|
cp ls reset test unsync
|
||||||
|
curl matrix rm ticket verb
|
||||||
|
dojo mv shell time wipe
|
||||||
|
gnab nop sole tree ye
|
||||||
|
my-pier/talsur-todres/$ cat base/app/ls/core.hook
|
||||||
|
:: ConCATenate file listings
|
||||||
|
::
|
||||||
|
:::: /hook/core/cat/app
|
||||||
|
::
|
||||||
|
/+ sh-utils
|
||||||
|
// /%%%/ls/subdir
|
||||||
|
!:
|
||||||
|
::::
|
||||||
|
::
|
||||||
|
|_ [hid=hide ~]
|
||||||
|
++ peer ,_`.
|
||||||
|
++ poke--args
|
||||||
|
%+ args-into-gate .
|
||||||
|
|= [arg=(list path)]
|
||||||
|
=- tang/(zing -)
|
||||||
|
%+ turn arg
|
||||||
|
|= pax=path
|
||||||
|
^- tang
|
||||||
|
=+ ark=;;(arch .^(%cy pax))
|
||||||
|
?^ q.ark
|
||||||
|
:- leaf/(spud pax)
|
||||||
|
%+ turn (lore ;;(@t .^(%cx pax)))
|
||||||
|
|=(a=cord leaf/(trip a))
|
||||||
|
?- r.ark :: handle ambiguity
|
||||||
|
~
|
||||||
|
[rose/[" " `~]^~[leaf/"~" (smyt pax)]]~
|
||||||
|
[[@t ~] ~ ~]
|
||||||
|
$(pax (welp pax /[p.n.r.ark]))
|
||||||
|
*
|
||||||
|
=- [palm/[": " ``~]^-]~
|
||||||
|
:~ rose/[" " `~]^~[leaf/"*" (smyt pax)]
|
||||||
|
`tank`(subdir pax r.ark)
|
||||||
|
==
|
||||||
|
==
|
||||||
|
--
|
||||||
|
|
||||||
|
Here you can see that `/base/app` is the main location where our apps
|
||||||
|
are stored, and the contents of the `:ls` app. urbit applications are of
|
||||||
|
course written in hoon, our naitive programming language. Don't worry
|
||||||
|
about the contents of the file for now. Since changes in unix are synced
|
||||||
|
back in to urbit, we can develop urbit programs by simply editing them
|
||||||
|
in our favorite editor and saving them.
|
||||||
|
|
||||||
|
For the time being let's switch back to urbit and update our file with
|
||||||
|
some new content, so we can see how `%clay` stores revisions.
|
||||||
|
|
||||||
|
~talsur-todres/try=> :into %/helo/txt 'gbye mars'
|
||||||
|
written
|
||||||
|
~talsur-todres/try=> :ls /=try/1
|
||||||
|
readme helo
|
||||||
|
~talsur-todres/try=> :cat /=try/1/helo/txt
|
||||||
|
/~talsur-todres/try/9/helo/txt
|
||||||
|
helo mars
|
||||||
|
~talsur-todres/try=> :cat /=try/2/helo/txt
|
||||||
|
/~talsur-todres/try/10/helo/txt
|
||||||
|
gbye mars
|
||||||
|
~talsur-todres/try=> :cat /=try=/helo/txt
|
||||||
|
/~talsur-todres/try/~2014.11.26..01.06.33..c93a/helo/txt
|
||||||
|
gbye mars
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
Here we use `:ls` to investigate the filesystem across versions. You can
|
||||||
|
see that our `helo` file exists in our first revision. Using the simple
|
||||||
|
`:cat` command we can print the contents of `/=try/helo/txt` in its two
|
||||||
|
separate, versioned states.
|
||||||
|
|
||||||
|
We can even move to a different version of our desk and look around:
|
||||||
|
|
||||||
|
~talsur-todres/try=> /=try/1
|
||||||
|
=% /~talsur-todres/try/1
|
||||||
|
~talsur-todres/try/1> :ls %
|
||||||
|
readme helo
|
||||||
|
~talsur-todres/try/1>
|
||||||
|
|
||||||
|
This is sort of like being in a detached HEAD in git.
|
||||||
|
|
||||||
|
3
|
||||||
|
|
||||||
|
Start a yacht
|
||||||
|
|
||||||
|
Each Urbit destroyer can delegate around four billion yachts. Yachts are
|
||||||
|
also urbit ships, but are pegged to their parent identity, and are set
|
||||||
|
up to mirror their filesystem. We can generate a `[ship: ticket]` pair
|
||||||
|
for a yacht by using the `:ticket` utility:
|
||||||
|
|
||||||
|
~talsur-todres/try=> :ticket ~talsur-todres-talsur-todres
|
||||||
|
~talsur-todres-talsur-todres: ~figpem-fapmyl-wacsud-racwyd
|
||||||
|
|
||||||
|
Every yacht for a particular destroyer ends in the same `ship-name`, and
|
||||||
|
has every possible destroyer prefix. For example,
|
||||||
|
`~tasfyn-partyv-talsur-todres` is also a valid yacht from
|
||||||
|
`~talsur-todres`.
|
||||||
|
|
||||||
|
Start up a new `vere` process with something like `bin/vere -c yacht`.
|
||||||
|
Then run `:begin` and enter the `[ship: ticket]` pair you just generated
|
||||||
|
when prompted. When the process is complete you should get a
|
||||||
|
`; ~talsur-todres-talsur-todres :y1: is your neighbor` message on your
|
||||||
|
destroyer. To confirm that everything is working properly, you can use
|
||||||
|
`:hi` to send a message:
|
||||||
|
|
||||||
|
~talsur-todres/try=> :hi ~talsur-todres-talsur-todres "whats up"
|
||||||
|
hi ~talsur-todres-talsur-todres successful
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
Which will appear on your new yacht:
|
||||||
|
|
||||||
|
< ~talsur-todres: whats up
|
||||||
|
~talsur-todres-talsur-todres/try=>
|
||||||
|
|
||||||
|
You should also see the contents of your `/try` desk mirrored on your
|
||||||
|
yacht:
|
||||||
|
|
||||||
|
~talsur-todres-talsur-todres/try=> :ls %
|
||||||
|
readme helo
|
||||||
|
~talsur-todres-talsur-todres/try=>
|
||||||
|
|
||||||
|
Making another change on your destroyer should automatically propagate
|
||||||
|
down to your yacht:
|
||||||
|
|
||||||
|
~talsur-todres/try=> :into %/helo/txt 'back to mars'
|
||||||
|
written
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
[%merge-fine ~talsur-todres %try]
|
||||||
|
~talsur-todres-talsur-todres/try=> :cat %/helo/txt
|
||||||
|
back to mars
|
||||||
|
~talsur-todres-talsur-todres/try=>
|
||||||
|
|
||||||
|
4
|
||||||
|
|
||||||
|
Move files around
|
||||||
|
|
||||||
|
Another familiar command line utility is `:mv`:
|
||||||
|
|
||||||
|
~talsur-todres/try=> :mv %/helo/txt %/test/helo/txt
|
||||||
|
moved
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
[%merge-fine ~talsur-todres %try]
|
||||||
|
~talsur-todres-talsur-todres/try=> :cat %/test/helo/txt
|
||||||
|
back to mars
|
||||||
|
~talsur-todres-talsur-todres/try=>
|
||||||
|
|
||||||
|
In `%clay` we don't use file extensions or folders. A path either does
|
||||||
|
or does not have anything in it. There's no need to do the equivalent of
|
||||||
|
`mkdir` before moving something.
|
||||||
|
|
||||||
|
We also implement the familiar `:rm`:
|
||||||
|
|
||||||
|
~talsur-todres/try=> :rm %/test/helo/txt
|
||||||
|
removed
|
||||||
|
~talsur-todres/try=> :cat %/test/helo/txt
|
||||||
|
file /~talsur-todres/try/~2014.11.26..16.49.52..3f5e/test/helo/txt not available
|
||||||
|
~talsur-todres/try=>
|
||||||
|
|
||||||
|
[%merge-fine ~talsur-todres %try]
|
||||||
|
~talsur-todres-talsur-todres/try=> :cat %/test/helo/txt
|
||||||
|
file /~tasfyn-partyv-talsur-todres/try/~2014.11.26..16.50.15..556b/test/helo/txt not available
|
||||||
|
~talsur-todres-talsur-todres/try=>
|
284
pub/doc/guide/c-gall.md
Normal file
284
pub/doc/guide/c-gall.md
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
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 necessary 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/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
|
||||||
|
`/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 `<head>`. 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
|
1
pub/doc/guide/d-app.md
Normal file
1
pub/doc/guide/d-app.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
A fully featured app using %ford and %gall
|
100
pub/doc/guide/e-dev.md
Normal file
100
pub/doc/guide/e-dev.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
Doing development can be a messy process. Since Urbit ships are meant to
|
||||||
|
last forever it can be convenient to development on a disposable ship as
|
||||||
|
to not permanently destroy any of your own part of the urbit network. In
|
||||||
|
this short guide we're going to go over how to set up a fake network for
|
||||||
|
development on a single physical machine.
|
||||||
|
|
||||||
|
This guide assumes that you have already followed the [setup
|
||||||
|
instructions](). Going forward we'll refer to the directory you cloned
|
||||||
|
the repo in as `/$URB_DIR`.
|
||||||
|
|
||||||
|
1.
|
||||||
|
|
||||||
|
Start a fake `vere` for the carrier `~zod`.
|
||||||
|
|
||||||
|
In `/$URB_DIR`, run
|
||||||
|
|
||||||
|
$ bin/vere -F -I ~zod -c zod
|
||||||
|
|
||||||
|
This will boot `vere` into the carrier `~zod`. Because we're using the
|
||||||
|
flag `-F` `vere` doesn't check any of the keys to confirm that we are in
|
||||||
|
fact the owner of `~zod`. We use `-I` here to signal to `vere` that we
|
||||||
|
want to start an 'imperial' ship, or carrier. `-I` takes a ship name.
|
||||||
|
You can enter any one of the 256 Urbit carriers. More information on
|
||||||
|
`vere` and its command line options can be found [here]().
|
||||||
|
|
||||||
|
You should see `vere` start as usual, although instead of copying its
|
||||||
|
files from a parent ship the files are copied from `urb/zod` inside your
|
||||||
|
Urbit directory.
|
||||||
|
|
||||||
|
For most development tasks, using a fake carrier works really well. You
|
||||||
|
get a very short name, and a safe testing environment. If you need to
|
||||||
|
test out a collection of ships talking to each other, let's keep going.
|
||||||
|
|
||||||
|
2.
|
||||||
|
|
||||||
|
Start a second fake `vere`.
|
||||||
|
|
||||||
|
In a new terminal, cd to `/$URB_DIR` and run:
|
||||||
|
|
||||||
|
$ bin/vere -F -c doznec
|
||||||
|
|
||||||
|
Since we don't specify a carrier with `-I` here, this should boot into a
|
||||||
|
submarine, as if you had started vere normally. In your running fake
|
||||||
|
`~zod` you should see a [`~&`]() alerting you that the sub you just
|
||||||
|
started is your neighbor. This means that a key exchange has happened,
|
||||||
|
and your packets are being transmitted directly. Now, let's get a ticket
|
||||||
|
for a cruiser.
|
||||||
|
|
||||||
|
In your fake `~zod`, ticket a cruiser:
|
||||||
|
|
||||||
|
~zod/try=> :ticket ~doznec
|
||||||
|
|
||||||
|
This line should return a `[ship: ticket]` pair of [`@p`](). You can Now
|
||||||
|
you can return to your submarine and run:
|
||||||
|
|
||||||
|
~sipmyl-wolmeb-haswel-losmyl--dibten-holdyn-dacdyn-natsep/try=> :begin
|
||||||
|
|
||||||
|
Use the `[ship: ticket]` pair you got from your fake `~zod` to complete
|
||||||
|
the `:begin` process for `~doznec`. When finished you should see
|
||||||
|
something like `; ~doznec _doz_ is your neighbor` on your fake `~zod`.
|
||||||
|
|
||||||
|
You can repeat this process on `~doznec`, ticketing destroyers that are
|
||||||
|
children of `~doznec` by running `:ticket` with a valid destroyer.
|
||||||
|
`:ticket` actually takes two arguments, a ship name and a number
|
||||||
|
indicating how many tickets to generate. `~tasfyn-partyv` is the first
|
||||||
|
destroyer under `~doznec`, so you can run `:ticket ~tasfyn-partyv 5` to
|
||||||
|
get five `[ship: ticket]` pairs.
|
||||||
|
|
||||||
|
3.
|
||||||
|
|
||||||
|
Add some files, make sure the network if functioning.
|
||||||
|
|
||||||
|
You should now have a directory `/$URB_DIR/zod`.
|
||||||
|
|
||||||
|
In
|
||||||
|
|
||||||
|
/$URB_DIR/zod/zod/try/test.txt
|
||||||
|
|
||||||
|
Put
|
||||||
|
|
||||||
|
hello from mars
|
||||||
|
|
||||||
|
You should see a sync event on `~zod` indicated by a `+` with the file
|
||||||
|
path, and at least one `%merge-fine` messages on `~doznec`. On your
|
||||||
|
filesystem, you should see the file mirrored in
|
||||||
|
`/$URB_DIR/doznec/doznec/try/test.txt`.
|
||||||
|
|
||||||
|
You can also send a few `:hi` messages over the network. On `~doznec`
|
||||||
|
try:
|
||||||
|
|
||||||
|
~doznec/try=> :hi ~zod "just checking in from urth"
|
||||||
|
|
||||||
|
You should see the message appear on your `~zod` and get a
|
||||||
|
`hi ~zod successful` on your `~doznec`.
|
||||||
|
|
||||||
|
This is a good way to set up a test environment where you can try
|
||||||
|
anything out and break anything you want. When working in these test
|
||||||
|
environments it is usually safest to keep your working files outside of
|
||||||
|
your pier and copy them in. This way you can quickly
|
||||||
|
`rm -rf zod/ && bin/vere -F -I ~zod -c zod` to start again.
|
12
pub/fab/guide/exercise/1/hymn.hook
Normal file
12
pub/fab/guide/exercise/1/hymn.hook
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 1
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 1 — Simple HTML
|
||||||
|
;p: As you may notice, urbit has no problem talking to the web.
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
21
pub/fab/guide/exercise/10/hymn.hook
Normal file
21
pub/fab/guide/exercise/10/hymn.hook
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/9/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
::
|
||||||
|
// /%%/lib
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 4
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: %ford Example 4 — Breaking Code Into Parts
|
||||||
|
;div: {<(fib 70)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
7
pub/fab/guide/exercise/10/lib.hoon
Normal file
7
pub/fab/guide/exercise/10/lib.hoon
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|%
|
||||||
|
++ fib
|
||||||
|
|= x=@
|
||||||
|
~+ ?: (lth x 2)
|
||||||
|
1
|
||||||
|
(add $(x (dec x)) $(x (sub x 2)))
|
||||||
|
--
|
26
pub/fab/guide/exercise/11/hymn.hook
Normal file
26
pub/fab/guide/exercise/11/hymn.hook
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/10/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
// /%%/lib
|
||||||
|
::
|
||||||
|
=+ ^= arg
|
||||||
|
%+ slav
|
||||||
|
%ud
|
||||||
|
%+ fall
|
||||||
|
%- ~(get by qix.gas) %number
|
||||||
|
'0'
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 5
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;h1: %ford Example 5 — Computing With Parameters
|
||||||
|
;div: {<(fib arg)>}
|
||||||
|
==
|
||||||
|
==
|
7
pub/fab/guide/exercise/11/lib.hoon
Normal file
7
pub/fab/guide/exercise/11/lib.hoon
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|%
|
||||||
|
++ fib
|
||||||
|
|= x=@
|
||||||
|
~+ ?: (lth x 2)
|
||||||
|
1
|
||||||
|
(add $(x (dec x)) $(x (sub x 2)))
|
||||||
|
--
|
26
pub/fab/guide/exercise/12/hymn.hook
Normal file
26
pub/fab/guide/exercise/12/hymn.hook
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/11/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/= posts /: /%%/lib
|
||||||
|
/; |= a=(list (pair ,@ manx))
|
||||||
|
%+ turn
|
||||||
|
a
|
||||||
|
|= [* b=manx]
|
||||||
|
b
|
||||||
|
/@
|
||||||
|
/psal/
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 6
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: %ford Example 6 — Loading Resources by Number
|
||||||
|
;* posts
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
3
pub/fab/guide/exercise/12/lib/1.md
Normal file
3
pub/fab/guide/exercise/12/lib/1.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#1
|
||||||
|
|
||||||
|
This is my first post.
|
3
pub/fab/guide/exercise/12/lib/2.md
Normal file
3
pub/fab/guide/exercise/12/lib/2.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#2
|
||||||
|
|
||||||
|
This is my second post.
|
12
pub/fab/guide/exercise/2/hymn.hook
Normal file
12
pub/fab/guide/exercise/2/hymn.hook
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 2
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 2 — Call a function
|
||||||
|
;p: Although it may be obvious, 2+2={<(add 2 2)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
18
pub/fab/guide/exercise/3/hymn.hook
Normal file
18
pub/fab/guide/exercise/3/hymn.hook
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
=+ ^= a 1
|
||||||
|
=+ b=2
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 3
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 3 — Assignment
|
||||||
|
;p: a={<a>}
|
||||||
|
;p: b={<b>}
|
||||||
|
;p: a+b={<(add a b)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
24
pub/fab/guide/exercise/4/hymn.hook
Normal file
24
pub/fab/guide/exercise/4/hymn.hook
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
|%
|
||||||
|
++ start 1
|
||||||
|
++ end 10
|
||||||
|
++ length
|
||||||
|
|=
|
||||||
|
[s=@ud e=@ud]
|
||||||
|
(sub e s)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 4
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 4 — Cores
|
||||||
|
;p: We'll be starting at {<start>}
|
||||||
|
;p: And ending at {<end>}
|
||||||
|
;p: Looks like a length of {<(length start end)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
21
pub/fab/guide/exercise/5/hymn.hook
Normal file
21
pub/fab/guide/exercise/5/hymn.hook
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|%
|
||||||
|
++ dist ,[start=@ud end=@ud]
|
||||||
|
++ length
|
||||||
|
|=
|
||||||
|
[d=dist]
|
||||||
|
(sub end.d start.d)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 5
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 5 — Cores
|
||||||
|
;p: How long does it take to get from 2 to 20? {<(length 2 20)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
21
pub/fab/guide/exercise/6/hymn.hook
Normal file
21
pub/fab/guide/exercise/6/hymn.hook
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|%
|
||||||
|
++ fib
|
||||||
|
|= x=@
|
||||||
|
?: (lth x 2)
|
||||||
|
1
|
||||||
|
(add $(x (dec x)) $(x (sub x 2)))
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: Exercise 5
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: Exercise 5 — Loops
|
||||||
|
;p: {<(fib 1)>}, {<(fib 2)>}, {<(fib 3)>}, {<(fib 4)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
24
pub/fab/guide/exercise/7/hymn.hook
Normal file
24
pub/fab/guide/exercise/7/hymn.hook
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/6/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 1
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: %ford Example 1 — Page Variables
|
||||||
|
;div.who: {<(~(get ju aut.ced.gas) 0)>}
|
||||||
|
;div.where: {(spud s.bem.gas)} rev {(scow %ud p.r.bem.gas)}
|
||||||
|
;code
|
||||||
|
;pre: {<gas>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
21
pub/fab/guide/exercise/8/hymn.hook
Normal file
21
pub/fab/guide/exercise/8/hymn.hook
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/7/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 2
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: %ford Example 2 — Query String Parameters
|
||||||
|
;div: Do you have a code?
|
||||||
|
;div: ?code={<(fall (~(get by qix.gas) %code) '')>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
33
pub/fab/guide/exercise/9/hymn.hook
Normal file
33
pub/fab/guide/exercise/9/hymn.hook
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/8/exercise/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
|%
|
||||||
|
++ fib
|
||||||
|
|= x=@
|
||||||
|
?: (lth x 2)
|
||||||
|
1
|
||||||
|
(add $(x (dec x)) $(x (sub x 2)))
|
||||||
|
++ arg
|
||||||
|
%+ fall
|
||||||
|
%+ biff
|
||||||
|
(~(get by qix.gas) %number)
|
||||||
|
(slat %ud)
|
||||||
|
0
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;title: %ford Example 3
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div
|
||||||
|
;h1: %ford Example 3 — Computing With Parameters
|
||||||
|
;div: {<(fib arg)>}
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
55
pub/fab/guide/hymn.hook
Normal file
55
pub/fab/guide/hymn.hook
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
::
|
||||||
|
::
|
||||||
|
:::: /hook/hymn/guide/fab/pub/
|
||||||
|
::
|
||||||
|
/? 314
|
||||||
|
/= gas /$ fuel
|
||||||
|
/= exercise
|
||||||
|
/: /%%/exercise
|
||||||
|
/; |= a=(list (pair ,@ ,manx))
|
||||||
|
=+ ^= b
|
||||||
|
%+ turn
|
||||||
|
a
|
||||||
|
|= [* b=manx]
|
||||||
|
;div.post
|
||||||
|
;+ -.c.i.-.t.+.c.b
|
||||||
|
==
|
||||||
|
(flop b)
|
||||||
|
/@
|
||||||
|
/hymn/
|
||||||
|
::
|
||||||
|
|%
|
||||||
|
++ rut
|
||||||
|
|= [gus=epic]
|
||||||
|
=+ ^= nyp
|
||||||
|
?: =(-.nyp.gus %gin)
|
||||||
|
"{<nyp.gus>}/{+:(scow %p p.bem.gus)}"
|
||||||
|
"{<nyp.gus>}"
|
||||||
|
"{nyp}".
|
||||||
|
"/{(trip q.bem.gas)}".
|
||||||
|
"{<`path`(flop (slag 1 s.bem.gas))>}"
|
||||||
|
--
|
||||||
|
::
|
||||||
|
^- manx
|
||||||
|
;html
|
||||||
|
;head
|
||||||
|
;title: Helo Web
|
||||||
|
;meta(charset "utf-8");
|
||||||
|
;link/"{(rut gas)}/main.css"(rel "stylesheet", type "text/css");
|
||||||
|
;script(src "{(rut gas)}/main.js", type "text/javascript");
|
||||||
|
==
|
||||||
|
;body
|
||||||
|
;div#container
|
||||||
|
;div#head
|
||||||
|
;div.info
|
||||||
|
;div.ship: {(scow %p p.bem.gas)}
|
||||||
|
;div.path: {(spud s.bem.gas)} desk at rev {(scow %ud p.r.bem.gas)}
|
||||||
|
==
|
||||||
|
;h1: Learning %ford
|
||||||
|
==
|
||||||
|
;div#body
|
||||||
|
;* exercise
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
45
pub/fab/guide/main.css
Normal file
45
pub/fab/guide/main.css
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
html {
|
||||||
|
font-size: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0; padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: .6rem;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
width: 32rem;
|
||||||
|
margin: 2rem auto 2rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#head {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-bottom: 3px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
padding: 0 0 .6rem 0;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post h1 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post p {
|
||||||
|
font-size: .8rem;
|
||||||
|
}
|
0
pub/fab/guide/main.js
Normal file
0
pub/fab/guide/main.js
Normal file
Loading…
Reference in New Issue
Block a user