Seven Story Rabbit Hole

Sometimes awesome things happen in deep rabbit holes. Or not.

   images

Deciphering Some Go Code

While trying to use couch-go and looking at the source, I’ve come across some things I don’t understand.

Is function wrapper needed?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
resp, err := client.Get(full_url)
log.Printf("resp: %v err: %v", resp, err)
if err == nil {
    func() {
        defer resp.Body.Close()
        defer conn.Close()

        tc := timeoutClient{resp.Body, conn, timeout}
        largest = handler(&tc)
    }()
} else {
    log.Printf("Error in stream: %v", err)
    time.Sleep(time.Second * 1)
}

Why the separate function wrapper here? Why not just call:

1
2
3
4
defer resp.Body.Close()
defer conn.Close()
tc := timeoutClient{resp.Body, conn, timeout}
largest = handler(&tc)

directly?

How to elegantly dump a request body?

I was trying to debug something, and I wanted to print out the json string before it was being parsed, and here’s what I ended up doing:

1
2
3
4
5
6
7
8
9
10
body, err0 := ioutil.ReadAll(r.Body)
bodyStr := string(body)
log.Printf("body: %v err: %v", bodyStr, err0)
newBody := bytes.NewBuffer(body)
d := json.NewDecoder(newBody)
// d := json.NewDecoder(r.Body) <-- orig code
if err := d.Decode(results); err != nil {
    log.Printf("Decode err: %v", err)
    return err
}

it seems like there should be a slicker way, like maybe rewinding the r.Body Reader?

Answer: yes, there is a much slicker way, io.TeeReader

1
d := json.NewDecoder(io.TeeReader(r.Body, os.Stdout))

Using new() with a map — does this look right?

According to “The Way To Go” (book), you are not supposed to be using new() with maps. However I did this in one case:

1
2
3
4
5
6
7
8
9
10
type Document map[string]interface{}

func (game Game) fetchLatestGameDoc() (doc Document, err error) {
        fetchedDoc := new(Document)
        err = game.db.Retrieve(GAME_DOC_ID, fetchedDoc)
        if err == nil {
     doc = *fetchedDoc
        }
        return
}

is there a better way? When I tried using make(), I got the error err: json: Unmarshal(non-pointer checkerlution.Document). Using make() followed by & didn’t work either.

Type Assertion hell

I’m trying to get some data that’s fairly deep in some json, and ended up doing a lot of awkward feeling type assertions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func (game Game) checkGameDocInChanges(changes Changes) bool {
        foundGameDoc := false
        changeResultsRaw := changes["results"]
        changeResults := changeResultsRaw.([]interface{})
        for _, changeResultRaw := range changeResults {
                changeResult := changeResultRaw.(map[string]interface{})
                docIdRaw := changeResult["id"]
                docId := docIdRaw.(string)
                if strings.Contains(docId, GAME_DOC_ID) {
                        foundGameDoc = true
              }
        }
        return foundGameDoc
}

there must be a better way?

Also I didn’t get why this didn’t work:

1
2
3
type ChangeItem map[string]interface{}
...
changeResult := changeResultRaw.(ChangeItem)

to make it work I had to change it to:

1
changeResult := changeResultRaw.(map[string]interface{})

Getting unexpected float64 back from JSON, had to do awkward type conversions

1
2
3
4
5
func (game Game) isOurTurn(gameDoc Document) bool {
        activeTeamRaw := gameDoc["activeTeam"]
        activeTeam := int(activeTeamRaw.(float64))
        return activeTeam == game.ourTeamId
}

Json snippet:

1
{"_id":"game:checkers","activeTeam":0}

Can’t check for nil

I have these structs:

1
2
3
4
5
6
7
type Game struct {
    gameState            GameState
}

type GameState struct {
   ...
}

and in a loop, I set the gameState field of the Game object to value. I’m trying to detect if it’s the first pass through the loop with this:

1
isFirstGame := (game.gameState == nil)

but I get the error cannot convert nil to type GameState

Comments