Update JS Tour Doc

This commit is contained in:
Aaron Boodman
2016-06-06 00:36:27 -07:00
parent d348361e10
commit c01026bb8c
2 changed files with 107 additions and 197 deletions

View File

@@ -1,123 +0,0 @@
# A Short Tour of Noms for JavaScript
This is a short tour of using Noms from JavaScript. It should only take a few minutes if you have some familiarty with JavaScript.
## Requirements
You'll need Node (v5.3+). Go [install that](https://nodejs.org/en/), if you haven't already.
## Install Noms
On a command-line:
```sh
mkdir noms-tour
cd noms-tour
echo '{"name":"noms-tour","version":"0.0.1"}' > package.json
npm install @attic/noms
```
Then launch Node so that we can have a play:
```sh
node
```
## [Database](TODO-link-to-Database-API)
In Noms, data is represented as trees of immutable *values*. For example, the number `42` is a value. The string `'hello, world'` is a value. The set of all photos from the Hubble space telescope is a value, and each of those photos is also a value.
A Database is a place where you can store Noms values. To do anything with Noms, you will first need to create an instance of a Database:
```js
const noms = require('@attic/noms');
// A database is backed by a "ChunkStore", which is where the physical chunks of data will be kept
// Noms/JS comes with several ChunkStore implementations, including MemoryStore, which is useful
// for testing.
const database = new noms.Database(new noms.MemoryStore());
```
Noms is a [content-addressed](https://en.wikipedia.org/wiki/Content-addressable_storage) database. Every noms value has a hash. When you store a value in noms, you get a *Ref* (short for *reference*) to the data back. The Ref encapsulates the value's hash and some other details.
```js
const ref1 = database.writeValue("Hello, world");
ref1.targetRef; // prints: Ref { _refStr: 'sha1-b237e82a5ed084438714743d30dd4900b1327609' }
// prints: Hello, world
database.readValue(ref1.targetRef).then(console.log);
```
## [Dataset](TODO-link-to-DataSet-API)
A Database on its own can only be used to store and retrieve immutable objects by their hash. This has limited utility.
If you need to keep track of something that changes over time, you need a [Dataset](TODO). A Dataset is a named pointer to a value that can change:
```
let dataSet = new noms.Dataset(database, "salutation");
// prints: null
dataSet.head().then(console.log);
// prints: sha1-b237e82a5ed084438714743d30dd4900b1327609 (same ref as we committed initially)
dataSet = dataSet.commit(ref1);
dataSet
.then(ds => ds.head())
.then(commit => console.log(commit.value));
// prints: Hello, world
dataSet
.then(ds => ds.head())
.then(commit => commit.value.targetValue(database))
.then(console.log);
// prints: Buenos dias
const ref2 = database.writeValue("Buenos dias");
dataSet = dataSet.then(ds => ds.commit(ref2));
dataSet
.then(ds => ds.head())
.then(commit => commit.value.targetValue(database))
.then(console.log);
```
A DataSet is versioned. When you *commit* a new value, you aren't overwriting the old value, but adding to a historical log of values.
```
// prints: Hello, world
dataSet
.then(ds => ds.head())
.then(h => h.parents.first())
.then(rv => rv.targetValue(database))
.then(commit => commit.value.targetValue(database))
.then(console.log);
```
## Values
The Noms format supports a [variety of datatypes](TODO-link-to-overview-of-Noms-and-Noms-datatypes). The following table summarizes the JavaScript datatype(s) used to represent each Noms datatype.
Noms Type | JavaScript Type
--------------- | ---------
Boolean | boolean
Number | number
String | string
Blob | [noms.Blob](#Blob)
Set | [noms.Set](#Set)
List | [noms.List](#List)
Map | [noms.Map](#Map)
Ref | [Ref](#Ref)
Struct | object or [noms.Struct](#Struct)
For example:
```
// Writes a noms value of type:
// Struct<foo: String, num: Number, list: List<String|Number>>
store.writeValue({
foo: "bar",
num: 42,
list: new List("a", "b", 4, 8),
});
```

View File

@@ -4,11 +4,20 @@ This is a short introduction to using Noms from JavaScript. It should only take
## Requirements
You'll need Node (v5.11+). Go [install that](https://nodejs.org/en/), if you haven't already.
* [Noms command-line tools](https://github.com/attic-labs/noms#setup)
* [Node v5.11+](https://nodejs.org/en/)
## Install Noms
## Start a Local Database
On a command-line:
Let's create a local database to play with:
```sh
noms serve ldb:/tmp/noms-js-tour
```
## Install Noms NPM Package
Leave the sever running, and in a separate terminal:
```sh
mkdir noms-tour
@@ -30,10 +39,10 @@ To get started with Noms, first create a Database:
```js
const noms = require('@attic/noms');
const database = new noms.Database(new noms.MemoryStore());
const db = noms.DatabaseSpec.parse('http://localhost:8000').database();
```
A database is backed by a "ChunkStore", which is where physical chunks of data are kept. Noms/JS comes with several ChunkStore implementations, including MemoryStore, which is useful for testing.
See [Spelling in Noms](spelling.md) for more information on database spec strings.
@@ -42,109 +51,133 @@ A database is backed by a "ChunkStore", which is where physical chunks of data a
Datasets are the main interface you'll use to work with Noms. A dataset is just a named value in the database that you can update:
```js
const dataset = new noms.Dataset(database, "counter");
let ds = new noms.Dataset(db, "people");
// prints: null
dataset.head().then(console.log);
ds.head().then(console.log);
dataset.commit(1);
let data = new noms.List([
noms.newStruct('', {
given: 'Rickon',
male: true,
}),
noms.newStruct('', {
given: 'Bran',
male: true,
}),
noms.newStruct('', {
given: 'Arya',
male: false,
}),
noms.newStruct('', {
given: 'Sansa',
male: false,
}),
]);
// prints:
// struct Commit<{
// parents: Set
// value: Value
// }>(
// parents: [],
// value: 1,
// )
dataset.head().then(console.log);
// List<struct {
// given: String
// male: Bool
// }>
console.log(data.type.describe());
dataset.commit(2);
dataset.commit("three");
dataset.commit({
count: 4,
});
ds.commit(data).
then(r => ds = r);
// prints: Rickon
ds.head().
then(commit => commit.value.get(0)).
then(v => console.log(v.given));
```
You can also see this on the command-line. In a new (third) terminal:
```sh
> noms ds http://localhost:8000
people
> noms show http://localhost:8000:people
struct Commit {
parents: Set<Ref<Cycle<0>>>
value: Value
}({
parents: {},
value: [
{
given: "Rickon",
male: true,
},
...
```
Let's add some more data. Back in Node:
```
data.append(noms.newStruct('', {
given: 'Jon',
family: 'Snow',
male: true,
})).then(d => data = d);
// prints:
// struct<{
// count: Number
// }>(
// value: 4,
// )
dataset.head().then(h => console.log(h.value));
// List<struct {
// family: String
// given: String
// male: Bool
// } | struct {
// given: String
// male: Bool
// }>
console.log(data.type.describe());
ds.commit(data).
then(r => ds = r);
```
Datasets are versioned. When you *commit* a new value, you aren't overwriting the old value, but adding to a historical log of values.
```js
function printHead(head) {
console.log(head.value);
head.parents().first()
.then(headRef => headRef.targetValue())
.then(printHead);
function printCommit(commit) {
console.log('list', commit.value.hash.toString(),
'length:', commit.value.length);
commit.parents.first().
then(r => r.targetValue(db)).
then(printCommit);
}
// Prints:
// {count:4}
// "three"
// 2
// 1
dataset.head().then(printHead);
```
Datasets can be very large. Noms will automatically break large lists, maps, sets, and blobs into chunks (using [Prolly Trees](TODO)), so that they can be transferred, searched, and updated efficiently.
If you have structs with really large fields though, it's sometimes useful to break them up manually:
```js
// Write a value to the database manually, outside of a commit
// Note that this doesn't get flushed until the next commit()
const log = 'logloglog'.repeat(100000);
const logRef = database.writeValue({log});
console.log(r);
database.readValue(r).then(v => console.log(v == log));
dataset.commit({
count: 5,
logOutput: r,
});
dataset.head().then(h => {
// prints: {ref: ...}
console.log(h.value);
// Prints the first bit of the log
h.value.targetValue().then(v => console.log.substr(0, 100));
}
// list sha1-eba46d10a2d1d10eb9f115c7b8df8c45653b430e length: 5
// list sha1-9cf762c697b10a2868957b6c4ea30de36608ac08 length: 4
ds.head().then(printCommit);
```
## Values
Noms supports a [variety of datatypes](TODO-link-to-overview-of-Noms-and-Noms-datatypes). The following table summarizes the JavaScript datatype(s) used to represent each Noms datatype.
Noms supports a [variety of datatypes](intro.md#types). The following table summarizes the JavaScript datatype(s) used to represent each Noms datatype.
Noms Type | JavaScript Type
--------------- | ---------
Boolean | boolean
Number | number
String | string
Blob | [noms.Blob](#Blob)
Set | [noms.Set](#Set)
List | [noms.List](#List)
Map | [noms.Map](#Map)
Ref | [noms.Ref](#Ref)
Struct | Object
Blob | noms.Blob
Set | noms.Set
List | noms.List
Map | noms.Map
Ref | noms.Ref
Struct | noms.Struct
In most cases, the Noms JavaScript library will automatically convert between JavaScript types and Noms types. For example:
```js
// Writes a noms value of type:
// Struct<foo: String, num: Number, list: List<String|Number>>
store.writeValue({
store.writeValue(newStruct('', {
foo: "bar",
num: 42,
list: new List("a", "b", 4, 8),
});
}));
```
Sometimes it's nice to expliclty control the type. You can do that with [NomsValue](TODO).
Sometimes it's nice to explicitly control the type. You can do that by calling `new Struct` directy and passing a `Type` for the first parameter.