Skip to content

Commit

Permalink
More examples
Browse files Browse the repository at this point in the history
  • Loading branch information
hypirion committed Jan 30, 2016
1 parent cc873fc commit 46e1f94
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 11 deletions.
7 changes: 4 additions & 3 deletions edn_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
// self-contained examples of go-edn can be found at
// https://github.com/go-edn/edn/tree/v1/examples.
//
// Note that the examples in this package is not checking errors as persively as
// you should do when you use this package. This is done because I'd like the
// examples to be easily readable and understandable.
// Note that the small examples in this package is not checking errors as
// persively as you should do when you use this package. This is done because
// I'd like the examples to be easily readable and understandable. The bigger
// examples provide proper error handling.
package edn

import (
Expand Down
51 changes: 51 additions & 0 deletions example_arbitrary_keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package edn_test

import (
"fmt"

"gopkg.in/edn.v1"
)

type Point3 struct {
X, Y, Z int64
}

type Unit struct {
Type edn.Keyword
HP int
}

// EDN, in contrast to JSON, supports arbitrary values as keys.
func Example_arbitraryKeys() {
input := `{{:x 1 :y 2 :z 3} "Greybeard"
{:y 10 :x 1 :z -10} "Blackwind"}`

var locStarships map[Point3]string
err := edn.UnmarshalString(input, &locStarships)
if err != nil {
panic(err)
}

p := Point3{1, 10, -10}

fmt.Printf("Starship at location %v is %s\n", p, locStarships[p])

input = `{[0 2] {:type :scout :hp 55}
[-3 10] {:type :villager :hp 25}
[5 5] {:type :bowman :hp 32}
[5 6] {:type :bowman :hp 29}}`

var locUnits map[[2]int]Unit
err = edn.UnmarshalString(input, &locUnits)
if err != nil {
panic(err)
}

loc := [2]int{5, 5}

fmt.Printf("Unit at location %v is %+v\n", loc, locUnits[loc])

// Output:
// Starship at location {1 10 -10} is Blackwind
// Unit at location [5 5] is {Type::bowman HP:32}
}
95 changes: 95 additions & 0 deletions example_poly_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package edn_test

import (
"fmt"
"strings"

"gopkg.in/edn.v1"
)

type Notifiable interface {
Notify()
}

type User struct {
Username string
}

func (u User) MarshalEDN() ([]byte, error) {
return edn.Marshal(edn.Tag{"myapp/user", u.Username})
}

func (u *User) Notify() {
fmt.Printf("Notified user %s.\n", u.Username)
}

type Group struct {
GroupId int
}

func (g Group) MarshalEDN() ([]byte, error) {
return edn.Marshal(edn.Tag{"myapp/group", g.GroupId})
}

func (g *Group) Notify() {
fmt.Printf("Notified group with id %d.\n", g.GroupId)
}

var notifyTagMap edn.TagMap

// We use a tagMap to avoid adding these values to the entire system.
func init() {
err := notifyTagMap.AddTagFn("myapp/user", func(s string) (*User, error) {
return &User{s}, nil
})
if err != nil {
panic(err)
}

err = notifyTagMap.AddTagFn("myapp/group", func(id int) (*Group, error) {
return &Group{id}, nil
})
if err != nil {
panic(err)
}
}

// This example shows how to read and write basic EDN tags, and how this can be
// utilised: In contrast to encoding/json, you can read in data where you only
// know that the input satisfies some sort of interface, provided the value is
// tagged.
func Example_polymorphicTags() {
input := `[#myapp/user "eugeness"
#myapp/group 10
#myapp/user "jeannikl"
#myapp/user "jeremiah"
#myapp/group 100]`

rdr := strings.NewReader(input)
dec := edn.NewDecoder(rdr)
dec.UseTagMap(&notifyTagMap)

var toNotify []Notifiable
err := dec.Decode(&toNotify)
if err != nil {
panic(err)
}
for _, notify := range toNotify {
notify.Notify()
}

// Print out the values as well
out, err := edn.Marshal(toNotify)
if err != nil {
panic(err)
}

fmt.Println(string(out))
// Output:
// Notified user eugeness.
// Notified group with id 10.
// Notified user jeannikl.
// Notified user jeremiah.
// Notified group with id 100.
// [#myapp/user"eugeness" #myapp/group 10 #myapp/user"jeannikl" #myapp/user"jeremiah" #myapp/group 100]
}
103 changes: 103 additions & 0 deletions example_set_all_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package edn_test

import (
"fmt"

"gopkg.in/edn.v1"
)

type UserOption edn.Keyword

type UnknownUserOptionError UserOption

func (err UnknownUserOptionError) Error() string {
return fmt.Sprintf("Unknown user option %s", edn.Keyword(err))
}

const (
UnknownOption = UserOption("")
ShowEmail = UserOption("show-email")
Notifications = UserOption("notifications")
DailyEmail = UserOption("daily-email")
RememberMe = UserOption("remember-me")
)

func ListUserOptions() []UserOption {
return []UserOption{
ShowEmail,
Notifications,
DailyEmail,
RememberMe,
}
}

func (uo *UserOption) UnmarshalEDN(bs []byte) error {
var kw edn.Keyword
err := edn.Unmarshal(bs, &kw)
if err != nil {
return err
}
opt := UserOption(kw)
switch opt {
case ShowEmail, Notifications, DailyEmail, RememberMe:
*uo = opt
return nil
default:
return UnknownUserOptionError(opt)
}
}

type UserOptions map[UserOption]bool

func (opts *UserOptions) UnmarshalEDN(bs []byte) error {
var kw edn.Keyword
// try to decode into keyword first
err := edn.Unmarshal(bs, &kw)
if err == nil && kw == edn.Keyword("all") {
// Put all options into the user option map
*opts = UserOptions(make(map[UserOption]bool))
for _, opt := range ListUserOptions() {
(*opts)[opt] = true
}
return nil
}
// then try to decode into user map
var rawOpts map[UserOption]bool
err = edn.Unmarshal(bs, &rawOpts)
*opts = UserOptions(rawOpts)
return err
}

// This example shows how one can implement enums and sets, and how to support
// multiple different forms for a specific value type. The set implemented here
// supports the notation `:all` for all values.
func Example_enumsAndSets() {
inputs := []string{
"#{:show-email :notifications}",
"#{:notifications :show-email :remember-me}",
":all",
"#{:doot-doot}",
":none",
"#{} ;; no options",
}
for _, input := range inputs {
var opts UserOptions
err := edn.UnmarshalString(input, &opts)
if err != nil {
fmt.Println(err)
// Do proper error handling here if something fails
continue
}
// Cannot print out a map, as its ordering is nondeterministic.
fmt.Printf("show email? %t, notifications? %t, daily email? %t, remember me? %t\n",
opts[ShowEmail], opts[Notifications], opts[DailyEmail], opts[RememberMe])
}

// Output:
// show email? true, notifications? true, daily email? false, remember me? false
// show email? true, notifications? true, daily email? false, remember me? true
// show email? true, notifications? true, daily email? true, remember me? true
// Unknown user option :doot-doot
// edn: cannot unmarshal keyword into Go value of type map[edn_test.UserOption]bool
// show email? false, notifications? false, daily email? false, remember me? false
}
58 changes: 58 additions & 0 deletions example_stream_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package edn_test

import (
"fmt"
"io"
"strings"

"gopkg.in/edn.v1"
)

// Typically, you'd also include timestamps here. Imagine that they are here.
type LogEntry struct {
Level edn.Keyword
Message string `edn:"msg"`
Environment string `edn:"env"`
Service string
}

// This example shows how one can do streaming with the decoder, and how to
// properly know when the stream has no elements left.
func Example_streaming() {
const input = `
{:level :debug :msg "1 < 2 ? true" :env "dev" :service "comparer"}
{:level :warn :msg "slow response time from 127.0.1.39" :env "prod" :service "worker 10"}
{:level :warn :msg "worker 8 has been unavailable for 30s" :env "prod" :service "gateway"}
{:level :info :msg "new processing request: what.png" :env "prod" :service "gateway"}
{:level :debug :msg "1 < nil? error" :env "dev" :service "comparer"}
{:level :warn :msg "comparison failed: 1 < nil" :env "dev" :service "comparer"}
{:level :info :msg "received new processing request: what.png" :env "prod" :service "worker 3"}
{:level :warn :msg "bad configuration value :timeout, using 3h" :env "staging" :service "worker 3"}
`

rdr := strings.NewReader(input)
dec := edn.NewDecoder(rdr)
var err error
for {
var entry LogEntry
err = dec.Decode(&entry)
if err != nil {
break
}
if entry.Level == edn.Keyword("warn") && entry.Environment != "dev" {
fmt.Println(entry.Message)
}
}
if err != nil && err != io.EOF {
// Something bad happened to our reader
fmt.Println(err)
return
}
// If err == io.EOF then we've reached end of stream
fmt.Println("End of stream!")
// Output:
// slow response time from 127.0.1.39
// worker 8 has been unavailable for 30s
// bad configuration value :timeout, using 3h
// End of stream!
}
41 changes: 33 additions & 8 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@ func ExampleDecoder_AddTagFn_duration() {

rdr := strings.NewReader(input)
dec := edn.NewDecoder(rdr)
err := dec.AddTagFn("com.myapp/duration", time.ParseDuration)
if err != nil {
panic(err)
}
dec.AddTagFn("com.myapp/duration", time.ParseDuration)

var d time.Duration
err = dec.Decode(&d)
dec.Decode(&d)
fmt.Println(d)

input = `#com.myapp/duration "1moment"`
rdr = strings.NewReader(input)
dec = edn.NewDecoder(rdr)
dec.AddTagFn("com.myapp/duration", time.ParseDuration)
err := dec.Decode(&d)
if err != nil {
panic(err)
fmt.Println(err)
}
fmt.Println(d)
// Output: 2h30m0s

// Output:
// 2h30m0s
// time: unknown unit moment in duration 1moment
}

func ExampleDecoder_AddTagFn_complex() {
Expand Down Expand Up @@ -197,3 +203,22 @@ func ExampleKeyword() {
// :friday
// It is friday!
}

func ExampleRune() {
runeSlice := []edn.Rune{'a', 'b', 'c', ',', ' ', '\n'}

bs, _ := edn.Marshal(runeSlice)

fmt.Println(string(bs))
// Output: [\a \b \c \u002c \space \newline]
}

func ExampleTag_reading() {
input := "#unknown ???"

var tag edn.Tag
edn.UnmarshalString(input, &tag)

fmt.Printf("Tag with name %s and value %q of type %T\n", tag.Tagname, tag.Value, tag.Value)
// Output: Tag with name unknown and value "???" of type edn.Symbol
}

0 comments on commit 46e1f94

Please sign in to comment.