Permalink
Fetching contributors…
Cannot retrieve contributors at this time
316 lines (243 sloc) 7.12 KB

Home »

Technical Overview  |  Use Cases  |  Command-Line Interface  |  Go bindings Tour |  Path Syntax  |  FAQ 

A Short Tour of Noms for Go

This is a short introduction to using Noms from Go. It should only take a few minutes if you have some familiarity with Go.

During the tour, you can refer to the complete Go SDK Reference for more information on anything you see.

Requirements

Start a Local Database

Let's create a local database to play with:

> mkdir /tmp/noms-go-tour
> noms serve /tmp/noms-go-tour

Database

Leave the server running, and in a separate terminal:

> mkdir noms-tour
> cd noms-tour

Then use your favorite editor so that we can start to play with code. To get started with Noms, first create a Database:

package main

import (
  "fmt"
  "os"

  "github.com/attic-labs/noms/go/spec"
)

func main() {
  sp, err := spec.ForDatabase("http://localhost:8000")
  if err != nil {
    fmt.Fprintf(os.Stderr, "Could not access database: %s\n", err)
    return
  }
  defer sp.Close()
}

Now let's run it:

> go run noms-tour.go

If you did not leave the server running you would see output of Could not access database here, otherwise your program should exit cleanly.

See Spelling in Noms for more information on database spec strings.

Dataset

Datasets are the main interface you'll use to work with Noms. Let's update our example to use a Dataset spec string:

package main

import (
  "fmt"
  "os"

  "github.com/attic-labs/noms/go/spec"
)

func main() {
  sp, err := spec.ForDataset("http://localhost:8000::people")
  if err != nil {
    fmt.Fprintf(os.Stderr, "Could not create dataset: %s\n", err)
    return
  }
  defer sp.Close()

  if _, ok := sp.GetDataset().MaybeHeadValue(); !ok {
    fmt.Fprintf(os.Stdout, "head is empty\n")
  }
}

Now let's run it:

> go run noms-tour.go
head is empty

Since the dataset does not yet have any values you see head is empty. Let's add some data to make it more interesting:

package main

import (
  "fmt"
  "os"

  "github.com/attic-labs/noms/go/spec"
  "github.com/attic-labs/noms/go/types"
)

func newPerson(givenName string, male bool) types.Struct {
  return types.NewStruct("Person", types.StructData{
    "given": types.String(givenName),
    "male":  types.Bool(male),
  })
}

func main() {
  sp, err := spec.ForDataset("http://localhost:8000::people")
  if err != nil {
    fmt.Fprintf(os.Stderr, "Could not create dataset: %s\n", err)
    return
  }
  defer sp.Close()

  db := sp.GetDatabase()

  data := types.NewList(db,
    newPerson("Rickon", true),
    newPerson("Bran", true),
    newPerson("Arya", false),
    newPerson("Sansa", false),
  )

  fmt.Fprintf(os.Stdout, "data type: %v\n", types.TypeOf(data).Describe())

  _, err = db.CommitValue(sp.GetDataset(), data)
  if err != nil {
    fmt.Fprint(os.Stderr, "Error commiting: %s\n", err)
  }
}

Now you will get output of the data type of our Dataset value:

> go run noms-tour.go
data type: List<struct  {
  given: String
  male: Bool
}>

Now you can access the data via your program:

package main

import (
  "fmt"
  "os"

  "github.com/attic-labs/noms/go/spec"
  "github.com/attic-labs/noms/go/types"
)

func main() {
  sp, err := spec.ForDataset("http://localhost:8000::people")
  if err != nil {
    fmt.Fprintf(os.Stderr, "Could not create dataset: %s\n", err)
    return
  }
  defer sp.Close()

  if headValue, ok := sp.GetDataset().MaybeHeadValue(); !ok {
    fmt.Fprintf(os.Stdout, "head is empty\n")
  } else {
    // type assertion to convert Head to List
    personList := headValue.(types.List)
    // type assertion to convert List Value to Struct
    personStruct := personList.Get(0).(types.Struct)
    // prints: Rickon
    fmt.Fprintf(os.Stdout, "given: %v\n", personStruct.Get("given"))
  }
}

Running it now:

> go run noms-tour.go
given: Rickon

You can see this data using the command-line too:

> noms ds http://localhost:8000
people

> noms show http://localhost:8000::people
struct Commit {
  meta: struct {},
  parents: set {},
  value: [  // 4 items
    struct Person {
      given: "Rickon",
      male: true,
    },
    struct Person {
      given: "Bran",
      male: true,
    },
    struct Person {
      given: "Arya",
      male: false,
    },
    struct Person {
      given: "Sansa",
      male: false,
    },
  ],
}

Let's add some more data.

package main

import (
  "fmt"
  "os"

  "github.com/attic-labs/noms/go/spec"
  "github.com/attic-labs/noms/go/types"
)

func main() {
  sp, err := spec.ForDataset("http://localhost:8000::people")
  if err != nil {
    fmt.Fprintf(os.Stderr, "Could not create dataset: %s\n", err)
    return
  }
  defer sp.Close()

  if headValue, ok := sp.GetDataset().MaybeHeadValue(); !ok {
    fmt.Fprintf(os.Stdout, "head is empty\n")
  } else {
    // type assertion to convert Head to List
    personList := headValue.(types.List)
    personEditor := personList.Edit()
    data := personEditor.Append(
      types.NewStruct("Person", types.StructData{
        "given":  types.String("Jon"),
        "family": types.String("Snow"),
        "male":   types.Bool(true),
      }),
    ).List()

    fmt.Fprintf(os.Stdout, "data type: %v\n", types.TypeOf(data).Describe())

    _, err = sp.GetDatabase().CommitValue(sp.GetDataset(), data)
    if err != nil {
      fmt.Fprint(os.Stderr, "Error commiting: %s\n", err)
    }
  }
}

Running this:

> go run noms-tour.go
data type: List<Struct Person {
  family?: String,
  given: String,
  male: Bool,
}>

Datasets are versioned. When you commit a new value, you aren't overwriting the old value, but adding to a historical log of values:

> noms log http://localhost:8000::people
commit ba3lvopbgcqqnofm3qk7sk4j2doroj1l
Parent: f0b1befu9jp82r1vcd4gmuhdno27uobi
(root) {
+   struct Person {
+     family: "Snow",
+     given: "Jon",
+     male: true,
+   }
  }

commit f0b1befu9jp82r1vcd4gmuhdno27uobi
Parent: hshltip9kss28uu910qadq04mhk9kuko

commit hshltip9kss28uu910qadq04mhk9kuko
Parent: None

Values

Noms supports a variety of datatypes beyond List, Struct, String, and Bool we used above.

Samples

You can continue learning more about the Noms Go SDK by looking at the documentation and by reviewing the samples. The hr sample is a more complete implementation of our example above and will help you to see further usage of the other datatypes.